From 86d9f8cf1cf1b7429cda9d3d5d39878518f5682e Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Fri, 18 Oct 2024 11:11:58 -0700 Subject: [PATCH 1/3] Bump coredns to 1.11.3 and K8s to 1.28.14 Signed-off-by: Derek Nola --- go.mod | 204 ++++---- go.sum | 1410 ++++++++------------------------------------------------ 2 files changed, 302 insertions(+), 1312 deletions(-) diff --git a/go.mod b/go.mod index 30e637afb..335a6787c 100644 --- a/go.mod +++ b/go.mod @@ -4,142 +4,150 @@ go 1.22 require ( github.com/coredns/caddy v1.1.1 - github.com/coredns/coredns v1.10.0 + github.com/coredns/coredns v1.11.3 github.com/coreos/etcd v3.3.13+incompatible github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/miekg/dns v1.1.50 + github.com/miekg/dns v1.1.58 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.23.0 - github.com/prometheus/client_golang v1.14.0 + github.com/onsi/gomega v1.27.10 + github.com/prometheus/client_golang v1.19.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.4 github.com/vishvananda/netlink v1.1.0 - go.etcd.io/etcd/api/v3 v3.5.5 - go.etcd.io/etcd/client/v2 v2.305.5 - go.etcd.io/etcd/client/v3 v3.5.5 - golang.org/x/net v0.17.0 - k8s.io/api v0.25.0 - k8s.io/apimachinery v0.25.0 - k8s.io/client-go v0.24.7 + go.etcd.io/etcd/api/v3 v3.5.12 + go.etcd.io/etcd/client/v2 v2.305.9 + go.etcd.io/etcd/client/v3 v3.5.12 + golang.org/x/net v0.23.0 + k8s.io/api v0.29.3 + k8s.io/apimachinery v0.29.3 + k8s.io/client-go v0.29.3 k8s.io/component-base v0.24.7 - k8s.io/klog/v2 v2.80.1 - k8s.io/kubernetes v1.26.10 - k8s.io/utils v0.0.0-20221107191617-1a15be271d1d + k8s.io/klog/v2 v2.120.1 + k8s.io/kubernetes v1.28.14 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b ) require ( - github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect - github.com/DataDog/datadog-go v4.8.2+incompatible // indirect - github.com/DataDog/datadog-go/v5 v5.0.2 // indirect - github.com/DataDog/sketches-go v1.2.1 // indirect - github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/DataDog/appsec-internal-go v1.5.0 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect + github.com/DataDog/datadog-go/v5 v5.3.0 // indirect + github.com/DataDog/go-libddwaf/v2 v2.3.2 // indirect + github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect + github.com/DataDog/sketches-go v1.4.2 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.3.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dnstap/golang-dnstap v0.4.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ebitengine/purego v0.6.0-alpha.5 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/farsightsec/golang-framestream v0.3.0 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.1.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 // indirect - github.com/openzipkin/zipkin-go v0.4.0 // indirect - github.com/philhofer/fwd v1.1.1 // indirect + github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 // indirect + github.com/openzipkin/zipkin-go v0.4.2 // indirect + github.com/outcaste-io/ristretto v0.2.3 // indirect + github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/spf13/cobra v1.6.0 // indirect - github.com/tinylib/msgp v1.1.2 // indirect - github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.6.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.53.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/quic-go/quic-go v0.42.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/tinylib/msgp v1.1.8 // indirect + github.com/vishvananda/netns v0.0.4 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/mock v0.4.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.19.0 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.org/x/tools v0.6.0 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/DataDog/dd-trace-go.v1 v1.41.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.17.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/DataDog/dd-trace-go.v1 v1.62.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( - // pinned latest version for vulnerability fixes - // this one is used by coredns - // if coredns starts using >= v0.14.0 this pinned version can be removed - github.com/apache/thrift => github.com/apache/thrift v0.14.0 - - k8s.io/api => k8s.io/api v0.24.7 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.7 - k8s.io/apimachinery => k8s.io/apimachinery v0.24.7 - k8s.io/apiserver => k8s.io/apiserver v0.24.7 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.7 - k8s.io/client-go => k8s.io/client-go v0.24.7 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.7 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.7 - k8s.io/code-generator => k8s.io/code-generator v0.24.7 - k8s.io/component-base => k8s.io/component-base v0.24.7 - k8s.io/component-helpers => k8s.io/component-helpers v0.24.7 - k8s.io/controller-manager => k8s.io/controller-manager v0.24.7 - k8s.io/cri-api => k8s.io/cri-api v0.24.7 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.7 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.7 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.7 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.7 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.7 - k8s.io/kubectl => k8s.io/kubectl v0.24.7 - k8s.io/kubelet => k8s.io/kubelet v0.24.7 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.7 - k8s.io/metrics => k8s.io/metrics v0.24.7 - k8s.io/mount-utils => k8s.io/mount-utils v0.24.7 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.7 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.7 + k8s.io/api => k8s.io/api v0.28.14 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.28.14 + k8s.io/apimachinery => k8s.io/apimachinery v0.28.14 + k8s.io/apiserver => k8s.io/apiserver v0.28.14 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.28.14 + k8s.io/client-go => k8s.io/client-go v0.28.14 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.28.14 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.28.14 + k8s.io/code-generator => k8s.io/code-generator v0.28.14 + k8s.io/component-base => k8s.io/component-base v0.28.14 + k8s.io/component-helpers => k8s.io/component-helpers v0.28.14 + k8s.io/controller-manager => k8s.io/controller-manager v0.28.14 + k8s.io/cri-api => k8s.io/cri-api v0.28.14 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.28.14 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.28.14 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.28.14 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.28.14 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.28.14 + k8s.io/kubectl => k8s.io/kubectl v0.28.14 + k8s.io/kubelet => k8s.io/kubelet v0.28.14 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.28.14 + k8s.io/metrics => k8s.io/metrics v0.28.14 + k8s.io/mount-utils => k8s.io/mount-utils v0.28.14 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.28.14 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.28.14 ) diff --git a/go.sum b/go.sum index 4cd670643..17ed95c36 100644 --- a/go.sum +++ b/go.sum @@ -1,761 +1,219 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.4.0/go.mod h1:LFrqilwgdw4X2cJS9ALgzYmMu+ULyrUN6IHV3CPK4TM= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/99designs/gqlgen v0.14.0/go.mod h1:S7z4boV+Nx4VvzMUpVrY/YuHjFX4n7rDyuTqvAkuoRE= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 h1:3nVO1nQyh64IUY6BPZUpMYMZ738Pu+LsMt3E0eqqIYw= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583/go.mod h1:EP9f4GqaDJyP1F5jTNMtzdIpw3JpNs3rMSJOnYywCiw= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go v4.8.2+incompatible h1:qbcKSx29aBLD+5QLvlQZlGmRMF/FfGqFLFev/1TDzRo= -github.com/DataDog/datadog-go v4.8.2+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go/v5 v5.0.2 h1:UFtEe7662/Qojxkw1d6SboAeA0CPI3naKhVASwFn+04= -github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV+PUAOUsHvKpcU= -github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= -github.com/DataDog/sketches-go v1.2.1 h1:qTBzWLnZ3kM2kw39ymh6rMcnN+5VULwFs++lEYUUsro= -github.com/DataDog/sketches-go v1.2.1/go.mod h1:1xYmPLY1So10AwxV6MJV0J53XVH+WL9Ad1KetxVivVI= -github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/DataDog/appsec-internal-go v1.5.0 h1:8kS5zSx5T49uZ8dZTdT19QVAvC/B8ByyZdhQKYQWHno= +github.com/DataDog/appsec-internal-go v1.5.0/go.mod h1:pEp8gjfNLtEOmz+iZqC8bXhu0h4k7NUsW/qiQb34k1U= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 h1:bUMSNsw1iofWiju9yc1f+kBd33E3hMJtq9GuU602Iy8= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0/go.mod h1:HzySONXnAgSmIQfL6gOv9hWprKJkx8CicuXuUbmgWfo= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 h1:5nE6N3JSs2IG3xzMthNFhXfOaXlrsdgqmJ73lndFf8c= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ= +github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8= +github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q= +github.com/DataDog/go-libddwaf/v2 v2.3.2 h1:pdi9xjWW57IpOpTeOyPuNveEDFLmmInsHDeuZk3TY34= +github.com/DataDog/go-libddwaf/v2 v2.3.2/go.mod h1:gsCdoijYQfj8ce/T2bEDNPZFIYnmHluAgVDpuQOWMZE= +github.com/DataDog/go-tuf v1.0.2-0.5.2 h1:EeZr937eKAWPxJ26IykAdWA4A0jQXJgkhUjqEI/w7+I= +github.com/DataDog/go-tuf v1.0.2-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= +github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= +github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= +github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o= +github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.22.0/go.mod h1:lm3THZ8reqBDBQKQyb5HB3sY1lKp3grEbQ81aWSgPp4= -github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.14.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go-v2 v1.0.0/go.mod h1:smfAbmpW+tcRVuNUjo3MOArSZmW72t62rkCzc2i0TWM= -github.com/aws/aws-sdk-go-v2/config v1.0.0/go.mod h1:WysE/OpUgE37tjtmtJd8GXgT8s1euilE5XtUkRNUQ1w= -github.com/aws/aws-sdk-go-v2/credentials v1.0.0/go.mod h1:/SvsiqBf509hG4Bddigr3NB12MIpfHhZapyBurJe8aY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.0/go.mod h1:wpMHDCXvOXZxGCRSidyepa8uJHY4vaBGfY2/+oKU/Bc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.0/go.mod h1:3jExOmpbjgPnz2FJaMOfbSk1heTkZ66aD3yNtVhnjvI= -github.com/aws/aws-sdk-go-v2/service/sqs v1.0.0/go.mod h1:w5BclCU8ptTbagzXS/fHBr+vAyXUjggg/72qDIURKMk= -github.com/aws/aws-sdk-go-v2/service/sts v1.0.0/go.mod h1:5f+cELGATgill5Pu3/vK3Ebuigstc+qYEHW5MvGWZO4= -github.com/aws/smithy-go v1.0.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/confluentinc/confluent-kafka-go v1.4.0/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/coredns v1.10.0 h1:jCfuWsBjTs0dapkkhISfPCzn5LqvSRtrFtaf/Tjj4DI= -github.com/coredns/coredns v1.10.0/go.mod h1:CIfRU5TgpuoIiJBJ4XrofQzfFQpPFh32ERpUevrSlaw= +github.com/coredns/coredns v1.11.3 h1:8RjnpZc42db5th84/QJKH2i137ecJdzZK1HJwhetSPk= +github.com/coredns/coredns v1.11.3/go.mod h1:lqFkDsHjEUdY7LJ75Nib3lwqJGip6ewWOqNIf8OavIQ= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234= github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= -github.com/elastic/go-elasticsearch/v7 v7.17.1/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY= +github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA= github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-chi/chi v1.5.0/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-pg/pg/v10 v10.0.0/go.mod h1:XHU1AkQW534GFuUdSiQ46+Xw6Ah+9+b8DlT4YwhiXL8= -github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis/v7 v7.1.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-redis/redis/v8 v8.0.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gocql/gocql v0.0.0-20220224095938-0eacd3183625/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/fiber/v2 v2.11.0/go.mod h1:oZTLWqYnqpMMuF922SjGbsYZsdpE1MCfh416HNdweIM= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.7.0/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210423192551-a2663126120b/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.0.0/go.mod h1:mbFwfRxOTDHZpT3iUsMAFcLNoVm6Xbe1xZ6KiSm8FY0= -github.com/hashicorp/consul/internal v0.1.0/go.mod h1:zi9bMZYbiPHyAjgBWo7kCUcy5l2NrTdrkVupCc7Oo6c= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.6/go.mod h1:5VDNHjqFMgEcclnwmkCnC99IPwxBmIsxwY8qn+Nl0H4= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.8.6/go.mod h1:P/AVgr4UHsUYqVHG1y9eFhz8S35pqhGhLZaDpfGKIMo= -github.com/hashicorp/vault/api v1.1.0/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= -github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.4.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.8.1/go.mod h1:4HOLxrl8wToZJReD04/yB20GDwf4KBYETvlHciCnwW0= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= -github.com/labstack/echo/v4 v4.2.0/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= -github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= -github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10Siffyepr6SvlKbUsjH5JpNCRi8= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY= +github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= +github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= +github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= +github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA= +github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.4.29/go.mod h1:m1lXeqJtIFYZayv0shM/tjrAFljvWLTprxBHd+3PnaU= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= +github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -763,680 +221,204 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tidwall/btree v0.3.0/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= -github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= -github.com/tidwall/buntdb v1.2.0/go.mod h1:XLza/dhlwzO6dc5o/KWor4kfZSt3BP8QV+77ZMKfI58= -github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/grect v0.1.0/go.mod h1:sa5O42oP6jWfTShL9ka6Sgmg3TgIK649veZe05B7+J8= -github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= -github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= -github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= -github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/twitchtv/twirp v8.1.1+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= -github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= -github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= -github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1/go.mod h1:xlngVLeyQ/Qi05oQxhQ+oTuqa03RjMwMfk/7/TCs+QI= -github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= -go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI= -go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= -go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= -go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= +go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= +go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= +go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= +go.etcd.io/etcd/client/v2 v2.305.9 h1:YZ2OLi0OvR0H75AcgSUajjd5uqKDKocQUqROTG11jIo= +go.etcd.io/etcd/client/v2 v2.305.9/go.mod h1:0NBdNx9wbxtEQLwAQtrDHwx58m02vXpDcgSYI2seohQ= +go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg= +go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200901203048-c4f52b2c50aa/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200527183253-8e7acdbce89d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200528110217-3d3490e7e671/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200726014623-da3ae01ef02d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/DataDog/dd-trace-go.v1 v1.41.0 h1:tD0e/cQGXSoUBbkOod2LWYCwG3gqShrWItViLcRt9Yw= -gopkg.in/DataDog/dd-trace-go.v1 v1.41.0/go.mod h1:CfhMxr9rU1IDdSNRjeLKhbNcZM6b8kRxOAKSvrG/GiI= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/DataDog/dd-trace-go.v1 v1.62.0 h1:jeZxE4ZlfAc+R0zO5TEmJBwOLet3NThsOfYJeSQg1x0= +gopkg.in/DataDog/dd-trace-go.v1 v1.62.0/go.mod h1:YTvYkk3PTsfw0OWrRFxV/IQ5Gy4nZ5TRvxTAP3JcIzs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/jinzhu/gorm.v1 v1.9.1/go.mod h1:56JJPUzbikvTVnoyP1nppSkbJ2L8sunqTBDY2fDrmFg= -gopkg.in/olivere/elastic.v3 v3.0.75/go.mod h1:yDEuSnrM51Pc8dM5ov7U8aI/ToR3PG0llA8aRv2qmw0= -gopkg.in/olivere/elastic.v5 v5.0.84/go.mod h1:LXF6q9XNBxpMqrcgax95C6xyARXWbbCXUrtTxrNrxJI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw= -gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to= -gorm.io/driver/sqlserver v1.0.4/go.mod h1:ciEo5btfITTBCj9BkoUVDvgQbUdLWQNqdFY5OGuGnRg= -gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= -inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= -k8s.io/api v0.24.7 h1:UU9XB38BLUEzGoC45387FiblblIbRQkEUAG6nZoddqE= -k8s.io/api v0.24.7/go.mod h1:tt+TFsvj8um6tsywVsTdwGdPvQ4IDCMLZXFH5C2BRlU= -k8s.io/apimachinery v0.24.7 h1:CYJ+iRJkNFWWdUz5XodHkA6Yk5nRTbudvvQDVGxtuqc= -k8s.io/apimachinery v0.24.7/go.mod h1:WR5z9Lpw2mOAeDg20iSSrEBRQMY0p2YXVdYpUIgSr4o= -k8s.io/client-go v0.24.7 h1:K4rTkxc94ctXfaDXfa5IflsflPYmguG/3zb3iJY4dQA= -k8s.io/client-go v0.24.7/go.mod h1:UoQaKAGHRRE77THP0jGUehwqt5javBlv1AoKdpfz3JA= -k8s.io/component-base v0.24.7 h1:zQ3SiqZucS1thDB/5UnRV3DQ71eXvmHaLY9GPB9YJYk= -k8s.io/component-base v0.24.7/go.mod h1:ce5M3pIoOY3DoFbcQ+Y8BMEsRFd3KcrAEjrZIZf0xqw= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/kubernetes v1.26.10 h1:0px6+62d5Z3pcRPYl3Fc00t3W7BtBjqkjcRarp597Lk= -k8s.io/kubernetes v1.26.10/go.mod h1:FJGPRZLL8WHUDq5XAPs4Ut4jCB0f08R7MKTRP8CGpvI= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ= +honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc= +k8s.io/api v0.28.14 h1:7DXeMrQq+BJI6H7WtSMC8l1gM4QZWtWN65UbN+qZ9Uc= +k8s.io/api v0.28.14/go.mod h1:ROk/G6/7IZf14AL1WkpZdq//5khE1EtLNxkcEpSXNFM= +k8s.io/apimachinery v0.28.14 h1:n2l8jNNOmUUDXpa8ljHCEUSeIChby1BKyqoL0AtpmGw= +k8s.io/apimachinery v0.28.14/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= +k8s.io/client-go v0.28.14 h1:wfPRgz07MvLMxcHfN8kAc4Qcwduc4My25A3CBU7OqBQ= +k8s.io/client-go v0.28.14/go.mod h1:HGfdb7BqkX4hRpNyVLHNQKWDU03W6a38LfIHD7QGJpI= +k8s.io/component-base v0.28.14 h1:sJowHyRY166hBfBQ4cOKjkSvUo4bUdeuePtEOQfSNRY= +k8s.io/component-base v0.28.14/go.mod h1:DgYlfHNvP1yeBb4L+UIzMsWNtOl0yqTk+4dGGc79H0w= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubernetes v1.28.14 h1:Q0qoY+dKjqAhSrVSAebEMVUK5zfrg9Sz3I/4aHnOCaQ= +k8s.io/kubernetes v1.28.14/go.mod h1:chlmcCDBnOA/y+572cw8dO0Rci1wiA8bm5+zhPdFLCk= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= From 097b2f87fda1dcb9e70faf2e4e3189cf34d240e3 Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Fri, 18 Oct 2024 11:39:20 -0700 Subject: [PATCH 2/3] Migrate off deprecated clock package Signed-off-by: Derek Nola --- pkg/dns/config/sync_dir.go | 4 ++-- pkg/dns/config/sync_dir_test.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/dns/config/sync_dir.go b/pkg/dns/config/sync_dir.go index 51168c4f0..f35404f97 100644 --- a/pkg/dns/config/sync_dir.go +++ b/pkg/dns/config/sync_dir.go @@ -25,8 +25,8 @@ import ( "time" "unicode/utf8" - "k8s.io/apimachinery/pkg/util/clock" "k8s.io/klog/v2" + "k8s.io/utils/clock" ) // NewFileSync returns a Sync that scans the given dir periodically for config data @@ -60,7 +60,7 @@ func (syncSource *kubeFileSyncSource) Once() (syncResult, error) { func (syncSource *kubeFileSyncSource) Periodic() <-chan syncResult { // TODO: drive via inotify? go func() { - ticker := syncSource.clock.NewTicker(syncSource.period).C() + ticker := syncSource.clock.Tick(syncSource.period) for { if result, err := syncSource.load(); err != nil { klog.Errorf("Error loading config from %s: %v", syncSource.dir, err) diff --git a/pkg/dns/config/sync_dir_test.go b/pkg/dns/config/sync_dir_test.go index b994bfb92..c768fb82e 100644 --- a/pkg/dns/config/sync_dir_test.go +++ b/pkg/dns/config/sync_dir_test.go @@ -25,14 +25,13 @@ import ( "testing" "time" - "k8s.io/apimachinery/pkg/util/clock" + clock "k8s.io/utils/clock/testing" ) func TestSyncFile(t *testing.T) { testParentDir := t.TempDir() testDir := filepath.Join(testParentDir, "datadir") - fakeClock := clock.NewFakeClock(time.Now()) source := newFileSyncSource(testDir, time.Second, fakeClock) From 1f3349e416e39b23060bb204e59c061cfd02a68f Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Fri, 18 Oct 2024 11:12:24 -0700 Subject: [PATCH 3/3] Go mod vendor Signed-off-by: Derek Nola --- .../DataDog/appsec-internal-go/LICENSE | 200 + .../appsec-internal-go/appsec/config.go | 196 + .../appsec-internal-go/appsec/embed.go | 20 + .../appsec-internal-go/appsec/processors.json | 208 + .../appsec-internal-go/appsec/rules.go | 33 + .../appsec-internal-go/appsec/rules.json | 9360 +++++++++++++ .../appsec-internal-go/limiter}/limiter.go | 66 +- .../DataDog/appsec-internal-go/log/backend.go | 138 + .../DataDog/appsec-internal-go/log/log.go | 45 + .../datadog-agent/pkg/obfuscate/cache.go | 7 +- .../pkg/obfuscate/credit_cards.go | 5 +- .../datadog-agent/pkg/obfuscate/http.go | 16 +- .../datadog-agent/pkg/obfuscate/json.go | 162 +- .../pkg/obfuscate/json_scanner.go | 15 +- .../datadog-agent/pkg/obfuscate/obfuscate.go | 70 +- .../pkg/obfuscate/obfuscate_easyjson.go | 100 - .../datadog-agent/pkg/obfuscate/redis.go | 39 + .../datadog-agent/pkg/obfuscate/sql.go | 212 +- .../pkg/obfuscate/sql_tokenizer.go | 183 +- .../pkg/remoteconfig/state/LICENSE | 200 + .../pkg/remoteconfig/state/README.md | 5 + .../pkg/remoteconfig/state/agent_config.go | 159 + .../pkg/remoteconfig/state/configs.go | 123 + .../remoteconfig/state/configs_agent_task.go | 59 + .../pkg/remoteconfig/state/configs_asm.go | 159 + .../pkg/remoteconfig/state/path.go | 100 + .../pkg/remoteconfig/state/products.go | 45 + .../pkg/remoteconfig/state/repository.go | 442 + .../pkg/remoteconfig/state/tuf.go | 233 + .../DataDog/datadog-go/statsd/README.md | 4 - .../DataDog/datadog-go/statsd/aggregator.go | 283 - .../DataDog/datadog-go/statsd/buffer.go | 190 - .../DataDog/datadog-go/statsd/buffer_pool.go | 40 - .../statsd/buffered_metric_context.go | 82 - .../DataDog/datadog-go/statsd/event.go | 91 - .../DataDog/datadog-go/statsd/fnv1a.go | 39 - .../DataDog/datadog-go/statsd/format.go | 257 - .../DataDog/datadog-go/statsd/metrics.go | 181 - .../DataDog/datadog-go/statsd/noop.go | 91 - .../DataDog/datadog-go/statsd/options.go | 323 - .../DataDog/datadog-go/statsd/pipe.go | 9 - .../DataDog/datadog-go/statsd/pipe_windows.go | 84 - .../DataDog/datadog-go/statsd/sender.go | 130 - .../datadog-go/statsd/service_check.go | 70 - .../DataDog/datadog-go/statsd/statsd.go | 687 - .../DataDog/datadog-go/statsd/telemetry.go | 151 - .../DataDog/datadog-go/statsd/udp.go | 40 - .../DataDog/datadog-go/statsd/uds.go | 100 - .../DataDog/datadog-go/statsd/uds_windows.go | 10 - .../DataDog/datadog-go/statsd/utils.go | 23 - .../DataDog/datadog-go/statsd/worker.go | 150 - .../datadog-go/v5/statsd/aggregator.go | 27 +- .../DataDog/datadog-go/v5/statsd/buffer.go | 17 +- .../datadog-go/v5/statsd}/container.go | 39 +- .../DataDog/datadog-go/v5/statsd/event.go | 2 +- .../DataDog/datadog-go/v5/statsd/format.go | 37 +- .../DataDog/datadog-go/v5/statsd/metrics.go | 6 +- .../DataDog/datadog-go/v5/statsd/noop.go | 20 + .../DataDog/datadog-go/v5/statsd/options.go | 55 +- .../DataDog/datadog-go/v5/statsd/statsd.go | 188 +- .../DataDog/datadog-go/v5/statsd/telemetry.go | 2 +- .../DataDog/datadog-go/v5/statsd/utils.go | 9 + .../DataDog/datadog-go/v5/statsd/worker.go | 4 +- .../DataDog/go-libddwaf/v2/.gitattributes | 3 + .../DataDog/go-libddwaf/v2/.gitignore | 18 + .../github.com/DataDog/go-libddwaf/v2/LICENSE | 200 + .../DataDog/go-libddwaf/v2/README.md | 148 + .../DataDog/go-libddwaf/v2/cgo_ref_pool.go | 97 + .../DataDog/go-libddwaf/v2/context.go | 195 + .../DataDog/go-libddwaf/v2/ctypes.go | 206 + .../DataDog/go-libddwaf/v2/decoder.go | 239 + .../DataDog/go-libddwaf/v2/encoder.go | 477 + .../DataDog/go-libddwaf/v2/handle.go | 222 + .../go-libddwaf/v2/internal/lib/.version | 1 + .../go-libddwaf/v2/internal/lib/README.md | 21 + .../go-libddwaf/v2/internal/lib/doc.go} | 7 +- .../go-libddwaf/v2/internal/lib/lib.go | 49 + .../v2/internal/lib/lib_darwin_amd64.go | 17 + .../v2/internal/lib/lib_darwin_arm64.go | 17 + .../v2/internal/lib/lib_linux_amd64.go | 17 + .../v2/internal/lib/lib_linux_arm64.go | 17 + .../internal/lib/libddwaf-darwin-amd64.dylib | Bin 0 -> 1566256 bytes .../internal/lib/libddwaf-darwin-arm64.dylib | Bin 0 -> 1436976 bytes .../v2/internal/lib/libddwaf-linux-amd64.so | Bin 0 -> 2136144 bytes .../v2/internal/lib/libddwaf-linux-arm64.so | Bin 0 -> 2030704 bytes .../go-libddwaf/v2/internal/log}/ddwaf.h | 320 +- .../go-libddwaf/v2/internal/log/gostring.go | 24 + .../go-libddwaf/v2/internal/log/log.go | 97 + .../go-libddwaf/v2/internal/log/log_cgo.go | 35 + .../go-libddwaf/v2/internal/log/log_purego.go | 36 + .../v2/internal/log/log_unsupported.go | 14 + .../v2/internal/noopfree/noopfree.go | 15 + .../v2/internal/noopfree/noopfree.s} | 9 +- .../github.com/DataDog/go-libddwaf/v2/safe.go | 68 + .../go-libddwaf/v2/symbols_linux_cgo.go | 21 + .../go-libddwaf/v2/symbols_linux_purego.go | 15 + .../github.com/DataDog/go-libddwaf/v2/waf.go | 185 + .../go-libddwaf/v2/waf_cgo_disabled.go | 14 + .../DataDog/go-libddwaf/v2/waf_dl.go | 234 + .../go-libddwaf/v2/waf_dl_unsupported.go | 52 + .../go-libddwaf/v2/waf_manually_disabled.go | 13 + .../DataDog/go-libddwaf/v2/waf_support.go | 85 + .../go-libddwaf/v2/waf_unsupported_go.go | 13 + .../go-libddwaf/v2/waf_unsupported_target.go | 18 + vendor/github.com/DataDog/go-tuf/LICENSE | 27 + .../DataDog/go-tuf/client/client.go | 982 ++ .../DataDog/go-tuf/client/delegations.go | 152 + .../DataDog/go-tuf/client/errors.go | 107 + .../DataDog/go-tuf/client/file_store.go | 90 + .../DataDog/go-tuf/client/local_store.go | 29 + .../DataDog/go-tuf/client/remote_store.go | 109 + .../DataDog/go-tuf/data/hex_bytes.go | 42 + .../github.com/DataDog/go-tuf/data/types.go | 348 + .../DataDog/go-tuf/internal/roles/roles.go | 48 + .../DataDog/go-tuf/internal/sets/strings.go | 24 + .../go-tuf/pkg/keys/deprecated_ecdsa.go | 101 + .../DataDog/go-tuf/pkg/keys/ecdsa.go | 173 + .../DataDog/go-tuf/pkg/keys/ed25519.go | 161 + .../DataDog/go-tuf/pkg/keys/keys.go | 82 + .../DataDog/go-tuf/pkg/keys/pkix.go | 56 + .../github.com/DataDog/go-tuf/pkg/keys/rsa.go | 162 + .../DataDog/go-tuf/pkg/targets/delegation.go | 102 + .../DataDog/go-tuf/pkg/targets/hash_bins.go | 113 + vendor/github.com/DataDog/go-tuf/util/util.go | 332 + vendor/github.com/DataDog/go-tuf/verify/db.go | 104 + .../DataDog/go-tuf/verify/errors.go | 73 + .../DataDog/go-tuf/verify/verify.go | 187 + .../DataDog/sketches-go/ddsketch/ddsketch.go | 153 +- .../mapping/cubically_interpolated_mapping.go | 78 +- .../ddsketch/mapping/index_mapping.go | 2 + .../mapping/linearly_interpolated_mapping.go | 81 +- .../ddsketch/mapping/logarithmic_mapping.go | 68 +- .../ddsketch/pb/sketchpb/ddsketch.pb.go | 4 +- .../sketches-go/ddsketch/stat/summary.go | 26 +- .../ddsketch/store/buffered_paginated.go | 43 +- .../store/collapsing_highest_dense_store.go | 7 +- .../store/collapsing_lowest_dense_store.go | 7 +- .../sketches-go/ddsketch/store/dense_store.go | 7 +- .../sketches-go/ddsketch/store/sparse.go | 7 +- .../Microsoft/go-winio/.gitattributes | 1 + .../github.com/Microsoft/go-winio/.gitignore | 9 + .../Microsoft/go-winio/.golangci.yml | 149 + .../github.com/Microsoft/go-winio/README.md | 72 +- .../github.com/Microsoft/go-winio/SECURITY.md | 41 + .../github.com/Microsoft/go-winio/backup.go | 48 +- vendor/github.com/Microsoft/go-winio/doc.go | 22 + vendor/github.com/Microsoft/go-winio/ea.go | 8 +- vendor/github.com/Microsoft/go-winio/file.go | 70 +- .../github.com/Microsoft/go-winio/fileinfo.go | 29 +- .../github.com/Microsoft/go-winio/hvsock.go | 360 +- .../Microsoft/go-winio/internal/fs/doc.go | 2 + .../Microsoft/go-winio/internal/fs/fs.go | 202 + .../go-winio/internal/fs/security.go | 12 + .../go-winio/internal/fs/zsyscall_windows.go | 64 + .../go-winio/internal/socket/rawaddr.go | 20 + .../go-winio/internal/socket/socket.go | 179 + .../internal/socket/zsyscall_windows.go | 72 + .../go-winio/internal/stringbuffer/wstring.go | 132 + vendor/github.com/Microsoft/go-winio/pipe.go | 134 +- .../Microsoft/go-winio/pkg/guid/guid.go | 25 +- .../go-winio/pkg/guid/guid_nonwindows.go | 16 + .../go-winio/pkg/guid/guid_windows.go | 13 + .../go-winio/pkg/guid/variant_string.go | 27 + .../Microsoft/go-winio/privilege.go | 32 +- .../github.com/Microsoft/go-winio/reparse.go | 11 +- vendor/github.com/Microsoft/go-winio/sd.go | 64 +- .../github.com/Microsoft/go-winio/syscall.go | 4 +- vendor/github.com/Microsoft/go-winio/tools.go | 5 + .../Microsoft/go-winio/zsyscall_windows.go | 64 +- .../coredns/coredns/core/dnsserver/config.go | 6 + .../coredns/coredns/core/dnsserver/https.go | 51 +- .../coredns/coredns/core/dnsserver/quic.go | 60 + .../coredns/core/dnsserver/register.go | 27 +- .../coredns/coredns/core/dnsserver/server.go | 40 +- .../coredns/core/dnsserver/server_https.go | 6 +- .../coredns/core/dnsserver/server_quic.go | 346 + .../coredns/core/dnsserver/server_tls.go | 24 +- .../coredns/core/dnsserver/zdirectives.go | 3 +- .../coredns/coredns/coremain/run.go | 9 +- .../coredns/coredns/coremain/version.go | 2 +- .../coredns/coredns/plugin/bind/README.md | 2 +- .../coredns/coredns/plugin/bind/setup.go | 15 +- .../coredns/coredns/plugin/bufsize/README.md | 16 +- .../coredns/coredns/plugin/bufsize/setup.go | 5 +- .../coredns/coredns/plugin/cache/README.md | 11 +- .../coredns/coredns/plugin/cache/cache.go | 37 +- .../coredns/coredns/plugin/cache/dnssec.go | 28 +- .../coredns/coredns/plugin/cache/handler.go | 46 +- .../coredns/coredns/plugin/cache/item.go | 6 +- .../coredns/coredns/plugin/cache/setup.go | 6 + .../coredns/coredns/plugin/dns64/dns64.go | 2 +- .../coredns/coredns/plugin/dnstap/README.md | 55 +- .../coredns/coredns/plugin/dnstap/handler.go | 40 +- .../coredns/coredns/plugin/dnstap/io.go | 28 +- .../coredns/coredns/plugin/dnstap/setup.go | 161 +- .../coredns/coredns/plugin/dnstap/writer.go | 8 +- .../coredns/coredns/plugin/forward/README.md | 32 +- .../coredns/coredns/plugin/forward/dnstap.go | 61 +- .../coredns/coredns/plugin/forward/forward.go | 68 +- .../coredns/coredns/plugin/forward/fuzz.go | 5 +- .../coredns/coredns/plugin/forward/metrics.go | 42 +- .../coredns/coredns/plugin/forward/policy.go | 15 +- .../coredns/coredns/plugin/forward/setup.go | 40 +- .../coredns/coredns/plugin/health/health.go | 21 +- .../coredns/plugin/health/overloaded.go | 18 +- .../coredns/coredns/plugin/hosts/hostsfile.go | 2 +- .../coredns/coredns/plugin/hosts/setup.go | 1 + .../coredns/plugin/loadbalance/README.md | 61 +- .../coredns/plugin/loadbalance/handler.go | 13 +- .../coredns/plugin/loadbalance/loadbalance.go | 25 +- .../coredns/plugin/loadbalance/setup.go | 82 +- .../coredns/plugin/loadbalance/weighted.go | 329 + .../coredns/plugin/metadata/provider.go | 41 +- .../coredns/coredns/plugin/metrics/README.md | 1 + .../coredns/plugin/metrics/vars/vars.go | 40 +- .../coredns/coredns/plugin/pkg/dnsutil/ttl.go | 1 + .../coredns/coredns/plugin/pkg/doh/doh.go | 23 +- .../coredns/coredns/plugin/pkg/edns/edns.go | 5 +- .../coredns/coredns/plugin/pkg/fall/fall.go | 1 - .../coredns/coredns/plugin/pkg/log/log.go | 18 +- .../coredns/coredns/plugin/pkg/parse/host.go | 12 +- .../coredns/plugin/pkg/parse/transport.go | 7 + .../plugin/{forward => pkg/proxy}/connect.go | 58 +- .../coredns/plugin/pkg/proxy/errors.go | 26 + .../plugin/{forward => pkg/proxy}/health.go | 56 +- .../coredns/plugin/pkg/proxy/metrics.go | 41 + .../{forward => pkg/proxy}/persistent.go | 15 +- .../plugin/{forward => pkg/proxy}/proxy.go | 59 +- .../plugin/{forward => pkg/proxy}/type.go | 6 +- .../coredns/plugin/pkg/replacer/replacer.go | 57 +- .../coredns/coredns/plugin/pkg/tls/tls.go | 41 +- .../coredns/plugin/pkg/transport/transport.go | 4 + .../coredns/coredns/plugin/pkg/up/up.go | 6 +- .../coredns/coredns/plugin/plugin.go | 4 + .../coredns/coredns/plugin/reload/README.md | 2 +- .../coredns/coredns/plugin/reload/reload.go | 1 + .../coredns/coredns/plugin/rewrite/README.md | 103 + .../coredns/plugin/rewrite/cname_target.go | 152 + .../coredns/coredns/plugin/rewrite/name.go | 4 +- .../coredns/coredns/plugin/rewrite/rcode.go | 178 + .../coredns/plugin/rewrite/reverter.go | 4 +- .../coredns/coredns/plugin/rewrite/rewrite.go | 4 + .../coredns/coredns/plugin/rewrite/ttl.go | 2 +- .../coredns/coredns/plugin/rewrite/wire.go | 2 +- .../coredns/coredns/plugin/template/README.md | 4 + .../coredns/coredns/plugin/template/setup.go | 17 + .../coredns/plugin/template/template.go | 12 + .../coredns/coredns/plugin/test/file.go | 23 +- .../coredns/coredns/plugin/test/helpers.go | 22 +- .../coredns/coredns/plugin/test/scrape.go | 3 +- .../coreos/go-semver/semver/semver.go | 2 +- .../go-systemd/v22/journal/journal_unix.go | 75 +- .../go-systemd/v22/journal/journal_windows.go | 8 + .../github.com/dustin/go-humanize/.travis.yml | 16 +- .../dustin/go-humanize/README.markdown | 2 +- .../github.com/dustin/go-humanize/bigbytes.go | 20 +- .../github.com/dustin/go-humanize/commaf.go | 1 + vendor/github.com/dustin/go-humanize/ftoa.go | 3 + .../github.com/dustin/go-humanize/number.go | 2 +- vendor/github.com/dustin/go-humanize/si.go | 4 + .../github.com/ebitengine/purego/.gitignore | 1 + vendor/github.com/ebitengine/purego/LICENSE | 201 + vendor/github.com/ebitengine/purego/README.md | 96 + .../github.com/ebitengine/purego/abi_amd64.h | 99 + .../github.com/ebitengine/purego/abi_arm64.h | 39 + vendor/github.com/ebitengine/purego/cgo.go | 19 + .../github.com/ebitengine/purego/dlerror.go | 15 + vendor/github.com/ebitengine/purego/dlfcn.go | 94 + .../ebitengine/purego/dlfcn_darwin.go | 24 + .../ebitengine/purego/dlfcn_freebsd.go | 13 + .../ebitengine/purego/dlfcn_linux.go | 14 + .../ebitengine/purego/dlfcn_nocgo_freebsd.go | 11 + .../ebitengine/purego/dlfcn_nocgo_linux.go | 19 + .../ebitengine/purego/dlfcn_stubs.s | 26 + vendor/github.com/ebitengine/purego/func.go | 338 + .../ebitengine/purego/go_runtime.go | 17 + .../purego/internal/cgo/dlfcn_cgo_unix.go | 22 + .../ebitengine/purego/internal/cgo/empty.go | 6 + .../purego/internal/cgo/syscall_cgo_unix.go | 55 + .../purego/internal/fakecgo/abi_amd64.h | 99 + .../purego/internal/fakecgo/abi_arm64.h | 39 + .../purego/internal/fakecgo/asm_amd64.s | 39 + .../purego/internal/fakecgo/asm_arm64.s | 36 + .../purego/internal/fakecgo/callbacks.go | 93 + .../ebitengine/purego/internal/fakecgo/doc.go | 33 + .../purego/internal/fakecgo/freebsd.go | 27 + .../internal/fakecgo/go_darwin_amd64.go | 71 + .../internal/fakecgo/go_darwin_arm64.go | 86 + .../internal/fakecgo/go_freebsd_amd64.go | 93 + .../internal/fakecgo/go_freebsd_arm64.go | 96 + .../purego/internal/fakecgo/go_libinit.go | 66 + .../purego/internal/fakecgo/go_linux_amd64.go | 93 + .../purego/internal/fakecgo/go_linux_arm64.go | 96 + .../purego/internal/fakecgo/go_setenv.go | 18 + .../purego/internal/fakecgo/go_util.go | 33 + .../purego/internal/fakecgo/iscgo.go | 19 + .../purego/internal/fakecgo/libcgo.go | 35 + .../purego/internal/fakecgo/libcgo_darwin.go | 20 + .../purego/internal/fakecgo/libcgo_freebsd.go | 14 + .../purego/internal/fakecgo/libcgo_linux.go | 14 + .../purego/internal/fakecgo/setenv.go | 19 + .../purego/internal/fakecgo/symbols.go | 184 + .../purego/internal/fakecgo/symbols_darwin.go | 27 + .../internal/fakecgo/symbols_freebsd.go | 27 + .../purego/internal/fakecgo/symbols_linux.go | 27 + .../internal/fakecgo/trampolines_amd64.s | 104 + .../internal/fakecgo/trampolines_arm64.s | 72 + .../internal/fakecgo/trampolines_stubs.s | 90 + .../purego/internal/strings/strings.go | 40 + vendor/github.com/ebitengine/purego/is_ios.go | 13 + vendor/github.com/ebitengine/purego/nocgo.go | 25 + .../ebitengine/purego/struct_amd64.go | 203 + .../ebitengine/purego/struct_arm64.go | 224 + .../ebitengine/purego/struct_other.go | 12 + .../github.com/ebitengine/purego/sys_amd64.s | 162 + .../github.com/ebitengine/purego/sys_arm64.s | 85 + .../ebitengine/purego/sys_unix_arm64.s | 70 + .../github.com/ebitengine/purego/syscall.go | 40 + .../ebitengine/purego/syscall_cgo_linux.go | 30 + .../ebitengine/purego/syscall_sysv.go | 209 + .../ebitengine/purego/syscall_windows.go | 45 + .../ebitengine/purego/zcallback_amd64.s | 2014 +++ .../ebitengine/purego/zcallback_arm64.s | 4014 ++++++ .../emicklei/go-restful/v3/CHANGES.md | 24 +- .../emicklei/go-restful/v3/README.md | 5 +- .../emicklei/go-restful/v3/constants.go | 2 + .../emicklei/go-restful/v3/request.go | 5 +- .../emicklei/go-restful/v3/response.go | 3 + .../emicklei/go-restful/v3/route.go | 17 +- .../emicklei/go-restful/v3/route_builder.go | 55 +- vendor/github.com/evanphx/json-patch/patch.go | 42 - vendor/github.com/go-logr/logr/.golangci.yaml | 3 - vendor/github.com/go-logr/logr/README.md | 126 +- vendor/github.com/go-logr/logr/SECURITY.md | 18 + vendor/github.com/go-logr/logr/context.go | 33 + .../github.com/go-logr/logr/context_noslog.go | 49 + .../github.com/go-logr/logr/context_slog.go | 83 + vendor/github.com/go-logr/logr/discard.go | 32 +- vendor/github.com/go-logr/logr/logr.go | 240 +- vendor/github.com/go-logr/logr/sloghandler.go | 192 + vendor/github.com/go-logr/logr/slogr.go | 100 + vendor/github.com/go-logr/logr/slogsink.go | 120 + .../go-openapi/jsonpointer/.travis.yml | 15 - .../go-openapi/jsonreference/.golangci.yml | 13 +- .../go-openapi/jsonreference/.travis.yml | 24 - .../jsonreference/internal/normalize_url.go | 22 +- .../github.com/go-openapi/swag/.gitattributes | 2 + .../github.com/go-openapi/swag/.golangci.yml | 15 + vendor/github.com/go-openapi/swag/.travis.yml | 37 - vendor/github.com/go-openapi/swag/doc.go | 15 +- vendor/github.com/go-openapi/swag/file.go | 33 + vendor/github.com/go-openapi/swag/loading.go | 11 +- .../github.com/go-openapi/swag/post_go18.go | 1 + .../github.com/go-openapi/swag/post_go19.go | 1 + vendor/github.com/go-openapi/swag/pre_go18.go | 1 + vendor/github.com/go-openapi/swag/pre_go19.go | 1 + vendor/github.com/go-openapi/swag/util.go | 17 +- vendor/github.com/go-openapi/swag/yaml.go | 252 +- vendor/github.com/golang/glog/LICENSE | 191 - vendor/github.com/golang/glog/README.md | 36 - vendor/github.com/golang/glog/glog.go | 621 - vendor/github.com/golang/glog/glog_file.go | 407 - vendor/github.com/golang/glog/glog_flags.go | 395 - .../golang/glog/internal/logsink/logsink.go | 387 - .../glog/internal/logsink/logsink_fatal.go | 35 - .../glog/internal/stackdump/stackdump.go | 127 - .../golang/protobuf/jsonpb/decode.go | 530 - .../golang/protobuf/jsonpb/encode.go | 559 - .../github.com/golang/protobuf/jsonpb/json.go | 69 - .../github.com/golang/protobuf/ptypes/any.go | 7 +- .../{gnostic => gnostic-models}/LICENSE | 0 .../compiler/README.md | 0 .../compiler/context.go | 0 .../compiler/error.go | 0 .../compiler/extensions.go | 2 +- .../compiler/helpers.go | 2 +- .../compiler/main.go | 0 .../compiler/reader.go | 0 .../extensions/README.md | 0 .../extensions/extension.pb.go | 4 +- .../extensions/extension.proto | 0 .../extensions/extensions.go | 0 .../jsonschema/README.md | 0 .../jsonschema/base.go | 15 +- .../jsonschema/display.go | 0 .../jsonschema/models.go | 0 .../jsonschema/operations.go | 0 .../jsonschema/reader.go | 0 .../jsonschema/schema.json | 0 .../jsonschema/writer.go | 0 .../openapiv2/OpenAPIv2.go | 2 +- .../openapiv2/OpenAPIv2.pb.go | 4 +- .../openapiv2/OpenAPIv2.proto | 0 .../openapiv2/README.md | 0 .../openapiv2/document.go | 2 +- .../openapiv2/openapi-2.0.json | 0 .../openapiv3/OpenAPIv3.go | 2 +- .../openapiv3/OpenAPIv3.pb.go | 4 +- .../openapiv3/OpenAPIv3.proto | 0 .../openapiv3/README.md | 0 .../openapiv3/document.go | 2 +- .../google/gnostic/openapiv3/openapi-3.0.json | 1251 -- .../google/gnostic/openapiv3/openapi-3.1.json | 1250 -- .../github.com/google/go-cmp/cmp/compare.go | 38 +- .../cmp/{export_unsafe.go => export.go} | 5 - .../google/go-cmp/cmp/export_panic.go | 16 - .../value/{pointer_unsafe.go => pointer.go} | 3 - .../cmp/internal/value/pointer_purego.go | 34 - .../github.com/google/go-cmp/cmp/options.go | 84 +- vendor/github.com/google/go-cmp/cmp/path.go | 46 +- .../google/go-cmp/cmp/report_reflect.go | 2 +- vendor/github.com/google/pprof/AUTHORS | 7 + vendor/github.com/google/pprof/CONTRIBUTORS | 16 + .../google/pprof}/LICENSE | 0 .../github.com/google/pprof/profile/encode.go | 588 + .../github.com/google/pprof/profile/filter.go | 274 + .../github.com/google/pprof/profile/index.go | 64 + .../pprof/profile/legacy_java_profile.go | 315 + .../google/pprof/profile/legacy_profile.go | 1228 ++ .../github.com/google/pprof/profile/merge.go | 667 + .../google/pprof/profile/profile.go | 856 ++ .../github.com/google/pprof/profile/proto.go | 367 + .../github.com/google/pprof/profile/prune.go | 194 + vendor/github.com/google/uuid/.travis.yml | 9 - vendor/github.com/google/uuid/CHANGELOG.md | 41 + vendor/github.com/google/uuid/CONTRIBUTING.md | 16 + vendor/github.com/google/uuid/README.md | 10 +- vendor/github.com/google/uuid/hash.go | 6 + vendor/github.com/google/uuid/node_js.go | 2 +- vendor/github.com/google/uuid/time.go | 21 +- vendor/github.com/google/uuid/uuid.go | 89 +- vendor/github.com/google/uuid/version6.go | 56 + vendor/github.com/google/uuid/version7.go | 104 + vendor/github.com/hashicorp/errwrap/LICENSE | 354 + vendor/github.com/hashicorp/errwrap/README.md | 89 + .../github.com/hashicorp/errwrap/errwrap.go | 178 + .../hashicorp/go-multierror/LICENSE | 353 + .../hashicorp/go-multierror/Makefile | 31 + .../hashicorp/go-multierror/README.md | 150 + .../hashicorp/go-multierror/append.go | 43 + .../hashicorp/go-multierror/flatten.go | 26 + .../hashicorp/go-multierror/format.go | 27 + .../hashicorp/go-multierror/group.go | 38 + .../hashicorp/go-multierror/multierror.go | 121 + .../hashicorp/go-multierror/prefix.go | 37 + .../hashicorp/go-multierror/sort.go | 16 + .../inconshreveable/mousetrap/trap_others.go | 1 + .../inconshreveable/mousetrap/trap_windows.go | 88 +- .../mousetrap/trap_windows_1.4.go | 46 - vendor/github.com/mailru/easyjson/.gitignore | 6 - vendor/github.com/mailru/easyjson/.travis.yml | 15 - vendor/github.com/mailru/easyjson/Makefile | 72 - vendor/github.com/mailru/easyjson/README.md | 387 - vendor/github.com/mailru/easyjson/helpers.go | 114 - vendor/github.com/mailru/easyjson/raw.go | 45 - .../mailru/easyjson/unknown_fields.go | 32 - vendor/github.com/miekg/dns/LICENSE | 49 +- vendor/github.com/miekg/dns/README.md | 9 + vendor/github.com/miekg/dns/acceptfunc.go | 3 - vendor/github.com/miekg/dns/client.go | 69 +- vendor/github.com/miekg/dns/clientconfig.go | 2 +- vendor/github.com/miekg/dns/defaults.go | 54 +- vendor/github.com/miekg/dns/dnssec.go | 14 +- vendor/github.com/miekg/dns/dnssec_keyscan.go | 5 +- vendor/github.com/miekg/dns/doc.go | 86 +- vendor/github.com/miekg/dns/edns.go | 55 +- vendor/github.com/miekg/dns/fuzz.go | 1 + vendor/github.com/miekg/dns/generate.go | 35 +- vendor/github.com/miekg/dns/labels.go | 2 +- .../miekg/dns/listen_no_reuseport.go | 13 +- .../github.com/miekg/dns/listen_reuseport.go | 32 +- vendor/github.com/miekg/dns/msg.go | 111 +- vendor/github.com/miekg/dns/msg_helpers.go | 92 +- vendor/github.com/miekg/dns/privaterr.go | 2 +- vendor/github.com/miekg/dns/scan.go | 162 +- vendor/github.com/miekg/dns/scan_rr.go | 492 +- vendor/github.com/miekg/dns/server.go | 14 +- vendor/github.com/miekg/dns/singleinflight.go | 61 - vendor/github.com/miekg/dns/svcb.go | 122 +- vendor/github.com/miekg/dns/tools.go | 1 + vendor/github.com/miekg/dns/types.go | 157 +- vendor/github.com/miekg/dns/udp.go | 1 + vendor/github.com/miekg/dns/udp_windows.go | 8 +- vendor/github.com/miekg/dns/version.go | 2 +- vendor/github.com/miekg/dns/xfr.go | 19 +- vendor/github.com/miekg/dns/zduplicate.go | 93 + vendor/github.com/miekg/dns/zmsg.go | 170 + vendor/github.com/miekg/dns/ztypes.go | 472 +- vendor/github.com/onsi/ginkgo/v2/LICENSE | 20 + .../onsi/ginkgo/v2/config/deprecated.go | 69 + .../ginkgo/v2/formatter/colorable_others.go | 41 + .../ginkgo/v2/formatter/colorable_windows.go | 809 ++ .../onsi/ginkgo/v2/formatter/formatter.go | 230 + .../ginkgo/v2/ginkgo/build/build_command.go | 63 + .../onsi/ginkgo/v2/ginkgo/command/abort.go | 61 + .../onsi/ginkgo/v2/ginkgo/command/command.go | 50 + .../onsi/ginkgo/v2/ginkgo/command/program.go | 182 + .../ginkgo/generators/boostrap_templates.go | 48 + .../v2/ginkgo/generators/bootstrap_command.go | 133 + .../v2/ginkgo/generators/generate_command.go | 264 + .../ginkgo/generators/generate_templates.go | 43 + .../v2/ginkgo/generators/generators_common.go | 76 + .../onsi/ginkgo/v2/ginkgo/internal/compile.go | 161 + .../ginkgo/internal/profiles_and_reports.go | 237 + .../onsi/ginkgo/v2/ginkgo/internal/run.go | 355 + .../ginkgo/v2/ginkgo/internal/test_suite.go | 283 + .../onsi/ginkgo/v2/ginkgo/internal/utils.go | 86 + .../v2/ginkgo/internal/verify_version.go | 54 + .../ginkgo/v2/ginkgo/labels/labels_command.go | 123 + .../github.com/onsi/ginkgo/v2/ginkgo/main.go | 58 + .../onsi/ginkgo/v2/ginkgo/outline/ginkgo.go | 300 + .../onsi/ginkgo/v2/ginkgo/outline/import.go | 65 + .../onsi/ginkgo/v2/ginkgo/outline/outline.go | 110 + .../v2/ginkgo/outline/outline_command.go | 98 + .../onsi/ginkgo/v2/ginkgo/run/run_command.go | 232 + .../v2/ginkgo/unfocus/unfocus_command.go | 186 + .../onsi/ginkgo/v2/ginkgo/watch/delta.go | 22 + .../ginkgo/v2/ginkgo/watch/delta_tracker.go | 75 + .../ginkgo/v2/ginkgo/watch/dependencies.go | 92 + .../ginkgo/v2/ginkgo/watch/package_hash.go | 108 + .../ginkgo/v2/ginkgo/watch/package_hashes.go | 85 + .../onsi/ginkgo/v2/ginkgo/watch/suite.go | 87 + .../ginkgo/v2/ginkgo/watch/watch_command.go | 192 + .../interrupt_handler/interrupt_handler.go | 177 + .../sigquit_swallower_unix.go | 15 + .../sigquit_swallower_windows.go | 8 + .../parallel_support/client_server.go | 72 + .../internal/parallel_support/http_client.go | 169 + .../internal/parallel_support/http_server.go | 242 + .../internal/parallel_support/rpc_client.go | 136 + .../internal/parallel_support/rpc_server.go | 75 + .../parallel_support/server_handler.go | 234 + .../ginkgo/v2/reporters/default_reporter.go | 759 ++ .../v2/reporters/deprecated_reporter.go | 149 + .../onsi/ginkgo/v2/reporters/json_report.go | 67 + .../onsi/ginkgo/v2/reporters/junit_report.go | 376 + .../onsi/ginkgo/v2/reporters/reporter.go | 29 + .../ginkgo/v2/reporters/teamcity_report.go | 105 + .../onsi/ginkgo/v2/types/code_location.go | 159 + .../github.com/onsi/ginkgo/v2/types/config.go | 758 ++ .../onsi/ginkgo/v2/types/deprecated_types.go | 141 + .../ginkgo/v2/types/deprecation_support.go | 177 + .../onsi/ginkgo/v2/types/enum_support.go | 43 + .../github.com/onsi/ginkgo/v2/types/errors.go | 630 + .../onsi/ginkgo/v2/types/file_filter.go | 106 + .../github.com/onsi/ginkgo/v2/types/flags.go | 489 + .../onsi/ginkgo/v2/types/label_filter.go | 358 + .../onsi/ginkgo/v2/types/report_entry.go | 190 + .../github.com/onsi/ginkgo/v2/types/types.go | 914 ++ .../onsi/ginkgo/v2/types/version.go | 3 + vendor/github.com/onsi/gomega/.gitignore | 3 +- vendor/github.com/onsi/gomega/CHANGELOG.md | 164 + vendor/github.com/onsi/gomega/RELEASING.md | 2 +- .../github.com/onsi/gomega/format/format.go | 10 +- vendor/github.com/onsi/gomega/gomega_dsl.go | 30 +- .../onsi/gomega/internal/async_assertion.go | 220 +- .../github.com/onsi/gomega/internal/gomega.go | 39 +- vendor/github.com/onsi/gomega/matchers.go | 24 +- .../onsi/gomega/matchers/be_a_directory.go | 2 +- .../onsi/gomega/matchers/be_a_regular_file.go | 2 +- .../gomega/matchers/be_an_existing_file.go | 4 +- .../onsi/gomega/matchers/consist_of.go | 29 +- .../gomega/matchers/have_exact_elements.go | 88 + .../gomega/matchers/have_occurred_matcher.go | 2 +- .../gomega/matchers/match_error_matcher.go | 12 +- .../onsi/gomega/matchers/succeed_matcher.go | 11 +- vendor/github.com/onsi/gomega/types/types.go | 9 +- .../zipkin-go-opentracing/.travis.yml | 17 +- .../zipkin-go-opentracing/context.go | 2 +- .../zipkin-go-opentracing/propagation.go | 2 +- .../zipkin-go-opentracing/span.go | 2 +- .../zipkin-go-opentracing/tracer.go | 2 +- .../zipkin-go-opentracing/tracer_options.go | 2 +- .../openzipkin/zipkin-go/.golangci.yml | 3 +- .../github.com/openzipkin/zipkin-go/README.md | 11 +- .../zipkin-go/propagation/b3/http.go | 2 + .../ristretto/.deepsource.toml | 0 .../github.com/outcaste-io/ristretto/.mailmap | 1 + .../ristretto/CHANGELOG.md | 0 .../ristretto/LICENSE | 0 .../ristretto/README.md | 63 +- .../ristretto/cache.go | 277 +- .../outcaste-io/ristretto/metrics.go | 249 + .../ristretto/policy.go | 137 +- .../ristretto/ring.go | 0 .../ristretto/sketch.go | 0 .../ristretto/store.go | 73 +- .../ristretto/test.sh | 6 +- .../ristretto/ttl.go | 12 +- .../ristretto/z/LICENSE | 0 .../ristretto/z/README.md | 0 .../ristretto/z/allocator.go | 0 .../ristretto/z/bbloom.go | 6 +- .../ristretto/z/btree.go | 2 +- .../ristretto/z/buffer.go | 5 +- .../ristretto/z/calloc.go | 0 .../ristretto/z/calloc_32bit.go | 0 .../ristretto/z/calloc_64bit.go | 0 .../ristretto/z/calloc_jemalloc.go | 7 +- .../ristretto/z/calloc_nojemalloc.go | 0 .../ristretto/z/file.go | 0 .../ristretto/z/file_default.go | 0 .../ristretto/z/file_linux.go | 0 .../ristretto/z/flags.go | 35 +- .../ristretto/z/histogram.go | 0 .../ristretto/z/mmap.go | 0 .../ristretto/z/mmap_darwin.go | 0 .../ristretto/z/mmap_linux.go | 6 +- .../ristretto/z/mmap_plan9.go | 0 .../ristretto/z/mmap_unix.go | 3 +- .../outcaste-io/ristretto/z/mmap_wasip1.go} | 35 +- .../ristretto/z/mmap_windows.go | 3 +- .../ristretto/z/rtutil.go | 0 .../ristretto/z/rtutil.s | 0 .../ristretto/z/simd/baseline.go | 0 .../ristretto/z/simd/search.go | 2 +- .../ristretto/z/simd/search_amd64.s | 0 .../ristretto/z/simd/stub_search_amd64.go | 0 .../ristretto/z/z.go | 12 + vendor/github.com/philhofer/fwd/README.md | 120 +- vendor/github.com/philhofer/fwd/reader.go | 40 +- .../philhofer/fwd/writer_appengine.go | 1 + .../github.com/philhofer/fwd/writer_tinygo.go | 19 + .../github.com/philhofer/fwd/writer_unsafe.go | 18 +- .../prometheus/collectors/collectors.go | 40 + .../collectors/dbstats_collector.go | 119 + .../prometheus/collectors/expvar_collector.go | 57 + .../collectors/go_collector_go116.go | 49 + .../collectors/go_collector_latest.go | 165 + .../collectors/process_collector.go | 56 + .../client_golang/prometheus/counter.go | 48 +- .../client_golang/prometheus/desc.go | 58 +- .../client_golang/prometheus/doc.go | 44 +- .../prometheus/expvar_collector.go | 2 +- .../client_golang/prometheus/gauge.go | 30 +- .../prometheus/go_collector_latest.go | 7 +- .../client_golang/prometheus/histogram.go | 221 +- .../prometheus/internal/difflib.go | 2 +- .../client_golang/prometheus/labels.go | 100 + .../client_golang/prometheus/metric.go | 9 +- .../prometheus/process_collector_other.go | 4 +- .../prometheus/process_collector_wasip1.go | 26 + .../client_golang/prometheus/promauto/auto.go | 34 +- .../client_golang/prometheus/promhttp/http.go | 19 +- .../prometheus/promhttp/instrument_client.go | 26 +- .../prometheus/promhttp/instrument_server.go | 110 +- .../prometheus/promhttp/option.go | 38 +- .../client_golang/prometheus/registry.go | 19 +- .../client_golang/prometheus/summary.go | 72 +- .../client_golang/prometheus/timer.go | 28 +- .../client_golang/prometheus/value.go | 61 +- .../client_golang/prometheus/vec.go | 95 +- .../client_golang/prometheus/vnext.go | 23 + .../client_golang/prometheus/wrap.go | 8 +- .../prometheus/client_model/go/metrics.pb.go | 1601 ++- .../prometheus/common/expfmt/decode.go | 70 +- .../prometheus/common/expfmt/encode.go | 87 +- .../prometheus/common/expfmt/expfmt.go | 166 +- .../prometheus/common/expfmt/fuzz.go | 4 +- .../common/expfmt/openmetrics_create.go | 297 +- .../prometheus/common/expfmt/text_create.go | 121 +- .../prometheus/common/expfmt/text_parse.go | 16 +- .../bitbucket.org/ww/goautoneg/autoneg.go | 22 +- .../prometheus/common/model/alert.go | 31 +- .../prometheus/common/model/labels.go | 22 +- .../prometheus/common/model/labelset.go | 11 - .../common/model/labelset_string.go | 45 + .../common/model/labelset_string_go120.go | 39 + .../prometheus/common/model/metadata.go | 28 + .../prometheus/common/model/metric.go | 369 +- .../prometheus/common/model/signature.go | 6 +- .../prometheus/common/model/silence.go | 2 +- .../prometheus/common/model/time.go | 89 +- .../prometheus/common/model/value.go | 262 +- .../prometheus/common/model/value_float.go | 98 + .../common/model/value_histogram.go | 178 + .../prometheus/common/model/value_type.go | 83 + .../prometheus/procfs/.golangci.yml | 3 + .../prometheus/procfs/Makefile.common | 27 +- vendor/github.com/prometheus/procfs/README.md | 4 +- vendor/github.com/prometheus/procfs/arp.go | 6 +- .../github.com/prometheus/procfs/buddyinfo.go | 6 +- .../github.com/prometheus/procfs/cpuinfo.go | 53 +- .../prometheus/procfs/cpuinfo_loong64.go | 19 + .../prometheus/procfs/cpuinfo_others.go | 4 +- vendor/github.com/prometheus/procfs/crypto.go | 7 +- vendor/github.com/prometheus/procfs/doc.go | 51 +- vendor/github.com/prometheus/procfs/fs.go | 11 +- .../prometheus/procfs/fs_statfs_notype.go | 23 + .../prometheus/procfs/fs_statfs_type.go | 33 + .../github.com/prometheus/procfs/fscache.go | 6 +- .../prometheus/procfs/internal/util/parse.go | 15 + vendor/github.com/prometheus/procfs/ipvs.go | 7 +- .../github.com/prometheus/procfs/loadavg.go | 4 +- vendor/github.com/prometheus/procfs/mdstat.go | 36 +- .../github.com/prometheus/procfs/meminfo.go | 4 +- .../github.com/prometheus/procfs/mountinfo.go | 10 +- .../prometheus/procfs/mountstats.go | 120 +- .../prometheus/procfs/net_conntrackstat.go | 91 +- .../prometheus/procfs/net_ip_socket.go | 32 +- .../prometheus/procfs/net_protocols.go | 4 +- .../github.com/prometheus/procfs/net_route.go | 143 + .../prometheus/procfs/net_sockstat.go | 9 +- .../prometheus/procfs/net_softnet.go | 79 +- .../github.com/prometheus/procfs/net_unix.go | 16 +- .../prometheus/procfs/net_wireless.go | 182 + .../github.com/prometheus/procfs/net_xfrm.go | 2 +- .../github.com/prometheus/procfs/netstat.go | 54 +- vendor/github.com/prometheus/procfs/proc.go | 37 +- .../prometheus/procfs/proc_cgroup.go | 6 +- .../prometheus/procfs/proc_cgroups.go | 8 +- .../prometheus/procfs/proc_fdinfo.go | 10 +- .../prometheus/procfs/proc_interrupts.go | 98 + .../prometheus/procfs/proc_limits.go | 4 +- .../github.com/prometheus/procfs/proc_maps.go | 24 +- .../prometheus/procfs/proc_netstat.go | 495 +- .../github.com/prometheus/procfs/proc_ns.go | 6 +- .../github.com/prometheus/procfs/proc_psi.go | 6 +- .../prometheus/procfs/proc_smaps.go | 4 +- .../github.com/prometheus/procfs/proc_snmp.go | 322 +- .../prometheus/procfs/proc_snmp6.go | 364 +- .../github.com/prometheus/procfs/proc_stat.go | 12 +- .../prometheus/procfs/proc_status.go | 59 +- .../github.com/prometheus/procfs/proc_sys.go | 2 +- vendor/github.com/prometheus/procfs/slab.go | 2 +- .../github.com/prometheus/procfs/softirqs.go | 24 +- vendor/github.com/prometheus/procfs/stat.go | 50 +- vendor/github.com/prometheus/procfs/swaps.go | 8 +- vendor/github.com/prometheus/procfs/thread.go | 80 + vendor/github.com/prometheus/procfs/vm.go | 6 +- .../github.com/prometheus/procfs/zoneinfo.go | 4 +- vendor/github.com/quic-go/quic-go/.gitignore | 17 + .../github.com/quic-go/quic-go/.golangci.yml | 33 + .../github.com/quic-go/quic-go/Changelog.md | 109 + vendor/github.com/quic-go/quic-go/LICENSE | 21 + vendor/github.com/quic-go/quic-go/README.md | 249 + vendor/github.com/quic-go/quic-go/SECURITY.md | 19 + .../github.com/quic-go/quic-go/buffer_pool.go | 92 + vendor/github.com/quic-go/quic-go/client.go | 251 + .../github.com/quic-go/quic-go/closed_conn.go | 57 + vendor/github.com/quic-go/quic-go/codecov.yml | 16 + vendor/github.com/quic-go/quic-go/config.go | 117 + .../quic-go/quic-go/conn_id_generator.go | 138 + .../quic-go/quic-go/conn_id_manager.go | 214 + .../github.com/quic-go/quic-go/connection.go | 2393 ++++ .../quic-go/quic-go/connection_timer.go | 51 + .../quic-go/quic-go/crypto_stream.go | 106 + .../quic-go/quic-go/crypto_stream_manager.go | 82 + .../quic-go/quic-go/datagram_queue.go | 137 + vendor/github.com/quic-go/quic-go/errors.go | 75 + .../quic-go/quic-go/frame_sorter.go | 237 + vendor/github.com/quic-go/quic-go/framer.go | 210 + .../github.com/quic-go/quic-go/interface.go | 362 + .../internal/ackhandler/ack_eliciting.go | 20 + .../quic-go/internal/ackhandler/ackhandler.go | 24 + .../quic-go/internal/ackhandler/ecn.go | 296 + .../quic-go/internal/ackhandler/frame.go | 21 + .../quic-go/internal/ackhandler/interfaces.go | 53 + .../quic-go/internal/ackhandler/mockgen.go | 9 + .../quic-go/internal/ackhandler/packet.go | 55 + .../ackhandler/packet_number_generator.go | 84 + .../ackhandler/received_packet_handler.go | 125 + .../ackhandler/received_packet_history.go | 151 + .../ackhandler/received_packet_tracker.go | 220 + .../quic-go/internal/ackhandler/send_mode.go | 46 + .../ackhandler/sent_packet_handler.go | 928 ++ .../ackhandler/sent_packet_history.go | 177 + .../quic-go/internal/congestion/bandwidth.go | 25 + .../quic-go/internal/congestion/clock.go | 18 + .../quic-go/internal/congestion/cubic.go | 213 + .../internal/congestion/cubic_sender.go | 316 + .../internal/congestion/hybrid_slow_start.go | 112 + .../quic-go/internal/congestion/interface.go | 28 + .../quic-go/internal/congestion/pacer.go | 84 + .../flowcontrol/base_flow_controller.go | 127 + .../flowcontrol/connection_flow_controller.go | 112 + .../quic-go/internal/flowcontrol/interface.go | 41 + .../flowcontrol/stream_flow_controller.go | 149 + .../quic-go/internal/handshake/aead.go | 90 + .../internal/handshake/cipher_suite.go | 104 + .../quic-go/internal/handshake/conn.go | 21 + .../internal/handshake/crypto_setup.go | 663 + .../internal/handshake/header_protector.go | 134 + .../quic-go/internal/handshake/hkdf.go | 29 + .../internal/handshake/initial_aead.go | 71 + .../quic-go/internal/handshake/interface.go | 116 + .../quic-go/internal/handshake/retry.go | 63 + .../internal/handshake/session_ticket.go | 54 + .../internal/handshake/token_generator.go | 120 + .../internal/handshake/token_protector.go | 82 + .../internal/handshake/updatable_aead.go | 332 + .../quic-go/internal/logutils/frame.go | 50 + .../internal/protocol/connection_id.go | 116 + .../internal/protocol/encryption_level.go | 30 + .../quic-go/internal/protocol/key_phase.go | 36 + .../internal/protocol/packet_number.go | 79 + .../quic-go/internal/protocol/params.go | 183 + .../quic-go/internal/protocol/perspective.go | 26 + .../quic-go/internal/protocol/protocol.go | 152 + .../quic-go/internal/protocol/stream.go | 76 + .../quic-go/internal/protocol/version.go | 114 + .../quic-go/internal/qerr/error_codes.go | 87 + .../quic-go/quic-go/internal/qerr/errors.go | 139 + .../quic-go/internal/qtls/cipher_suite.go | 52 + .../internal/qtls/client_session_cache.go | 70 + .../quic-go/quic-go/internal/qtls/qtls.go | 124 + .../internal/utils/buffered_write_closer.go | 26 + .../quic-go/internal/utils/byteorder.go | 21 + .../internal/utils/byteorder_big_endian.go | 103 + .../quic-go/quic-go/internal/utils/ip.go | 10 + .../internal/utils/linkedlist/README.md | 6 + .../internal/utils/linkedlist/linkedlist.go | 264 + .../quic-go/quic-go/internal/utils/log.go | 131 + .../quic-go/quic-go/internal/utils/minmax.go | 36 + .../quic-go/quic-go/internal/utils/rand.go | 29 + .../internal/utils/ringbuffer/ringbuffer.go | 96 + .../quic-go/internal/utils/rtt_stats.go | 131 + .../quic-go/quic-go/internal/utils/timer.go | 57 + .../quic-go/internal/wire/ack_frame.go | 266 + .../quic-go/internal/wire/ack_range.go | 14 + .../internal/wire/connection_close_frame.go | 78 + .../quic-go/internal/wire/crypto_frame.go | 98 + .../internal/wire/data_blocked_frame.go | 31 + .../quic-go/internal/wire/datagram_frame.go | 86 + .../quic-go/internal/wire/extended_header.go | 210 + .../quic-go/internal/wire/frame_parser.go | 192 + .../internal/wire/handshake_done_frame.go | 17 + .../quic-go/quic-go/internal/wire/header.go | 300 + .../quic-go/internal/wire/interface.go | 11 + .../quic-go/quic-go/internal/wire/log.go | 74 + .../quic-go/internal/wire/max_data_frame.go | 35 + .../internal/wire/max_stream_data_frame.go | 42 + .../internal/wire/max_streams_frame.go | 50 + .../internal/wire/new_connection_id_frame.go | 77 + .../quic-go/internal/wire/new_token_frame.go | 45 + .../internal/wire/path_challenge_frame.go | 35 + .../internal/wire/path_response_frame.go | 35 + .../quic-go/internal/wire/ping_frame.go | 17 + .../quic-go/quic-go/internal/wire/pool.go | 33 + .../internal/wire/reset_stream_frame.go | 54 + .../wire/retire_connection_id_frame.go | 32 + .../quic-go/internal/wire/short_header.go | 73 + .../internal/wire/stop_sending_frame.go | 44 + .../wire/stream_data_blocked_frame.go | 42 + .../quic-go/internal/wire/stream_frame.go | 184 + .../internal/wire/streams_blocked_frame.go | 50 + .../internal/wire/transport_parameters.go | 520 + .../internal/wire/version_negotiation.go | 53 + .../quic-go/logging/connection_tracer.go | 263 + .../quic-go/quic-go/logging/frame.go | 66 + .../quic-go/quic-go/logging/interface.go | 111 + .../quic-go/quic-go/logging/packet_header.go | 24 + .../quic-go/quic-go/logging/tracer.go | 59 + .../quic-go/quic-go/logging/types.go | 128 + vendor/github.com/quic-go/quic-go/mockgen.go | 76 + .../quic-go/quic-go/mtu_discoverer.go | 119 + .../github.com/quic-go/quic-go/multiplexer.go | 75 + vendor/github.com/quic-go/quic-go/oss-fuzz.sh | 42 + .../quic-go/quic-go/packet_handler_map.go | 255 + .../quic-go/quic-go/packet_packer.go | 911 ++ .../quic-go/quic-go/packet_unpacker.go | 226 + .../quic-go/quic-go/quicvarint/io.go | 68 + .../quic-go/quic-go/quicvarint/varint.go | 141 + .../quic-go/quic-go/receive_stream.go | 323 + .../quic-go/quic-go/retransmission_queue.go | 180 + .../github.com/quic-go/quic-go/send_conn.go | 103 + .../github.com/quic-go/quic-go/send_queue.go | 103 + .../github.com/quic-go/quic-go/send_stream.go | 494 + vendor/github.com/quic-go/quic-go/server.go | 895 ++ vendor/github.com/quic-go/quic-go/stream.go | 146 + .../github.com/quic-go/quic-go/streams_map.go | 318 + .../quic-go/quic-go/streams_map_incoming.go | 195 + .../quic-go/quic-go/streams_map_outgoing.go | 230 + vendor/github.com/quic-go/quic-go/sys_conn.go | 117 + .../quic-go/quic-go/sys_conn_buffers.go | 68 + .../quic-go/quic-go/sys_conn_buffers_write.go | 70 + .../github.com/quic-go/quic-go/sys_conn_df.go | 22 + .../quic-go/quic-go/sys_conn_df_darwin.go | 74 + .../quic-go/quic-go/sys_conn_df_linux.go | 42 + .../quic-go/quic-go/sys_conn_df_windows.go | 54 + .../quic-go/quic-go/sys_conn_helper_darwin.go | 36 + .../quic-go/sys_conn_helper_freebsd.go | 31 + .../quic-go/quic-go/sys_conn_helper_linux.go | 110 + .../quic-go/sys_conn_helper_nonlinux.go | 10 + .../quic-go/quic-go/sys_conn_no_oob.go | 21 + .../quic-go/quic-go/sys_conn_oob.go | 331 + .../quic-go/quic-go/sys_conn_windows.go | 42 + .../github.com/quic-go/quic-go/token_store.go | 116 + .../gomega/tools => quic-go/quic-go/tools.go} | 4 +- .../github.com/quic-go/quic-go/transport.go | 542 + .../quic-go/quic-go/window_update_queue.go | 71 + .../go-securesystemslib/LICENSE} | 4 +- .../cjson/canonicaljson.go | 151 + vendor/github.com/spf13/cobra/.golangci.yml | 2 +- vendor/github.com/spf13/cobra/Makefile | 8 +- vendor/github.com/spf13/cobra/README.md | 4 +- vendor/github.com/spf13/cobra/active_help.go | 2 +- vendor/github.com/spf13/cobra/args.go | 4 +- .../spf13/cobra/bash_completions.go | 4 +- .../spf13/cobra/bash_completionsV2.go | 71 +- vendor/github.com/spf13/cobra/cobra.go | 6 +- vendor/github.com/spf13/cobra/command.go | 75 +- .../github.com/spf13/cobra/command_notwin.go | 2 +- vendor/github.com/spf13/cobra/command_win.go | 2 +- vendor/github.com/spf13/cobra/completions.go | 13 +- .../spf13/cobra/fish_completions.go | 78 +- vendor/github.com/spf13/cobra/flag_groups.go | 2 +- .../spf13/cobra/powershell_completions.go | 27 +- .../spf13/cobra/projects_using_cobra.md | 6 +- .../spf13/cobra/shell_completions.go | 2 +- .../spf13/cobra/shell_completions.md | 30 +- vendor/github.com/spf13/cobra/user_guide.md | 46 +- .../github.com/spf13/cobra/zsh_completions.go | 17 +- .../testify/assert/assertion_compare.go | 36 +- .../testify/assert/assertion_format.go | 216 +- .../testify/assert/assertion_forward.go | 432 +- .../testify/assert/assertion_order.go | 24 +- .../stretchr/testify/assert/assertions.go | 384 +- .../github.com/stretchr/testify/assert/doc.go | 43 +- .../testify/assert/http_assertions.go | 12 +- .../stretchr/testify/require/doc.go | 23 +- .../stretchr/testify/require/require.go | 444 +- .../testify/require/require_forward.go | 432 +- .../tinylib/msgp/msgp/advise_linux.go | 3 +- .../tinylib/msgp/msgp/advise_other.go | 3 +- vendor/github.com/tinylib/msgp/msgp/defs.go | 27 +- vendor/github.com/tinylib/msgp/msgp/elsize.go | 167 +- .../tinylib/msgp/msgp/elsize_default.go | 21 + .../tinylib/msgp/msgp/elsize_tinygo.go | 13 + vendor/github.com/tinylib/msgp/msgp/errors.go | 91 +- .../tinylib/msgp/msgp/errors_default.go | 25 + .../tinylib/msgp/msgp/errors_tinygo.go | 42 + .../github.com/tinylib/msgp/msgp/extension.go | 15 +- vendor/github.com/tinylib/msgp/msgp/file.go | 3 +- .../github.com/tinylib/msgp/msgp/file_port.go | 3 +- vendor/github.com/tinylib/msgp/msgp/json.go | 4 +- .../tinylib/msgp/msgp/json_bytes.go | 22 - vendor/github.com/tinylib/msgp/msgp/number.go | 1 - vendor/github.com/tinylib/msgp/msgp/purego.go | 1 + vendor/github.com/tinylib/msgp/msgp/read.go | 20 +- .../tinylib/msgp/msgp/read_bytes.go | 262 +- vendor/github.com/tinylib/msgp/msgp/size.go | 7 +- vendor/github.com/tinylib/msgp/msgp/unsafe.go | 18 +- vendor/github.com/tinylib/msgp/msgp/write.go | 100 +- .../tinylib/msgp/msgp/write_bytes.go | 39 +- .../vishvananda/netns/.golangci.yml | 2 + vendor/github.com/vishvananda/netns/README.md | 1 + vendor/github.com/vishvananda/netns/doc.go | 9 + .../vishvananda/netns/netns_linux.go | 117 +- .../{netns_unspecified.go => netns_others.go} | 17 + .../netns/{netns.go => nshandle_linux.go} | 12 +- .../vishvananda/netns/nshandle_others.go | 45 + .../etcd/api/v3/etcdserverpb/rpc.pb.go | 516 +- .../etcd/api/v3/etcdserverpb/rpc.proto | 2 +- .../etcd/api/v3/v3rpc/rpctypes/error.go | 3 + .../go.etcd.io/etcd/api/v3/version/version.go | 2 +- .../etcd/client/pkg/v3/logutil/zap.go | 24 +- vendor/go.etcd.io/etcd/client/v2/doc.go | 1 - vendor/go.etcd.io/etcd/client/v3/client.go | 5 +- vendor/go.etcd.io/etcd/client/v3/doc.go | 4 +- .../client/v3/internal/endpoint/endpoint.go | 31 +- vendor/go.etcd.io/etcd/client/v3/lease.go | 4 +- .../go.etcd.io/etcd/client/v3/maintenance.go | 1 + .../etcd/client/v3/retry_interceptor.go | 31 +- vendor/go.etcd.io/etcd/client/v3/txn.go | 17 +- vendor/go.etcd.io/etcd/client/v3/watch.go | 52 +- vendor/go.uber.org/atomic/CHANGELOG.md | 27 + vendor/go.uber.org/atomic/bool.go | 11 +- vendor/go.uber.org/atomic/duration.go | 11 +- vendor/go.uber.org/atomic/error.go | 23 +- vendor/go.uber.org/atomic/error_ext.go | 4 +- vendor/go.uber.org/atomic/float32.go | 77 + vendor/go.uber.org/atomic/float32_ext.go | 76 + vendor/go.uber.org/atomic/float64.go | 2 +- vendor/go.uber.org/atomic/float64_ext.go | 35 +- vendor/go.uber.org/atomic/int32.go | 9 +- vendor/go.uber.org/atomic/int64.go | 9 +- vendor/go.uber.org/atomic/nocmp.go | 12 +- vendor/go.uber.org/atomic/pointer_go118.go | 31 + .../atomic/pointer_go118_pre119.go | 60 + vendor/go.uber.org/atomic/pointer_go119.go | 61 + vendor/go.uber.org/atomic/string.go | 30 +- vendor/go.uber.org/atomic/string_ext.go | 17 +- vendor/go.uber.org/atomic/time.go | 2 +- vendor/go.uber.org/atomic/uint32.go | 9 +- vendor/go.uber.org/atomic/uint64.go | 9 +- vendor/go.uber.org/atomic/uintptr.go | 9 +- vendor/go.uber.org/atomic/unsafe_pointer.go | 9 +- vendor/go.uber.org/atomic/value.go | 4 +- vendor/go.uber.org/mock/AUTHORS | 12 + vendor/go.uber.org/mock/LICENSE | 202 + .../go.uber.org/mock/mockgen/generic_go118.go | 130 + .../mock/mockgen/generic_notgo118.go | 41 + vendor/go.uber.org/mock/mockgen/mockgen.go | 883 ++ .../go.uber.org/mock/mockgen/model/model.go | 533 + vendor/go.uber.org/mock/mockgen/parse.go | 805 ++ vendor/go.uber.org/mock/mockgen/reflect.go | 256 + vendor/go.uber.org/mock/mockgen/version.go | 31 + vendor/go.uber.org/multierr/.travis.yml | 23 - vendor/go.uber.org/multierr/CHANGELOG.md | 35 + vendor/go.uber.org/multierr/LICENSE.txt | 2 +- vendor/go.uber.org/multierr/Makefile | 6 +- vendor/go.uber.org/multierr/README.md | 30 +- vendor/go.uber.org/multierr/error.go | 415 +- .../go.uber.org/multierr/error_post_go120.go | 48 + .../multierr/{go113.go => error_pre_go120.go} | 31 +- vendor/go.uber.org/multierr/glide.yaml | 8 - vendor/golang.org/x/crypto/LICENSE | 27 + vendor/golang.org/x/crypto/PATENTS | 22 + .../x/crypto/chacha20/chacha_arm64.go | 16 + .../x/crypto/chacha20/chacha_arm64.s | 307 + .../x/crypto/chacha20/chacha_generic.go | 398 + .../x/crypto/chacha20/chacha_noasm.go | 13 + .../x/crypto/chacha20/chacha_ppc64le.go | 16 + .../x/crypto/chacha20/chacha_ppc64le.s | 443 + .../x/crypto/chacha20/chacha_s390x.go | 27 + .../x/crypto/chacha20/chacha_s390x.s | 224 + vendor/golang.org/x/crypto/chacha20/xor.go | 42 + .../chacha20poly1305/chacha20poly1305.go | 98 + .../chacha20poly1305_amd64.go | 86 + .../chacha20poly1305/chacha20poly1305_amd64.s | 2715 ++++ .../chacha20poly1305_generic.go | 81 + .../chacha20poly1305_noasm.go | 15 + .../chacha20poly1305/xchacha20poly1305.go | 86 + vendor/golang.org/x/crypto/hkdf/hkdf.go | 95 + .../x/crypto/internal/alias/alias.go | 31 + .../x/crypto/internal/alias/alias_purego.go | 34 + .../x/crypto/internal/poly1305/mac_noasm.go | 9 + .../x/crypto/internal/poly1305/poly1305.go | 99 + .../x/crypto/internal/poly1305/sum_amd64.go | 47 + .../x/crypto/internal/poly1305/sum_amd64.s | 108 + .../x/crypto/internal/poly1305/sum_generic.go | 312 + .../x/crypto/internal/poly1305/sum_ppc64le.go | 47 + .../x/crypto/internal/poly1305/sum_ppc64le.s | 179 + .../x/crypto/internal/poly1305/sum_s390x.go | 76 + .../x/crypto/internal/poly1305/sum_s390x.s | 503 + vendor/golang.org/x/exp/LICENSE | 27 + vendor/golang.org/x/exp/PATENTS | 22 + vendor/golang.org/x/exp/rand/exp.go | 221 + vendor/golang.org/x/exp/rand/normal.go | 156 + vendor/golang.org/x/exp/rand/rand.go | 372 + vendor/golang.org/x/exp/rand/rng.go | 91 + vendor/golang.org/x/exp/rand/zipf.go | 77 + .../x/mod/internal/lazyregexp/lazyre.go | 78 + vendor/golang.org/x/mod/modfile/print.go | 184 + vendor/golang.org/x/mod/modfile/read.go | 958 ++ vendor/golang.org/x/mod/modfile/rule.go | 1663 +++ vendor/golang.org/x/mod/modfile/work.go | 285 + vendor/golang.org/x/mod/module/module.go | 841 ++ vendor/golang.org/x/mod/module/pseudo.go | 250 + vendor/golang.org/x/mod/semver/semver.go | 6 +- vendor/golang.org/x/net/context/go17.go | 1 - vendor/golang.org/x/net/context/go19.go | 1 - vendor/golang.org/x/net/context/pre_go17.go | 1 - vendor/golang.org/x/net/context/pre_go19.go | 1 - vendor/golang.org/x/net/html/token.go | 12 +- vendor/golang.org/x/net/http2/databuffer.go | 59 +- vendor/golang.org/x/net/http2/frame.go | 42 +- vendor/golang.org/x/net/http2/go111.go | 30 - vendor/golang.org/x/net/http2/go115.go | 27 - vendor/golang.org/x/net/http2/go118.go | 17 - vendor/golang.org/x/net/http2/not_go111.go | 21 - vendor/golang.org/x/net/http2/not_go115.go | 31 - vendor/golang.org/x/net/http2/not_go118.go | 17 - vendor/golang.org/x/net/http2/pipe.go | 11 +- vendor/golang.org/x/net/http2/server.go | 37 +- vendor/golang.org/x/net/http2/testsync.go | 331 + vendor/golang.org/x/net/http2/transport.go | 340 +- vendor/golang.org/x/net/idna/go118.go | 1 - vendor/golang.org/x/net/idna/idna10.0.0.go | 1 - vendor/golang.org/x/net/idna/idna9.0.0.go | 1 - vendor/golang.org/x/net/idna/pre_go118.go | 1 - vendor/golang.org/x/net/idna/tables10.0.0.go | 1 - vendor/golang.org/x/net/idna/tables11.0.0.go | 1 - vendor/golang.org/x/net/idna/tables12.0.0.go | 1 - vendor/golang.org/x/net/idna/tables13.0.0.go | 1 - vendor/golang.org/x/net/idna/tables15.0.0.go | 1 - vendor/golang.org/x/net/idna/tables9.0.0.go | 1 - vendor/golang.org/x/net/idna/trie12.0.0.go | 1 - vendor/golang.org/x/net/idna/trie13.0.0.go | 1 - .../x/net/internal/socket/cmsghdr.go | 1 - .../x/net/internal/socket/cmsghdr_bsd.go | 1 - .../internal/socket/cmsghdr_linux_32bit.go | 2 - .../internal/socket/cmsghdr_linux_64bit.go | 2 - .../internal/socket/cmsghdr_solaris_64bit.go | 1 - .../x/net/internal/socket/cmsghdr_stub.go | 1 - .../x/net/internal/socket/cmsghdr_unix.go | 1 - .../net/internal/socket/complete_dontwait.go | 1 - .../internal/socket/complete_nodontwait.go | 1 - .../golang.org/x/net/internal/socket/empty.s | 1 - .../x/net/internal/socket/error_unix.go | 1 - .../x/net/internal/socket/iovec_32bit.go | 2 - .../x/net/internal/socket/iovec_64bit.go | 2 - .../internal/socket/iovec_solaris_64bit.go | 1 - .../x/net/internal/socket/iovec_stub.go | 1 - .../x/net/internal/socket/mmsghdr_stub.go | 1 - .../x/net/internal/socket/mmsghdr_unix.go | 1 - .../x/net/internal/socket/msghdr_bsd.go | 1 - .../x/net/internal/socket/msghdr_bsdvar.go | 1 - .../net/internal/socket/msghdr_linux_32bit.go | 2 - .../net/internal/socket/msghdr_linux_64bit.go | 2 - .../internal/socket/msghdr_solaris_64bit.go | 1 - .../x/net/internal/socket/msghdr_stub.go | 1 - .../x/net/internal/socket/msghdr_zos_s390x.go | 1 - .../x/net/internal/socket/norace.go | 1 - .../golang.org/x/net/internal/socket/race.go | 1 - .../x/net/internal/socket/rawconn_mmsg.go | 1 - .../x/net/internal/socket/rawconn_msg.go | 1 - .../x/net/internal/socket/rawconn_nommsg.go | 1 - .../x/net/internal/socket/rawconn_nomsg.go | 1 - .../x/net/internal/socket/sys_bsd.go | 1 - .../x/net/internal/socket/sys_const_unix.go | 1 - .../x/net/internal/socket/sys_linux.go | 1 - .../net/internal/socket/sys_linux_loong64.go | 1 - .../net/internal/socket/sys_linux_riscv64.go | 1 - .../x/net/internal/socket/sys_posix.go | 1 - .../x/net/internal/socket/sys_stub.go | 1 - .../x/net/internal/socket/sys_unix.go | 1 - .../x/net/internal/socket/zsys_aix_ppc64.go | 1 - .../net/internal/socket/zsys_linux_loong64.go | 1 - .../net/internal/socket/zsys_linux_riscv64.go | 1 - vendor/golang.org/x/net/ipv4/control_bsd.go | 1 - .../golang.org/x/net/ipv4/control_pktinfo.go | 1 - vendor/golang.org/x/net/ipv4/control_stub.go | 1 - vendor/golang.org/x/net/ipv4/control_unix.go | 1 - vendor/golang.org/x/net/ipv4/icmp_stub.go | 1 - vendor/golang.org/x/net/ipv4/payload_cmsg.go | 1 - .../golang.org/x/net/ipv4/payload_nocmsg.go | 1 - vendor/golang.org/x/net/ipv4/sockopt_posix.go | 1 - vendor/golang.org/x/net/ipv4/sockopt_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_aix.go | 1 - vendor/golang.org/x/net/ipv4/sys_asmreq.go | 1 - .../golang.org/x/net/ipv4/sys_asmreq_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_asmreqn.go | 1 - .../golang.org/x/net/ipv4/sys_asmreqn_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_bpf.go | 1 - vendor/golang.org/x/net/ipv4/sys_bpf_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_bsd.go | 1 - vendor/golang.org/x/net/ipv4/sys_ssmreq.go | 1 - .../golang.org/x/net/ipv4/sys_ssmreq_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_stub.go | 1 - .../golang.org/x/net/ipv4/zsys_aix_ppc64.go | 1 - .../x/net/ipv4/zsys_linux_loong64.go | 1 - .../x/net/ipv4/zsys_linux_riscv64.go | 1 - .../x/net/ipv6/control_rfc2292_unix.go | 1 - .../x/net/ipv6/control_rfc3542_unix.go | 1 - vendor/golang.org/x/net/ipv6/control_stub.go | 1 - vendor/golang.org/x/net/ipv6/control_unix.go | 1 - vendor/golang.org/x/net/ipv6/icmp_bsd.go | 1 - vendor/golang.org/x/net/ipv6/icmp_stub.go | 1 - vendor/golang.org/x/net/ipv6/payload_cmsg.go | 1 - .../golang.org/x/net/ipv6/payload_nocmsg.go | 1 - vendor/golang.org/x/net/ipv6/sockopt_posix.go | 1 - vendor/golang.org/x/net/ipv6/sockopt_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_aix.go | 1 - vendor/golang.org/x/net/ipv6/sys_asmreq.go | 1 - .../golang.org/x/net/ipv6/sys_asmreq_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_bpf.go | 1 - vendor/golang.org/x/net/ipv6/sys_bpf_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_bsd.go | 1 - vendor/golang.org/x/net/ipv6/sys_ssmreq.go | 1 - .../golang.org/x/net/ipv6/sys_ssmreq_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_stub.go | 1 - .../golang.org/x/net/ipv6/zsys_aix_ppc64.go | 1 - .../x/net/ipv6/zsys_linux_loong64.go | 1 - .../x/net/ipv6/zsys_linux_riscv64.go | 1 - vendor/golang.org/x/oauth2/deviceauth.go | 198 + .../x/oauth2/internal/client_appengine.go | 1 - vendor/golang.org/x/oauth2/internal/oauth2.go | 2 +- vendor/golang.org/x/oauth2/internal/token.go | 130 +- vendor/golang.org/x/oauth2/oauth2.go | 33 +- vendor/golang.org/x/oauth2/pkce.go | 68 + vendor/golang.org/x/oauth2/token.go | 21 +- vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s | 17 + vendor/golang.org/x/sys/cpu/byteorder.go | 66 + vendor/golang.org/x/sys/cpu/cpu.go | 290 + vendor/golang.org/x/sys/cpu/cpu_aix.go | 33 + vendor/golang.org/x/sys/cpu/cpu_arm.go | 73 + vendor/golang.org/x/sys/cpu/cpu_arm64.go | 172 + vendor/golang.org/x/sys/cpu/cpu_arm64.s | 31 + vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go | 11 + vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go | 21 + vendor/golang.org/x/sys/cpu/cpu_gc_x86.go | 15 + .../golang.org/x/sys/cpu/cpu_gccgo_arm64.go | 11 + .../golang.org/x/sys/cpu/cpu_gccgo_s390x.go | 22 + vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c | 37 + vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go | 31 + vendor/golang.org/x/sys/cpu/cpu_linux.go | 15 + vendor/golang.org/x/sys/cpu/cpu_linux_arm.go | 39 + .../golang.org/x/sys/cpu/cpu_linux_arm64.go | 111 + .../golang.org/x/sys/cpu/cpu_linux_mips64x.go | 22 + .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 9 + .../golang.org/x/sys/cpu/cpu_linux_ppc64x.go | 30 + .../golang.org/x/sys/cpu/cpu_linux_s390x.go | 40 + vendor/golang.org/x/sys/cpu/cpu_loong64.go | 12 + vendor/golang.org/x/sys/cpu/cpu_mips64x.go | 15 + vendor/golang.org/x/sys/cpu/cpu_mipsx.go | 11 + .../golang.org/x/sys/cpu/cpu_netbsd_arm64.go | 173 + .../golang.org/x/sys/cpu/cpu_openbsd_arm64.go | 65 + .../golang.org/x/sys/cpu/cpu_openbsd_arm64.s | 11 + vendor/golang.org/x/sys/cpu/cpu_other_arm.go | 9 + .../golang.org/x/sys/cpu/cpu_other_arm64.go | 9 + .../golang.org/x/sys/cpu/cpu_other_mips64x.go | 11 + .../golang.org/x/sys/cpu/cpu_other_ppc64x.go | 12 + .../golang.org/x/sys/cpu/cpu_other_riscv64.go | 11 + vendor/golang.org/x/sys/cpu/cpu_ppc64x.go | 16 + vendor/golang.org/x/sys/cpu/cpu_riscv64.go | 11 + vendor/golang.org/x/sys/cpu/cpu_s390x.go | 172 + vendor/golang.org/x/sys/cpu/cpu_s390x.s | 57 + vendor/golang.org/x/sys/cpu/cpu_wasm.go | 17 + vendor/golang.org/x/sys/cpu/cpu_x86.go | 151 + vendor/golang.org/x/sys/cpu/cpu_x86.s | 26 + vendor/golang.org/x/sys/cpu/cpu_zos.go | 10 + vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go | 25 + vendor/golang.org/x/sys/cpu/endian_big.go | 10 + vendor/golang.org/x/sys/cpu/endian_little.go | 10 + vendor/golang.org/x/sys/cpu/hwcap_linux.go | 71 + vendor/golang.org/x/sys/cpu/parse.go | 43 + .../x/sys/cpu/proc_cpuinfo_linux.go | 53 + vendor/golang.org/x/sys/cpu/runtime_auxv.go | 16 + .../x/sys/cpu/runtime_auxv_go121.go | 18 + .../golang.org/x/sys/cpu/syscall_aix_gccgo.go | 26 + .../x/sys/cpu/syscall_aix_ppc64_gc.go | 35 + vendor/golang.org/x/sys/execabs/execabs.go | 102 - .../golang.org/x/sys/execabs/execabs_go118.go | 18 - .../golang.org/x/sys/execabs/execabs_go119.go | 21 - .../golang.org/x/sys/plan9/pwd_go15_plan9.go | 1 - vendor/golang.org/x/sys/plan9/pwd_plan9.go | 1 - vendor/golang.org/x/sys/plan9/race.go | 1 - vendor/golang.org/x/sys/plan9/race0.go | 1 - vendor/golang.org/x/sys/plan9/str.go | 1 - vendor/golang.org/x/sys/plan9/syscall.go | 1 - .../x/sys/plan9/zsyscall_plan9_386.go | 1 - .../x/sys/plan9/zsyscall_plan9_amd64.go | 1 - .../x/sys/plan9/zsyscall_plan9_arm.go | 1 - vendor/golang.org/x/sys/unix/aliases.go | 4 +- vendor/golang.org/x/sys/unix/asm_aix_ppc64.s | 1 - vendor/golang.org/x/sys/unix/asm_bsd_386.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_amd64.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_arm.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_arm64.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s | 2 - .../golang.org/x/sys/unix/asm_bsd_riscv64.s | 2 - vendor/golang.org/x/sys/unix/asm_linux_386.s | 1 - .../golang.org/x/sys/unix/asm_linux_amd64.s | 1 - vendor/golang.org/x/sys/unix/asm_linux_arm.s | 1 - .../golang.org/x/sys/unix/asm_linux_arm64.s | 3 - .../golang.org/x/sys/unix/asm_linux_loong64.s | 3 - .../golang.org/x/sys/unix/asm_linux_mips64x.s | 3 - .../golang.org/x/sys/unix/asm_linux_mipsx.s | 3 - .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 3 - .../golang.org/x/sys/unix/asm_linux_riscv64.s | 2 - .../golang.org/x/sys/unix/asm_linux_s390x.s | 3 - .../x/sys/unix/asm_openbsd_mips64.s | 1 - .../golang.org/x/sys/unix/asm_solaris_amd64.s | 1 - vendor/golang.org/x/sys/unix/asm_zos_s390x.s | 3 - vendor/golang.org/x/sys/unix/cap_freebsd.go | 1 - vendor/golang.org/x/sys/unix/constants.go | 1 - vendor/golang.org/x/sys/unix/dev_aix_ppc.go | 1 - vendor/golang.org/x/sys/unix/dev_aix_ppc64.go | 1 - vendor/golang.org/x/sys/unix/dev_zos.go | 1 - vendor/golang.org/x/sys/unix/dirent.go | 1 - vendor/golang.org/x/sys/unix/endian_big.go | 1 - vendor/golang.org/x/sys/unix/endian_little.go | 1 - vendor/golang.org/x/sys/unix/env_unix.go | 1 - vendor/golang.org/x/sys/unix/epoll_zos.go | 1 - vendor/golang.org/x/sys/unix/fcntl.go | 3 +- .../x/sys/unix/fcntl_linux_32bit.go | 1 - vendor/golang.org/x/sys/unix/fdset.go | 1 - vendor/golang.org/x/sys/unix/fstatfs_zos.go | 1 - vendor/golang.org/x/sys/unix/gccgo.go | 1 - vendor/golang.org/x/sys/unix/gccgo_c.c | 1 - .../x/sys/unix/gccgo_linux_amd64.go | 1 - vendor/golang.org/x/sys/unix/ifreq_linux.go | 1 - vendor/golang.org/x/sys/unix/ioctl_linux.go | 5 + vendor/golang.org/x/sys/unix/ioctl_signed.go | 1 - .../golang.org/x/sys/unix/ioctl_unsigned.go | 1 - vendor/golang.org/x/sys/unix/ioctl_zos.go | 1 - vendor/golang.org/x/sys/unix/mkerrors.sh | 43 +- vendor/golang.org/x/sys/unix/mmap_nomremap.go | 3 +- vendor/golang.org/x/sys/unix/mremap.go | 1 - vendor/golang.org/x/sys/unix/pagesize_unix.go | 1 - .../golang.org/x/sys/unix/pledge_openbsd.go | 92 +- vendor/golang.org/x/sys/unix/ptrace_darwin.go | 1 - vendor/golang.org/x/sys/unix/ptrace_ios.go | 1 - vendor/golang.org/x/sys/unix/race.go | 1 - vendor/golang.org/x/sys/unix/race0.go | 1 - .../x/sys/unix/readdirent_getdents.go | 1 - .../x/sys/unix/readdirent_getdirentries.go | 1 - vendor/golang.org/x/sys/unix/sockcmsg_unix.go | 1 - .../x/sys/unix/sockcmsg_unix_other.go | 1 - vendor/golang.org/x/sys/unix/syscall.go | 1 - vendor/golang.org/x/sys/unix/syscall_aix.go | 4 +- .../golang.org/x/sys/unix/syscall_aix_ppc.go | 1 - .../x/sys/unix/syscall_aix_ppc64.go | 1 - vendor/golang.org/x/sys/unix/syscall_bsd.go | 3 +- .../x/sys/unix/syscall_darwin_amd64.go | 1 - .../x/sys/unix/syscall_darwin_arm64.go | 1 - .../x/sys/unix/syscall_darwin_libSystem.go | 3 +- .../x/sys/unix/syscall_dragonfly_amd64.go | 1 - .../golang.org/x/sys/unix/syscall_freebsd.go | 12 +- .../x/sys/unix/syscall_freebsd_386.go | 1 - .../x/sys/unix/syscall_freebsd_amd64.go | 1 - .../x/sys/unix/syscall_freebsd_arm.go | 1 - .../x/sys/unix/syscall_freebsd_arm64.go | 1 - .../x/sys/unix/syscall_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/syscall_hurd.go | 1 - .../golang.org/x/sys/unix/syscall_hurd_386.go | 1 - .../golang.org/x/sys/unix/syscall_illumos.go | 1 - vendor/golang.org/x/sys/unix/syscall_linux.go | 132 +- .../x/sys/unix/syscall_linux_386.go | 1 - .../x/sys/unix/syscall_linux_alarm.go | 2 - .../x/sys/unix/syscall_linux_amd64.go | 1 - .../x/sys/unix/syscall_linux_amd64_gc.go | 1 - .../x/sys/unix/syscall_linux_arm.go | 1 - .../x/sys/unix/syscall_linux_arm64.go | 1 - .../golang.org/x/sys/unix/syscall_linux_gc.go | 1 - .../x/sys/unix/syscall_linux_gc_386.go | 1 - .../x/sys/unix/syscall_linux_gc_arm.go | 1 - .../x/sys/unix/syscall_linux_gccgo_386.go | 1 - .../x/sys/unix/syscall_linux_gccgo_arm.go | 1 - .../x/sys/unix/syscall_linux_loong64.go | 1 - .../x/sys/unix/syscall_linux_mips64x.go | 2 - .../x/sys/unix/syscall_linux_mipsx.go | 2 - .../x/sys/unix/syscall_linux_ppc.go | 1 - .../x/sys/unix/syscall_linux_ppc64x.go | 2 - .../x/sys/unix/syscall_linux_riscv64.go | 1 - .../x/sys/unix/syscall_linux_s390x.go | 1 - .../x/sys/unix/syscall_linux_sparc64.go | 1 - .../x/sys/unix/syscall_netbsd_386.go | 1 - .../x/sys/unix/syscall_netbsd_amd64.go | 1 - .../x/sys/unix/syscall_netbsd_arm.go | 1 - .../x/sys/unix/syscall_netbsd_arm64.go | 1 - .../golang.org/x/sys/unix/syscall_openbsd.go | 28 +- .../x/sys/unix/syscall_openbsd_386.go | 1 - .../x/sys/unix/syscall_openbsd_amd64.go | 1 - .../x/sys/unix/syscall_openbsd_arm.go | 1 - .../x/sys/unix/syscall_openbsd_arm64.go | 1 - .../x/sys/unix/syscall_openbsd_libc.go | 1 - .../x/sys/unix/syscall_openbsd_ppc64.go | 1 - .../x/sys/unix/syscall_openbsd_riscv64.go | 1 - .../golang.org/x/sys/unix/syscall_solaris.go | 5 +- .../x/sys/unix/syscall_solaris_amd64.go | 1 - vendor/golang.org/x/sys/unix/syscall_unix.go | 1 - .../golang.org/x/sys/unix/syscall_unix_gc.go | 2 - .../x/sys/unix/syscall_unix_gc_ppc64x.go | 3 - .../x/sys/unix/syscall_zos_s390x.go | 11 +- vendor/golang.org/x/sys/unix/sysvshm_linux.go | 1 - vendor/golang.org/x/sys/unix/sysvshm_unix.go | 1 - .../x/sys/unix/sysvshm_unix_other.go | 1 - vendor/golang.org/x/sys/unix/timestruct.go | 1 - .../golang.org/x/sys/unix/unveil_openbsd.go | 41 +- vendor/golang.org/x/sys/unix/xattr_bsd.go | 1 - .../golang.org/x/sys/unix/zerrors_aix_ppc.go | 1 - .../x/sys/unix/zerrors_aix_ppc64.go | 1 - .../x/sys/unix/zerrors_darwin_amd64.go | 1 - .../x/sys/unix/zerrors_darwin_arm64.go | 1 - .../x/sys/unix/zerrors_dragonfly_amd64.go | 1 - .../x/sys/unix/zerrors_freebsd_386.go | 1 - .../x/sys/unix/zerrors_freebsd_amd64.go | 1 - .../x/sys/unix/zerrors_freebsd_arm.go | 1 - .../x/sys/unix/zerrors_freebsd_arm64.go | 1 - .../x/sys/unix/zerrors_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/zerrors_linux.go | 104 +- .../x/sys/unix/zerrors_linux_386.go | 4 +- .../x/sys/unix/zerrors_linux_amd64.go | 4 +- .../x/sys/unix/zerrors_linux_arm.go | 4 +- .../x/sys/unix/zerrors_linux_arm64.go | 4 +- .../x/sys/unix/zerrors_linux_loong64.go | 5 +- .../x/sys/unix/zerrors_linux_mips.go | 4 +- .../x/sys/unix/zerrors_linux_mips64.go | 4 +- .../x/sys/unix/zerrors_linux_mips64le.go | 4 +- .../x/sys/unix/zerrors_linux_mipsle.go | 4 +- .../x/sys/unix/zerrors_linux_ppc.go | 4 +- .../x/sys/unix/zerrors_linux_ppc64.go | 4 +- .../x/sys/unix/zerrors_linux_ppc64le.go | 4 +- .../x/sys/unix/zerrors_linux_riscv64.go | 7 +- .../x/sys/unix/zerrors_linux_s390x.go | 4 +- .../x/sys/unix/zerrors_linux_sparc64.go | 4 +- .../x/sys/unix/zerrors_netbsd_386.go | 1 - .../x/sys/unix/zerrors_netbsd_amd64.go | 1 - .../x/sys/unix/zerrors_netbsd_arm.go | 1 - .../x/sys/unix/zerrors_netbsd_arm64.go | 1 - .../x/sys/unix/zerrors_openbsd_386.go | 1 - .../x/sys/unix/zerrors_openbsd_amd64.go | 1 - .../x/sys/unix/zerrors_openbsd_arm.go | 1 - .../x/sys/unix/zerrors_openbsd_arm64.go | 1 - .../x/sys/unix/zerrors_openbsd_mips64.go | 1 - .../x/sys/unix/zerrors_openbsd_ppc64.go | 1 - .../x/sys/unix/zerrors_openbsd_riscv64.go | 1 - .../x/sys/unix/zerrors_solaris_amd64.go | 1 - .../x/sys/unix/zerrors_zos_s390x.go | 1 - .../x/sys/unix/zptrace_armnn_linux.go | 2 - .../x/sys/unix/zptrace_mipsnn_linux.go | 2 - .../x/sys/unix/zptrace_mipsnnle_linux.go | 2 - .../x/sys/unix/zptrace_x86_linux.go | 2 - .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64_gc.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64_gccgo.go | 1 - .../x/sys/unix/zsyscall_darwin_amd64.go | 1 - .../x/sys/unix/zsyscall_darwin_arm64.go | 1 - .../x/sys/unix/zsyscall_dragonfly_amd64.go | 1 - .../x/sys/unix/zsyscall_freebsd_386.go | 1 - .../x/sys/unix/zsyscall_freebsd_amd64.go | 1 - .../x/sys/unix/zsyscall_freebsd_arm.go | 1 - .../x/sys/unix/zsyscall_freebsd_arm64.go | 1 - .../x/sys/unix/zsyscall_freebsd_riscv64.go | 1 - .../x/sys/unix/zsyscall_illumos_amd64.go | 1 - .../golang.org/x/sys/unix/zsyscall_linux.go | 36 +- .../x/sys/unix/zsyscall_linux_386.go | 1 - .../x/sys/unix/zsyscall_linux_amd64.go | 1 - .../x/sys/unix/zsyscall_linux_arm.go | 1 - .../x/sys/unix/zsyscall_linux_arm64.go | 1 - .../x/sys/unix/zsyscall_linux_loong64.go | 1 - .../x/sys/unix/zsyscall_linux_mips.go | 1 - .../x/sys/unix/zsyscall_linux_mips64.go | 1 - .../x/sys/unix/zsyscall_linux_mips64le.go | 1 - .../x/sys/unix/zsyscall_linux_mipsle.go | 1 - .../x/sys/unix/zsyscall_linux_ppc.go | 1 - .../x/sys/unix/zsyscall_linux_ppc64.go | 1 - .../x/sys/unix/zsyscall_linux_ppc64le.go | 1 - .../x/sys/unix/zsyscall_linux_riscv64.go | 1 - .../x/sys/unix/zsyscall_linux_s390x.go | 1 - .../x/sys/unix/zsyscall_linux_sparc64.go | 1 - .../x/sys/unix/zsyscall_netbsd_386.go | 1 - .../x/sys/unix/zsyscall_netbsd_amd64.go | 1 - .../x/sys/unix/zsyscall_netbsd_arm.go | 1 - .../x/sys/unix/zsyscall_netbsd_arm64.go | 1 - .../x/sys/unix/zsyscall_openbsd_386.go | 70 +- .../x/sys/unix/zsyscall_openbsd_386.s | 20 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 70 +- .../x/sys/unix/zsyscall_openbsd_amd64.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm.go | 70 +- .../x/sys/unix/zsyscall_openbsd_arm.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 70 +- .../x/sys/unix/zsyscall_openbsd_arm64.s | 20 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 70 +- .../x/sys/unix/zsyscall_openbsd_mips64.s | 20 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 70 +- .../x/sys/unix/zsyscall_openbsd_ppc64.s | 24 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 70 +- .../x/sys/unix/zsyscall_openbsd_riscv64.s | 20 + .../x/sys/unix/zsyscall_solaris_amd64.go | 1 - .../x/sys/unix/zsyscall_zos_s390x.go | 1 - .../x/sys/unix/zsysctl_openbsd_386.go | 1 - .../x/sys/unix/zsysctl_openbsd_amd64.go | 1 - .../x/sys/unix/zsysctl_openbsd_arm.go | 1 - .../x/sys/unix/zsysctl_openbsd_arm64.go | 1 - .../x/sys/unix/zsysctl_openbsd_mips64.go | 1 - .../x/sys/unix/zsysctl_openbsd_ppc64.go | 1 - .../x/sys/unix/zsysctl_openbsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_darwin_amd64.go | 1 - .../x/sys/unix/zsysnum_darwin_arm64.go | 1 - .../x/sys/unix/zsysnum_dragonfly_amd64.go | 1 - .../x/sys/unix/zsysnum_freebsd_386.go | 1 - .../x/sys/unix/zsysnum_freebsd_amd64.go | 1 - .../x/sys/unix/zsysnum_freebsd_arm.go | 1 - .../x/sys/unix/zsysnum_freebsd_arm64.go | 1 - .../x/sys/unix/zsysnum_freebsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_linux_386.go | 6 +- .../x/sys/unix/zsysnum_linux_amd64.go | 6 +- .../x/sys/unix/zsysnum_linux_arm.go | 6 +- .../x/sys/unix/zsysnum_linux_arm64.go | 6 +- .../x/sys/unix/zsysnum_linux_loong64.go | 6 +- .../x/sys/unix/zsysnum_linux_mips.go | 6 +- .../x/sys/unix/zsysnum_linux_mips64.go | 6 +- .../x/sys/unix/zsysnum_linux_mips64le.go | 6 +- .../x/sys/unix/zsysnum_linux_mipsle.go | 6 +- .../x/sys/unix/zsysnum_linux_ppc.go | 6 +- .../x/sys/unix/zsysnum_linux_ppc64.go | 6 +- .../x/sys/unix/zsysnum_linux_ppc64le.go | 6 +- .../x/sys/unix/zsysnum_linux_riscv64.go | 6 +- .../x/sys/unix/zsysnum_linux_s390x.go | 6 +- .../x/sys/unix/zsysnum_linux_sparc64.go | 6 +- .../x/sys/unix/zsysnum_netbsd_386.go | 1 - .../x/sys/unix/zsysnum_netbsd_amd64.go | 1 - .../x/sys/unix/zsysnum_netbsd_arm.go | 1 - .../x/sys/unix/zsysnum_netbsd_arm64.go | 1 - .../x/sys/unix/zsysnum_openbsd_386.go | 1 - .../x/sys/unix/zsysnum_openbsd_amd64.go | 1 - .../x/sys/unix/zsysnum_openbsd_arm.go | 1 - .../x/sys/unix/zsysnum_openbsd_arm64.go | 1 - .../x/sys/unix/zsysnum_openbsd_mips64.go | 1 - .../x/sys/unix/zsysnum_openbsd_ppc64.go | 1 - .../x/sys/unix/zsysnum_openbsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_zos_s390x.go | 1 - .../golang.org/x/sys/unix/ztypes_aix_ppc.go | 1 - .../golang.org/x/sys/unix/ztypes_aix_ppc64.go | 1 - .../x/sys/unix/ztypes_darwin_amd64.go | 1 - .../x/sys/unix/ztypes_darwin_arm64.go | 1 - .../x/sys/unix/ztypes_dragonfly_amd64.go | 1 - .../x/sys/unix/ztypes_freebsd_386.go | 1 - .../x/sys/unix/ztypes_freebsd_amd64.go | 1 - .../x/sys/unix/ztypes_freebsd_arm.go | 1 - .../x/sys/unix/ztypes_freebsd_arm64.go | 1 - .../x/sys/unix/ztypes_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/ztypes_linux.go | 230 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 1 - .../x/sys/unix/ztypes_linux_amd64.go | 1 - .../golang.org/x/sys/unix/ztypes_linux_arm.go | 1 - .../x/sys/unix/ztypes_linux_arm64.go | 1 - .../x/sys/unix/ztypes_linux_loong64.go | 1 - .../x/sys/unix/ztypes_linux_mips.go | 1 - .../x/sys/unix/ztypes_linux_mips64.go | 1 - .../x/sys/unix/ztypes_linux_mips64le.go | 1 - .../x/sys/unix/ztypes_linux_mipsle.go | 1 - .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 1 - .../x/sys/unix/ztypes_linux_ppc64.go | 1 - .../x/sys/unix/ztypes_linux_ppc64le.go | 1 - .../x/sys/unix/ztypes_linux_riscv64.go | 1 - .../x/sys/unix/ztypes_linux_s390x.go | 1 - .../x/sys/unix/ztypes_linux_sparc64.go | 1 - .../x/sys/unix/ztypes_netbsd_386.go | 1 - .../x/sys/unix/ztypes_netbsd_amd64.go | 1 - .../x/sys/unix/ztypes_netbsd_arm.go | 1 - .../x/sys/unix/ztypes_netbsd_arm64.go | 1 - .../x/sys/unix/ztypes_openbsd_386.go | 1 - .../x/sys/unix/ztypes_openbsd_amd64.go | 1 - .../x/sys/unix/ztypes_openbsd_arm.go | 1 - .../x/sys/unix/ztypes_openbsd_arm64.go | 1 - .../x/sys/unix/ztypes_openbsd_mips64.go | 1 - .../x/sys/unix/ztypes_openbsd_ppc64.go | 1 - .../x/sys/unix/ztypes_openbsd_riscv64.go | 1 - .../x/sys/unix/ztypes_solaris_amd64.go | 1 - .../golang.org/x/sys/unix/ztypes_zos_s390x.go | 1 - vendor/golang.org/x/sys/windows/aliases.go | 1 - vendor/golang.org/x/sys/windows/empty.s | 1 - .../golang.org/x/sys/windows/env_windows.go | 17 +- vendor/golang.org/x/sys/windows/eventlog.go | 1 - vendor/golang.org/x/sys/windows/mksyscall.go | 1 - vendor/golang.org/x/sys/windows/race.go | 1 - vendor/golang.org/x/sys/windows/race0.go | 1 - .../golang.org/x/sys/windows/registry/key.go | 1 - .../x/sys/windows/registry/mksyscall.go | 1 - .../x/sys/windows/registry/syscall.go | 1 - .../x/sys/windows/registry/value.go | 1 - vendor/golang.org/x/sys/windows/service.go | 1 - vendor/golang.org/x/sys/windows/str.go | 1 - vendor/golang.org/x/sys/windows/syscall.go | 1 - .../x/sys/windows/syscall_windows.go | 92 +- .../golang.org/x/sys/windows/types_windows.go | 52 +- .../x/sys/windows/zsyscall_windows.go | 145 + vendor/golang.org/x/term/term_unix.go | 1 - vendor/golang.org/x/term/term_unix_bsd.go | 1 - vendor/golang.org/x/term/term_unix_other.go | 1 - vendor/golang.org/x/term/term_unsupported.go | 1 - .../x/text/secure/bidirule/bidirule10.0.0.go | 1 - .../x/text/secure/bidirule/bidirule9.0.0.go | 1 - .../x/text/unicode/bidi/tables10.0.0.go | 1 - .../x/text/unicode/bidi/tables11.0.0.go | 1 - .../x/text/unicode/bidi/tables12.0.0.go | 1 - .../x/text/unicode/bidi/tables13.0.0.go | 1 - .../x/text/unicode/bidi/tables15.0.0.go | 1 - .../x/text/unicode/bidi/tables9.0.0.go | 1 - .../x/text/unicode/norm/tables10.0.0.go | 1 - .../x/text/unicode/norm/tables11.0.0.go | 1 - .../x/text/unicode/norm/tables12.0.0.go | 1 - .../x/text/unicode/norm/tables13.0.0.go | 1 - .../x/text/unicode/norm/tables15.0.0.go | 1 - .../x/text/unicode/norm/tables9.0.0.go | 1 - vendor/golang.org/x/time/AUTHORS | 3 - vendor/golang.org/x/time/CONTRIBUTORS | 3 - vendor/golang.org/x/time/rate/rate.go | 125 +- vendor/golang.org/x/time/rate/sometimes.go | 67 + .../x/tools/cmd/stringer/stringer.go | 660 + .../x/tools/go/ast/astutil/enclosing.go | 634 + .../x/tools/go/ast/astutil/imports.go | 485 + .../x/tools/go/ast/astutil/rewrite.go | 486 + .../golang.org/x/tools/go/ast/astutil/util.go | 18 + .../x/tools/go/ast/inspector/inspector.go | 4 +- .../x/tools/go/ast/inspector/typeof.go | 4 +- .../x/tools/go/gcexportdata/gcexportdata.go | 11 +- .../tools/go/internal/packagesdriver/sizes.go | 24 +- vendor/golang.org/x/tools/go/packages/doc.go | 36 +- .../x/tools/go/packages/external.go | 2 +- .../golang.org/x/tools/go/packages/golist.go | 118 +- .../x/tools/go/packages/golist_overlay.go | 492 - .../x/tools/go/packages/packages.go | 197 +- .../x/tools/go/types/objectpath/objectpath.go | 752 + vendor/golang.org/x/tools/imports/forward.go | 77 + .../x/tools/internal/event/keys/util.go | 21 + .../x/tools/internal/event/tag/tag.go | 59 + .../x/tools/internal/gcimporter/bexport.go | 852 -- .../x/tools/internal/gcimporter/bimport.go | 907 +- .../x/tools/internal/gcimporter/gcimporter.go | 30 +- .../x/tools/internal/gcimporter/iexport.go | 229 +- .../x/tools/internal/gcimporter/iimport.go | 228 +- .../tools/internal/gcimporter/ureader_yes.go | 50 +- .../x/tools/internal/gocommand/invoke.go | 173 +- .../x/tools/internal/gocommand/version.go | 18 +- .../x/tools/internal/gopathwalk/walk.go | 331 + .../x/tools/internal/imports/fix.go | 1769 +++ .../x/tools/internal/imports/imports.go | 356 + .../x/tools/internal/imports/mod.go | 723 + .../x/tools/internal/imports/mod_cache.go | 236 + .../x/tools/internal/imports/sortimports.go | 297 + .../x/tools/internal/imports/zstdlib.go | 11345 ++++++++++++++++ .../internal/packagesinternal/packages.go | 8 - .../internal/tokeninternal/tokeninternal.go | 92 + .../x/tools/internal/typeparams/common.go | 51 +- .../x/tools/internal/typeparams/coretype.go | 16 +- .../internal/typeparams/enabled_go117.go | 12 - .../internal/typeparams/enabled_go118.go | 15 - .../x/tools/internal/typeparams/normalize.go | 20 +- .../x/tools/internal/typeparams/termlist.go | 2 +- .../internal/typeparams/typeparams_go117.go | 197 - .../internal/typeparams/typeparams_go118.go | 151 - .../x/tools/internal/typeparams/typeterm.go | 9 +- .../x/tools/internal/versions/gover.go | 172 + .../x/tools/internal/versions/types.go | 19 + .../x/tools/internal/versions/types_go121.go | 20 + .../x/tools/internal/versions/types_go122.go | 24 + .../x/tools/internal/versions/versions.go | 52 + .../appengine/internal/api.go | 347 +- .../appengine/internal/api_classic.go | 29 +- .../appengine/internal/api_common.go | 50 +- .../appengine/internal/identity.go | 7 +- .../appengine/internal/identity_classic.go | 23 +- .../appengine/internal/identity_flex.go | 1 + .../appengine/internal/identity_vm.go | 20 +- .../appengine/internal/main.go | 1 + .../appengine/internal/main_vm.go | 3 +- .../appengine/internal/transaction.go | 10 +- .../appengine/urlfetch/urlfetch.go | 9 +- .../genproto/googleapis/api/LICENSE | 202 + .../googleapis/api/annotations/client.pb.go | 366 +- .../api/annotations/field_behavior.pb.go | 42 +- .../api/annotations/field_info.pb.go | 295 + .../genproto/googleapis/rpc/LICENSE | 202 + vendor/google.golang.org/grpc/README.md | 60 +- .../grpc/attributes/attributes.go | 59 +- .../grpc/balancer/balancer.go | 69 +- .../grpc/balancer/base/balancer.go | 22 +- .../grpc/balancer_conn_wrappers.go | 459 - .../grpc/balancer_wrapper.go | 337 + .../grpc_binarylog_v1/binarylog.pb.go | 6 +- vendor/google.golang.org/grpc/call.go | 11 +- vendor/google.golang.org/grpc/clientconn.go | 903 +- vendor/google.golang.org/grpc/codec.go | 8 +- vendor/google.golang.org/grpc/codes/codes.go | 8 +- .../grpc/credentials/credentials.go | 4 +- .../google.golang.org/grpc/credentials/tls.go | 75 +- vendor/google.golang.org/grpc/dialoptions.go | 98 +- .../grpc/encoding/encoding.go | 17 +- .../grpc/encoding/proto/proto.go | 28 +- .../grpc/grpclog/component.go | 40 +- .../google.golang.org/grpc/grpclog/grpclog.go | 30 +- .../google.golang.org/grpc/grpclog/logger.go | 30 +- .../grpc/grpclog/loggerv2.go | 56 +- vendor/google.golang.org/grpc/idle.go | 287 - vendor/google.golang.org/grpc/interceptor.go | 12 +- .../grpc/internal/backoff/backoff.go | 36 + .../balancer/gracefulswitch/config.go | 83 + .../balancer/gracefulswitch/gracefulswitch.go | 104 +- .../grpc/internal/balancerload/load.go | 4 +- .../grpc/internal/binarylog/method_logger.go | 13 +- .../grpc/internal/binarylog/sink.go | 2 +- .../grpc/internal/buffer/unbounded.go | 57 +- .../grpc/internal/channelz/channel.go | 255 + .../grpc/internal/channelz/channelmap.go | 402 + .../grpc/internal/channelz/funcs.go | 761 +- .../grpc/internal/channelz/id.go | 75 - .../grpc/internal/channelz/logging.go | 28 +- .../grpc/internal/channelz/server.go | 119 + .../grpc/internal/channelz/socket.go | 130 + .../grpc/internal/channelz/subchannel.go | 151 + .../{types_linux.go => syscall_linux.go} | 14 + ...{types_nonlinux.go => syscall_nonlinux.go} | 6 +- .../grpc/internal/channelz/trace.go | 204 + .../grpc/internal/channelz/types.go | 722 - .../grpc/internal/credentials/credentials.go | 8 +- .../grpc/internal/envconfig/envconfig.go | 11 +- .../grpc/internal/envconfig/xds.go | 39 - .../grpc/internal/experimental.go | 28 + .../grpc/internal/grpclog/grpclog.go | 40 +- .../grpc/internal/grpclog/prefixLogger.go | 8 +- .../grpc/internal/grpcrand/grpcrand.go | 12 + .../grpc/internal/grpcrand/grpcrand_go1.21.go | 73 + .../internal/grpcsync/callback_serializer.go | 75 +- .../grpc/internal/grpcsync/pubsub.go | 121 + .../grpc/internal/idle/idle.go | 278 + .../grpc/internal/internal.go | 78 +- .../grpc/internal/metadata/metadata.go | 2 +- .../grpc/internal/pretty/pretty.go | 37 +- .../grpc/internal/resolver/config_selector.go | 4 +- .../internal/resolver/dns/dns_resolver.go | 166 +- .../resolver/dns/internal/internal.go | 70 + .../grpc/internal/resolver/unix/unix.go | 4 + .../grpc/internal/status/status.go | 51 +- ...il_nonlinux.go => tcp_keepalive_others.go} | 18 +- .../grpc/internal/tcp_keepalive_unix.go | 54 + .../grpc/internal/tcp_keepalive_windows.go | 54 + .../grpc/internal/transport/controlbuf.go | 21 +- .../grpc/internal/transport/handler_server.go | 93 +- .../grpc/internal/transport/http2_client.go | 158 +- .../grpc/internal/transport/http2_server.go | 232 +- .../grpc/internal/transport/http_util.go | 76 +- .../grpc/internal/transport/proxy.go | 14 +- .../grpc/internal/transport/transport.go | 78 +- .../grpc/internal/xds_handshake_cluster.go | 40 - .../grpc/metadata/metadata.go | 31 +- vendor/google.golang.org/grpc/peer/peer.go | 2 + .../google.golang.org/grpc/picker_wrapper.go | 53 +- vendor/google.golang.org/grpc/pickfirst.go | 90 +- vendor/google.golang.org/grpc/preloader.go | 2 +- .../grpc/resolver/dns/dns_resolver.go | 54 + .../grpc/resolver/manual/manual.go | 55 +- vendor/google.golang.org/grpc/resolver/map.go | 123 +- .../grpc/resolver/resolver.go | 110 +- .../grpc/resolver_conn_wrapper.go | 239 - .../grpc/resolver_wrapper.go | 198 + vendor/google.golang.org/grpc/rpc_util.go | 114 +- vendor/google.golang.org/grpc/server.go | 592 +- .../google.golang.org/grpc/service_config.go | 41 +- .../grpc/shared_buffer_pool.go | 154 + vendor/google.golang.org/grpc/stats/stats.go | 14 +- .../google.golang.org/grpc/status/status.go | 14 +- vendor/google.golang.org/grpc/stream.go | 143 +- vendor/google.golang.org/grpc/tap/tap.go | 6 + vendor/google.golang.org/grpc/trace.go | 32 +- .../google.golang.org/grpc/trace_notrace.go | 52 + .../google.golang.org/grpc/trace_withtrace.go | 39 + vendor/google.golang.org/grpc/version.go | 2 +- vendor/google.golang.org/grpc/vet.sh | 179 +- .../encoding/protodelim/protodelim.go | 160 + .../protobuf/encoding/protojson/decode.go | 38 +- .../protobuf/encoding/protojson/doc.go | 2 +- .../protobuf/encoding/protojson/encode.go | 53 +- .../encoding/protojson/well_known_types.go | 59 +- .../protobuf/encoding/prototext/decode.go | 8 +- .../protobuf/encoding/prototext/encode.go | 18 +- .../protobuf/encoding/protowire/wire.go | 28 +- .../protobuf/internal/descfmt/stringer.go | 183 +- .../internal/editiondefaults/defaults.go | 12 + .../editiondefaults/editions_defaults.binpb | 4 + .../protobuf/internal/encoding/json/decode.go | 2 +- .../protobuf/internal/encoding/json/encode.go | 10 +- .../protobuf/internal/encoding/text/encode.go | 10 +- .../protobuf/internal/filedesc/desc.go | 102 +- .../protobuf/internal/filedesc/desc_init.go | 52 + .../protobuf/internal/filedesc/desc_lazy.go | 28 + .../protobuf/internal/filedesc/editions.go | 142 + .../protobuf/internal/genid/descriptor_gen.go | 376 +- .../internal/genid/go_features_gen.go | 31 + .../protobuf/internal/genid/struct_gen.go | 5 + .../protobuf/internal/genid/type_gen.go | 44 + .../protobuf/internal/impl/codec_extension.go | 22 +- .../protobuf/internal/impl/codec_gen.go | 113 +- .../protobuf/internal/impl/codec_tables.go | 2 +- .../protobuf/internal/impl/legacy_message.go | 19 +- .../protobuf/internal/impl/message.go | 17 +- .../internal/impl/message_reflect_field.go | 2 +- .../protobuf/internal/impl/pointer_reflect.go | 36 + .../protobuf/internal/impl/pointer_unsafe.go | 40 + .../protobuf/internal/order/order.go | 2 +- .../protobuf/internal/strs/strings.go | 2 +- ...ings_unsafe.go => strings_unsafe_go120.go} | 4 +- .../internal/strs/strings_unsafe_go121.go | 74 + .../protobuf/internal/version/version.go | 2 +- .../protobuf/proto/decode.go | 2 +- .../google.golang.org/protobuf/proto/doc.go | 58 +- .../protobuf/proto/encode.go | 2 +- .../protobuf/proto/extension.go | 2 +- .../google.golang.org/protobuf/proto/merge.go | 2 +- .../google.golang.org/protobuf/proto/proto.go | 18 +- .../google.golang.org/protobuf/proto/size.go | 10 +- .../protobuf/protoadapt/convert.go | 31 + .../protobuf/reflect/protodesc/desc.go | 29 +- .../protobuf/reflect/protodesc/desc_init.go | 56 + .../reflect/protodesc/desc_resolve.go | 4 +- .../reflect/protodesc/desc_validate.go | 6 +- .../protobuf/reflect/protodesc/editions.go | 148 + .../protobuf/reflect/protodesc/proto.go | 18 +- .../protobuf/reflect/protoreflect/proto.go | 85 +- .../reflect/protoreflect/source_gen.go | 87 +- .../protobuf/reflect/protoreflect/type.go | 44 +- .../protobuf/reflect/protoreflect/value.go | 24 +- .../reflect/protoreflect/value_equal.go | 8 +- .../reflect/protoreflect/value_union.go | 44 +- ...{value_unsafe.go => value_unsafe_go120.go} | 4 +- .../protoreflect/value_unsafe_go121.go | 87 + .../reflect/protoregistry/registry.go | 24 +- .../types/descriptorpb/descriptor.pb.go | 2700 +++- .../types/gofeaturespb/go_features.pb.go | 177 + .../types/gofeaturespb/go_features.proto | 28 + .../protobuf/types/known/anypb/any.pb.go | 73 +- .../types/known/timestamppb/timestamp.pb.go | 2 +- .../options/options.go} | 10 +- .../DataDog/dd-trace-go.v1/ddtrace/ddtrace.go | 49 +- .../dd-trace-go.v1/ddtrace/ext/cassandra.go | 29 - .../DataDog/dd-trace-go.v1/ddtrace/ext/db.go | 66 + .../dd-trace-go.v1/ddtrace/ext/messaging.go | 25 + .../dd-trace-go.v1/ddtrace/ext/peer.go | 1 + .../DataDog/dd-trace-go.v1/ddtrace/ext/rpc.go | 34 + .../dd-trace-go.v1/ddtrace/ext/span_kind.go | 32 + .../dd-trace-go.v1/ddtrace/ext/system.go | 2 +- .../dd-trace-go.v1/ddtrace/ext/tags.go | 19 +- .../ddtrace/internal/globaltracer.go | 44 +- .../dd-trace-go.v1/ddtrace/opentracer/span.go | 12 +- .../ddtrace/opentracer/tracer.go | 7 +- .../dd-trace-go.v1/ddtrace/span_link_msgp.go | 221 + .../ddtrace/tracer/abandonedspans.go | 300 + .../dd-trace-go.v1/ddtrace/tracer/context.go | 17 +- .../ddtrace/tracer/data_streams.go | 74 + .../dd-trace-go.v1/ddtrace/tracer/doc.go | 57 +- .../ddtrace/tracer/dynamic_config.go | 117 + .../dd-trace-go.v1/ddtrace/tracer/log.go | 80 +- .../dd-trace-go.v1/ddtrace/tracer/metrics.go | 16 +- .../dd-trace-go.v1/ddtrace/tracer/option.go | 591 +- .../dd-trace-go.v1/ddtrace/tracer/payload.go | 67 +- .../ddtrace/tracer/propagating_tags.go | 68 + .../ddtrace/tracer/propagator.go | 2 +- .../ddtrace/tracer/remote_config.go | 224 + .../ddtrace/tracer/rules_sampler.go | 379 +- .../dd-trace-go.v1/ddtrace/tracer/sampler.go | 4 +- .../dd-trace-go.v1/ddtrace/tracer/span.go | 249 +- .../ddtrace/tracer/span_msgp.go | 266 +- .../ddtrace/tracer/spancontext.go | 374 +- .../ddtrace/tracer/sqlcomment.go | 244 +- .../dd-trace-go.v1/ddtrace/tracer/stats.go | 22 +- .../ddtrace/tracer/telemetry.go | 104 + .../dd-trace-go.v1/ddtrace/tracer/textmap.go | 754 +- .../dd-trace-go.v1/ddtrace/tracer/time.go | 7 +- .../ddtrace/tracer/time_windows.go | 22 +- .../dd-trace-go.v1/ddtrace/tracer/tracer.go | 360 +- .../ddtrace/tracer/transport.go | 37 +- .../dd-trace-go.v1/ddtrace/tracer/util.go | 2 +- .../dd-trace-go.v1/ddtrace/tracer/writer.go | 61 +- ...sabled_build_tag.go => active_span_key.go} | 9 +- .../DataDog/dd-trace-go.v1/internal/agent.go | 36 + .../dd-trace-go.v1/internal/appsec/appsec.go | 144 +- .../internal/appsec/appsec_disabled.go | 38 - .../dd-trace-go.v1/internal/appsec/config.go | 169 - .../internal/appsec/config/config.go | 81 + .../internal/appsec/config/rules_manager.go | 167 + .../appsec/dyngo/instrumentation/common.go | 131 - .../dyngo/instrumentation/grpcsec/grpc.go | 175 - .../dyngo/instrumentation/grpcsec/tags.go | 44 - .../dyngo/instrumentation/httpsec/http.go | 258 - .../dyngo/instrumentation/httpsec/tags.go | 81 - .../internal/appsec/dyngo/operation.go | 349 +- .../appsec/emitter/sharedsec/actions.go | 138 + .../emitter/sharedsec/blocked-template.html | 1 + .../emitter/sharedsec/blocked-template.json | 1 + .../appsec/emitter/sharedsec/shared.go | 71 + .../internal/appsec/listener/listener.go | 31 + .../internal/appsec/remoteconfig.go | 415 + .../dd-trace-go.v1/internal/appsec/rule.go | 13 - .../internal/appsec/telemetry.go | 83 + .../internal/appsec/telemetry_cgo.go | 14 + .../dd-trace-go.v1/internal/appsec/waf.go | 386 +- .../appsec/waf/lib/darwin-amd64/libddwaf.a | Bin 1572044 -> 0 bytes .../appsec/waf/lib/linux-amd64/libddwaf.a | Bin 3127504 -> 0 bytes .../internal/appsec/waf/types.go | 75 - .../dd-trace-go.v1/internal/appsec/waf/waf.go | 861 -- .../internal/appsec/waf/waf_disabled.go | 65 - .../internal/appsec/waf/waf_disabled_cgo.go | 12 - .../appsec/waf/waf_disabled_target.go | 19 - .../internal/container_linux.go | 182 + .../dd-trace-go.v1/internal/container_stub.go | 19 + .../internal/datastreams/fast_queue.go | 67 + .../internal/datastreams/hash_cache.go | 70 + .../internal/datastreams/pathway.go | 96 + .../internal/datastreams/payload.go | 84 + .../internal/datastreams/payload_msgp.go | 907 ++ .../internal/datastreams/processor.go | 533 + .../internal/datastreams/propagator.go | 87 + .../internal/datastreams/transport.go | 120 + .../DataDog/dd-trace-go.v1/internal/env.go | 66 + .../dd-trace-go.v1/internal/gitmetadata.go | 143 + .../internal/gitmetadatabinary.go | 41 + .../internal/gitmetadatabinary_legacy.go | 19 + .../internal/globalconfig/globalconfig.go | 58 +- .../internal/hostname/azure/azure.go | 63 + .../internal/hostname/cachedfetch/fetcher.go | 86 + .../internal/hostname/ec2/ec2.go | 72 + .../internal/hostname/ecs/aws.go | 54 + .../internal/hostname/fqdn_nix.go | 28 + .../internal/hostname/fqdn_windows.go | 14 + .../internal/hostname/gce/gce.go | 120 + .../internal/hostname/httputils/helpers.go | 74 + .../internal/hostname/providers.go | 245 + .../internal/hostname/validate/validate.go | 57 + .../dd-trace-go.v1/internal/log/log.go | 63 +- .../internal/namingschema/namingschema.go | 77 + .../internal/namingschema/op.go | 191 + .../internal/namingschema/service_name.go | 40 + .../internal/normalizer/normalizer.go | 56 + .../internal/osinfo/osinfo_linux.go | 2 +- .../internal/remoteconfig/config.go | 68 + .../internal/remoteconfig/remoteconfig.go | 616 + .../internal/remoteconfig/types.go | 83 + .../internal/samplernames/samplernames.go | 9 +- .../DataDog/dd-trace-go.v1/internal/statsd.go | 220 + .../internal/telemetry/client.go | 590 + .../internal/telemetry/message.go | 266 + .../internal/telemetry/option.go | 143 + .../internal/telemetry/telemetry.go | 121 + .../internal/telemetry/utils.go | 90 + .../dd-trace-go.v1/internal/trace_context.go | 47 + .../internal/traceprof/endpoint_counter.go | 105 + .../internal/traceprof/profiler.go | 35 + .../internal/traceprof/traceprof.go | 5 +- .../DataDog/dd-trace-go.v1/internal/utils.go | 64 + .../internal/version/version.go | 34 +- .../admissionregistration/v1/generated.pb.go | 484 +- .../admissionregistration/v1/generated.proto | 77 + .../api/admissionregistration/v1/types.go | 77 + .../v1/types_swagger_doc_generated.go | 14 +- .../v1/zz_generated.deepcopy.go | 26 + .../api/admissionregistration/v1alpha1/doc.go | 23 + .../v1alpha1/generated.pb.go | 4634 +++++++ .../v1alpha1/generated.proto | 609 + .../v1alpha1/register.go | 56 + .../admissionregistration/v1alpha1/types.go | 665 + .../v1alpha1/types_swagger_doc_generated.go | 204 + .../v1alpha1/zz_generated.deepcopy.go | 475 + .../v1beta1/generated.pb.go | 6119 +++++++-- .../v1beta1/generated.proto | 669 +- .../admissionregistration/v1beta1/register.go | 4 + .../admissionregistration/v1beta1/types.go | 737 +- .../v1beta1/types_swagger_doc_generated.go | 193 +- .../v1beta1/zz_generated.deepcopy.go | 463 +- .../zz_generated.prerelease-lifecycle.go | 72 + vendor/k8s.io/api/apidiscovery/v2beta1/doc.go | 24 + .../api/apidiscovery/v2beta1/generated.pb.go | 1744 +++ .../api/apidiscovery/v2beta1/generated.proto | 156 + .../api/apidiscovery/v2beta1/register.go | 56 + .../k8s.io/api/apidiscovery/v2beta1/types.go | 163 + .../v2beta1/zz_generated.deepcopy.go | 190 + .../zz_generated.prerelease-lifecycle.go | 58 + .../v1alpha1/generated.pb.go | 148 +- .../v1alpha1/generated.proto | 7 +- .../api/apiserverinternal/v1alpha1/types.go | 7 +- .../v1alpha1/types_swagger_doc_generated.go | 5 +- .../v1alpha1/zz_generated.deepcopy.go | 5 + vendor/k8s.io/api/apps/v1/generated.pb.go | 481 +- vendor/k8s.io/api/apps/v1/generated.proto | 40 +- vendor/k8s.io/api/apps/v1/types.go | 43 +- .../apps/v1/types_swagger_doc_generated.go | 28 +- .../api/apps/v1/zz_generated.deepcopy.go | 21 + .../k8s.io/api/apps/v1beta1/generated.pb.go | 459 +- .../k8s.io/api/apps/v1beta1/generated.proto | 89 +- vendor/k8s.io/api/apps/v1beta1/types.go | 89 +- .../v1beta1/types_swagger_doc_generated.go | 72 +- .../api/apps/v1beta1/zz_generated.deepcopy.go | 21 + .../k8s.io/api/apps/v1beta2/generated.pb.go | 498 +- .../k8s.io/api/apps/v1beta2/generated.proto | 42 +- vendor/k8s.io/api/apps/v1beta2/types.go | 42 +- .../v1beta2/types_swagger_doc_generated.go | 30 +- .../api/apps/v1beta2/zz_generated.deepcopy.go | 21 + .../api/authentication/v1/generated.pb.go | 511 +- .../api/authentication/v1/generated.proto | 22 +- .../k8s.io/api/authentication/v1/register.go | 1 + vendor/k8s.io/api/authentication/v1/types.go | 27 +- .../v1/types_swagger_doc_generated.go | 23 +- .../v1/zz_generated.deepcopy.go | 44 + .../k8s.io/api/authentication/v1alpha1/doc.go | 23 + .../authentication/v1alpha1/generated.pb.go | 567 + .../authentication/v1alpha1/generated.proto | 51 + .../api/authentication/v1alpha1/register.go | 51 + .../api/authentication/v1alpha1/types.go | 48 + .../v1alpha1/types_swagger_doc_generated.go | 49 + .../v1alpha1/zz_generated.deepcopy.go | 70 + .../zz_generated.prerelease-lifecycle.go | 40 + .../authentication/v1beta1/generated.pb.go | 476 +- .../authentication/v1beta1/generated.proto | 21 + .../api/authentication/v1beta1/register.go | 1 + .../api/authentication/v1beta1/types.go | 27 + .../v1beta1/types_swagger_doc_generated.go | 21 +- .../v1beta1/zz_generated.deepcopy.go | 44 + .../zz_generated.prerelease-lifecycle.go | 18 + .../v1/types_swagger_doc_generated.go | 2 +- .../v1beta1/types_swagger_doc_generated.go | 2 +- .../k8s.io/api/autoscaling/v1/generated.proto | 42 +- vendor/k8s.io/api/autoscaling/v1/types.go | 79 +- .../v1/types_swagger_doc_generated.go | 42 +- .../k8s.io/api/autoscaling/v2/generated.proto | 20 +- vendor/k8s.io/api/autoscaling/v2/types.go | 61 +- .../v2/types_swagger_doc_generated.go | 22 +- .../api/autoscaling/v2beta1/generated.proto | 4 +- .../k8s.io/api/autoscaling/v2beta1/types.go | 4 +- .../v2beta1/types_swagger_doc_generated.go | 6 +- .../api/autoscaling/v2beta2/generated.proto | 24 +- .../k8s.io/api/autoscaling/v2beta2/types.go | 62 +- .../v2beta2/types_swagger_doc_generated.go | 26 +- vendor/k8s.io/api/batch/v1/generated.pb.go | 1424 +- vendor/k8s.io/api/batch/v1/generated.proto | 202 +- vendor/k8s.io/api/batch/v1/types.go | 287 +- .../batch/v1/types_swagger_doc_generated.go | 69 +- .../api/batch/v1/zz_generated.deepcopy.go | 121 + .../k8s.io/api/batch/v1beta1/generated.pb.go | 317 +- .../k8s.io/api/batch/v1beta1/generated.proto | 26 +- vendor/k8s.io/api/batch/v1beta1/register.go | 1 - vendor/k8s.io/api/batch/v1beta1/types.go | 31 +- .../v1beta1/types_swagger_doc_generated.go | 16 +- .../batch/v1beta1/zz_generated.deepcopy.go | 27 - .../zz_generated.prerelease-lifecycle.go | 18 - vendor/k8s.io/api/certificates/v1/types.go | 7 +- .../v1/types_swagger_doc_generated.go | 2 +- .../k8s.io/api/certificates/v1alpha1/doc.go | 24 + .../api/certificates/v1alpha1/generated.pb.go | 831 ++ .../api/certificates/v1alpha1/generated.proto | 103 + .../api/certificates/v1alpha1/register.go | 61 + .../k8s.io/api/certificates/v1alpha1/types.go | 106 + .../v1alpha1/types_swagger_doc_generated.go | 60 + .../v1alpha1/zz_generated.deepcopy.go | 102 + .../zz_generated.prerelease-lifecycle.go | 58 + .../api/certificates/v1beta1/generated.proto | 6 +- .../k8s.io/api/certificates/v1beta1/types.go | 12 +- .../v1beta1/types_swagger_doc_generated.go | 4 +- .../api/coordination/v1/generated.proto | 6 +- vendor/k8s.io/api/coordination/v1/types.go | 6 +- .../v1/types_swagger_doc_generated.go | 8 +- .../api/coordination/v1beta1/generated.proto | 6 +- .../k8s.io/api/coordination/v1beta1/types.go | 6 +- .../v1beta1/types_swagger_doc_generated.go | 8 +- .../api/core/v1/annotation_key_constants.go | 27 +- vendor/k8s.io/api/core/v1/generated.pb.go | 5955 +++++--- vendor/k8s.io/api/core/v1/generated.proto | 617 +- vendor/k8s.io/api/core/v1/toleration.go | 7 +- vendor/k8s.io/api/core/v1/types.go | 793 +- .../core/v1/types_swagger_doc_generated.go | 224 +- .../k8s.io/api/core/v1/well_known_labels.go | 4 + .../api/core/v1/zz_generated.deepcopy.go | 262 +- .../k8s.io/api/discovery/v1/generated.proto | 37 +- vendor/k8s.io/api/discovery/v1/types.go | 53 +- .../v1/types_swagger_doc_generated.go | 20 +- .../api/discovery/v1beta1/generated.proto | 22 +- vendor/k8s.io/api/discovery/v1beta1/types.go | 39 +- .../v1beta1/types_swagger_doc_generated.go | 18 +- .../events/v1/types_swagger_doc_generated.go | 2 +- .../v1beta1/types_swagger_doc_generated.go | 2 +- .../api/extensions/v1beta1/generated.pb.go | 11064 ++++++--------- .../api/extensions/v1beta1/generated.proto | 365 +- .../k8s.io/api/extensions/v1beta1/register.go | 2 - vendor/k8s.io/api/extensions/v1beta1/types.go | 494 +- .../v1beta1/types_swagger_doc_generated.go | 215 +- .../v1beta1/zz_generated.deepcopy.go | 457 +- .../zz_generated.prerelease-lifecycle.go | 48 - .../api/flowcontrol/v1alpha1/generated.pb.go | 532 +- .../api/flowcontrol/v1alpha1/generated.proto | 75 +- .../k8s.io/api/flowcontrol/v1alpha1/types.go | 86 +- .../v1alpha1/types_swagger_doc_generated.go | 17 +- .../v1alpha1/zz_generated.deepcopy.go | 41 + .../zz_generated.prerelease-lifecycle.go | 8 +- .../api/flowcontrol/v1beta1/generated.pb.go | 530 +- .../api/flowcontrol/v1beta1/generated.proto | 75 +- .../k8s.io/api/flowcontrol/v1beta1/types.go | 90 +- .../v1beta1/types_swagger_doc_generated.go | 17 +- .../v1beta1/zz_generated.deepcopy.go | 41 + .../zz_generated.prerelease-lifecycle.go | 8 +- .../api/flowcontrol/v1beta2/generated.pb.go | 531 +- .../api/flowcontrol/v1beta2/generated.proto | 75 +- .../k8s.io/api/flowcontrol/v1beta2/types.go | 86 +- .../v1beta2/types_swagger_doc_generated.go | 17 +- .../v1beta2/zz_generated.deepcopy.go | 41 + .../zz_generated.prerelease-lifecycle.go | 28 + vendor/k8s.io/api/flowcontrol/v1beta3/doc.go | 25 + .../api/flowcontrol/v1beta3/generated.pb.go | 5663 ++++++++ .../api/flowcontrol/v1beta3/generated.proto | 515 + .../api/flowcontrol/v1beta3/register.go | 58 + .../k8s.io/api/flowcontrol/v1beta3/types.go | 659 + .../v1beta3/types_swagger_doc_generated.go | 274 + .../v1beta3/zz_generated.deepcopy.go | 583 + .../zz_generated.prerelease-lifecycle.go | 94 + .../k8s.io/api/networking/v1/generated.pb.go | 1316 +- .../k8s.io/api/networking/v1/generated.proto | 248 +- vendor/k8s.io/api/networking/v1/types.go | 295 +- .../v1/types_swagger_doc_generated.go | 143 +- .../networking/v1/zz_generated.deepcopy.go | 91 +- vendor/k8s.io/api/networking/v1alpha1/doc.go | 23 + .../api/networking/v1alpha1/generated.pb.go | 1858 +++ .../api/networking/v1alpha1/generated.proto | 155 + .../api/networking/v1alpha1/register.go | 62 + .../k8s.io/api/networking/v1alpha1/types.go | 163 + .../v1alpha1/types_swagger_doc_generated.go | 104 + .../networking/v1alpha1/well_known_labels.go | 33 + .../v1alpha1/zz_generated.deepcopy.go | 205 + .../zz_generated.prerelease-lifecycle.go | 94 + .../api/networking/v1beta1/generated.pb.go | 830 +- .../api/networking/v1beta1/generated.proto | 113 +- vendor/k8s.io/api/networking/v1beta1/types.go | 124 +- .../v1beta1/types_swagger_doc_generated.go | 89 +- .../v1beta1/zz_generated.deepcopy.go | 67 + vendor/k8s.io/api/node/v1/generated.proto | 10 +- vendor/k8s.io/api/node/v1/types.go | 12 +- .../node/v1/types_swagger_doc_generated.go | 12 +- .../k8s.io/api/node/v1alpha1/generated.proto | 14 +- vendor/k8s.io/api/node/v1alpha1/types.go | 16 +- .../v1alpha1/types_swagger_doc_generated.go | 14 +- .../k8s.io/api/node/v1beta1/generated.proto | 12 +- vendor/k8s.io/api/node/v1beta1/types.go | 14 +- .../v1beta1/types_swagger_doc_generated.go | 12 +- vendor/k8s.io/api/policy/v1/generated.pb.go | 150 +- vendor/k8s.io/api/policy/v1/generated.proto | 28 + vendor/k8s.io/api/policy/v1/types.go | 48 + .../policy/v1/types_swagger_doc_generated.go | 11 +- .../api/policy/v1/zz_generated.deepcopy.go | 5 + .../k8s.io/api/policy/v1beta1/generated.pb.go | 287 +- .../k8s.io/api/policy/v1beta1/generated.proto | 29 +- vendor/k8s.io/api/policy/v1beta1/types.go | 49 +- .../v1beta1/types_swagger_doc_generated.go | 13 +- .../policy/v1beta1/zz_generated.deepcopy.go | 5 + vendor/k8s.io/api/rbac/v1/generated.proto | 4 +- vendor/k8s.io/api/rbac/v1/types.go | 4 +- .../rbac/v1/types_swagger_doc_generated.go | 8 +- .../k8s.io/api/rbac/v1alpha1/generated.proto | 2 +- vendor/k8s.io/api/rbac/v1alpha1/types.go | 2 +- .../v1alpha1/types_swagger_doc_generated.go | 4 +- .../k8s.io/api/rbac/v1beta1/generated.proto | 2 +- vendor/k8s.io/api/rbac/v1beta1/types.go | 2 +- .../v1beta1/types_swagger_doc_generated.go | 4 +- vendor/k8s.io/api/resource/v1alpha2/doc.go | 24 + .../api/resource/v1alpha2/generated.pb.go | 4817 +++++++ .../api/resource/v1alpha2/generated.proto | 400 + .../k8s.io/api/resource/v1alpha2/register.go | 63 + vendor/k8s.io/api/resource/v1alpha2/types.go | 462 + .../v1alpha2/types_swagger_doc_generated.go | 232 + .../v1alpha2/zz_generated.deepcopy.go | 498 + .../k8s.io/api/scheduling/v1/generated.proto | 4 +- vendor/k8s.io/api/scheduling/v1/types.go | 4 +- .../v1/types_swagger_doc_generated.go | 6 +- .../api/scheduling/v1alpha1/generated.proto | 4 +- .../k8s.io/api/scheduling/v1alpha1/types.go | 4 +- .../v1alpha1/types_swagger_doc_generated.go | 6 +- .../api/scheduling/v1beta1/generated.proto | 4 +- vendor/k8s.io/api/scheduling/v1beta1/types.go | 4 +- .../v1beta1/types_swagger_doc_generated.go | 6 +- vendor/k8s.io/api/storage/v1/generated.pb.go | 246 +- vendor/k8s.io/api/storage/v1/generated.proto | 145 +- vendor/k8s.io/api/storage/v1/types.go | 151 +- .../storage/v1/types_swagger_doc_generated.go | 81 +- .../api/storage/v1/zz_generated.deepcopy.go | 5 + .../api/storage/v1alpha1/generated.proto | 38 +- vendor/k8s.io/api/storage/v1alpha1/types.go | 41 +- .../v1alpha1/types_swagger_doc_generated.go | 38 +- .../api/storage/v1beta1/generated.pb.go | 246 +- .../api/storage/v1beta1/generated.proto | 131 +- vendor/k8s.io/api/storage/v1beta1/types.go | 138 +- .../v1beta1/types_swagger_doc_generated.go | 77 +- .../storage/v1beta1/zz_generated.deepcopy.go | 5 + .../apimachinery/pkg/api/equality/semantic.go | 49 + .../k8s.io/apimachinery/pkg/api/errors/OWNERS | 1 - .../apimachinery/pkg/api/errors/errors.go | 30 +- .../apimachinery/pkg/api/meta/conditions.go | 6 +- .../apimachinery/pkg/api/meta/errors.go | 35 +- .../k8s.io/apimachinery/pkg/api/meta/help.go | 89 +- .../k8s.io/apimachinery/pkg/api/meta/meta.go | 5 +- .../apimachinery/pkg/api/resource/OWNERS | 1 - .../pkg/api/resource/generated.proto | 27 +- .../apimachinery/pkg/api/resource/quantity.go | 37 +- .../apimachinery/pkg/api/resource/suffix.go | 2 +- .../apimachinery/pkg/api/validation/doc.go | 18 + .../pkg/api/validation/generic.go | 88 + .../pkg/api/validation/objectmeta.go | 265 + .../pkg/apis/meta/internalversion/defaults.go | 38 + .../pkg/apis/meta/internalversion/types.go | 25 + .../zz_generated.conversion.go | 2 + .../internalversion/zz_generated.deepcopy.go | 5 + .../pkg/apis/meta/v1/generated.pb.go | 437 +- .../pkg/apis/meta/v1/generated.proto | 101 +- .../apimachinery/pkg/apis/meta/v1/helpers.go | 2 +- .../apimachinery/pkg/apis/meta/v1/meta.go | 6 - .../pkg/apis/meta/v1/micro_time_proto.go | 10 +- .../apimachinery/pkg/apis/meta/v1/types.go | 132 +- .../meta/v1/types_swagger_doc_generated.go | 26 +- .../apis/meta/v1/unstructured/unstructured.go | 17 +- .../meta/v1/unstructured/unstructured_list.go | 9 + .../pkg/apis/meta/v1/validation/validation.go | 320 + .../apis/meta/v1/zz_generated.conversion.go | 7 + .../pkg/apis/meta/v1/zz_generated.deepcopy.go | 5 + .../v1beta1/types_swagger_doc_generated.go | 2 +- .../apimachinery/pkg/conversion/converter.go | 8 +- .../apimachinery/pkg/conversion/deep_equal.go | 11 + .../apimachinery/pkg/conversion/helper.go | 2 +- .../pkg/conversion/queryparams/convert.go | 4 +- .../k8s.io/apimachinery/pkg/labels/labels.go | 2 + .../apimachinery/pkg/labels/selector.go | 146 +- .../apimachinery/pkg/runtime/allocator.go | 14 +- .../k8s.io/apimachinery/pkg/runtime/codec.go | 14 +- .../apimachinery/pkg/runtime/codec_check.go | 2 +- .../apimachinery/pkg/runtime/converter.go | 20 +- .../apimachinery/pkg/runtime/generated.proto | 59 +- .../apimachinery/pkg/runtime/interfaces.go | 5 + .../pkg/runtime/schema/group_version.go | 6 +- .../k8s.io/apimachinery/pkg/runtime/scheme.go | 8 +- .../pkg/runtime/serializer/codec_factory.go | 2 +- .../serializer/versioning/versioning.go | 2 +- .../k8s.io/apimachinery/pkg/runtime/splice.go | 76 + .../k8s.io/apimachinery/pkg/runtime/types.go | 61 +- .../apimachinery/pkg/types/namespacedname.go | 11 + .../k8s.io/apimachinery/pkg/types/nodename.go | 24 +- .../apimachinery/pkg/util/cache/expiring.go | 12 +- .../apimachinery/pkg/util/clock/clock.go | 86 - .../k8s.io/apimachinery/pkg/util/diff/diff.go | 39 +- .../k8s.io/apimachinery/pkg/util/dump/dump.go | 54 + .../apimachinery/pkg/util/errors/errors.go | 2 +- .../apimachinery/pkg/util/framer/framer.go | 10 +- .../apimachinery/pkg/util/intstr/intstr.go | 9 +- .../pkg/util/managedfields/endpoints.yaml | 7018 ++++++++++ .../pkg/util/managedfields/extract.go | 2 +- .../pkg/util/managedfields/fieldmanager.go | 57 + .../managedfields/internal/atmostevery.go | 60 + .../internal/buildmanagerinfo.go | 74 + .../managedfields/internal/capmanagers.go | 133 + .../util/managedfields/internal/conflict.go | 89 + .../managedfields/internal/fieldmanager.go | 209 + .../pkg/util/managedfields/internal/fields.go | 47 + .../managedfields/internal/lastapplied.go | 50 + .../internal/lastappliedmanager.go | 171 + .../internal/lastappliedupdater.go | 102 + .../managedfields/internal/managedfields.go | 248 + .../internal/managedfieldsupdater.go | 82 + .../util/managedfields/internal/manager.go | 52 + .../managedfields/internal/pathelement.go | 140 + .../managedfields/internal/skipnonapplied.go | 89 + .../util/managedfields/internal/stripmeta.go | 90 + .../managedfields/internal/structuredmerge.go | 186 + .../managedfields/internal/typeconverter.go | 193 + .../managedfields/internal/versioncheck.go | 52 + .../internal/versionconverter.go | 123 + .../pkg/util/managedfields/node.yaml | 261 + .../pkg/util/managedfields/pod.yaml | 121 + .../pkg/util/managedfields/scalehandler.go | 174 + .../pkg/util/managedfields/typeconverter.go | 47 + .../apimachinery/pkg/util/mergepatch/util.go | 6 +- .../apimachinery/pkg/util/net/interface.go | 2 +- .../apimachinery/pkg/util/net/port_split.go | 13 +- .../k8s.io/apimachinery/pkg/util/net/util.go | 7 + .../apimachinery/pkg/util/runtime/runtime.go | 17 +- .../k8s.io/apimachinery/pkg/util/sets/byte.go | 150 +- .../k8s.io/apimachinery/pkg/util/sets/doc.go | 7 +- .../apimachinery/pkg/util/sets/empty.go | 4 +- .../k8s.io/apimachinery/pkg/util/sets/int.go | 150 +- .../apimachinery/pkg/util/sets/int32.go | 150 +- .../apimachinery/pkg/util/sets/int64.go | 150 +- .../apimachinery/pkg/util/sets/ordered.go | 53 + .../k8s.io/apimachinery/pkg/util/sets/set.go | 241 + .../apimachinery/pkg/util/sets/string.go | 150 +- .../pkg/util/strategicpatch/OWNERS | 1 + .../pkg/util/strategicpatch/meta.go | 4 +- .../pkg/util/strategicpatch/patch.go | 65 +- .../pkg/util/validation/field/errors.go | 6 +- .../pkg/util/validation/validation.go | 10 +- .../apimachinery/pkg/util/version/version.go | 5 + .../apimachinery/pkg/util/wait/backoff.go | 502 + .../apimachinery/pkg/util/wait/delay.go | 51 + .../apimachinery/pkg/util/wait/error.go | 96 + .../k8s.io/apimachinery/pkg/util/wait/loop.go | 97 + .../k8s.io/apimachinery/pkg/util/wait/poll.go | 315 + .../apimachinery/pkg/util/wait/timer.go | 121 + .../k8s.io/apimachinery/pkg/util/wait/wait.go | 642 +- .../k8s.io/apimachinery/pkg/watch/filter.go | 1 - vendor/k8s.io/apimachinery/pkg/watch/mux.go | 53 +- .../third_party/forked/golang/json/fields.go | 4 +- .../forked/golang/reflect/deep_equal.go | 84 +- .../v1/matchcondition.go | 48 + .../v1/mutatingwebhook.go | 14 + .../v1/validatingwebhook.go | 14 + .../v1alpha1/auditannotation.go | 48 + .../v1alpha1/expressionwarning.go | 48 + .../v1alpha1/matchcondition.go | 48 + .../v1alpha1/matchresources.go | 90 + .../v1alpha1/namedrulewithoperations.go | 95 + .../v1alpha1/paramkind.go | 48 + .../v1alpha1/paramref.go | 71 + .../v1alpha1/typechecking.go | 44 + .../v1alpha1/validatingadmissionpolicy.go | 256 + .../validatingadmissionpolicybinding.go | 247 + .../validatingadmissionpolicybindingspec.go | 72 + .../v1alpha1/validatingadmissionpolicyspec.go | 117 + .../validatingadmissionpolicystatus.go | 66 + .../v1alpha1/validation.go | 70 + .../v1alpha1/variable.go | 48 + .../v1beta1/auditannotation.go | 48 + .../v1beta1/expressionwarning.go | 48 + .../v1beta1/matchcondition.go | 48 + .../v1beta1/matchresources.go | 90 + .../v1beta1/mutatingwebhook.go | 29 +- ...erations.go => namedrulewithoperations.go} | 37 +- .../v1beta1/paramkind.go | 48 + .../admissionregistration/v1beta1/paramref.go | 71 + .../admissionregistration/v1beta1/rule.go | 76 - .../v1beta1/typechecking.go | 44 + .../v1beta1/validatingadmissionpolicy.go | 256 + .../validatingadmissionpolicybinding.go | 247 + .../validatingadmissionpolicybindingspec.go | 72 + .../v1beta1/validatingadmissionpolicyspec.go | 117 + .../validatingadmissionpolicystatus.go | 66 + .../v1beta1/validatingwebhook.go | 29 +- .../v1beta1/validation.go | 70 + .../admissionregistration/v1beta1/variable.go | 48 + .../v1alpha1/serverstorageversion.go | 11 + .../apps/v1/statefulsetordinals.go | 39 + .../apps/v1/statefulsetspec.go | 9 + .../v1beta1/statefulsetordinals.go} | 20 +- .../apps/v1beta1/statefulsetspec.go | 9 + .../apps/v1beta2/statefulsetordinals.go | 39 + .../apps/v1beta2/statefulsetspec.go | 9 + .../applyconfigurations/batch/v1/jobspec.go | 42 +- .../applyconfigurations/batch/v1/jobstatus.go | 18 + .../batch/v1/podfailurepolicy.go | 44 + .../podfailurepolicyonexitcodesrequirement.go | 63 + .../podfailurepolicyonpodconditionspattern.go | 52 + .../batch/v1/podfailurepolicyrule.go | 66 + .../v1alpha1/clustertrustbundle.go | 247 + .../v1alpha1/clustertrustbundlespec.go | 48 + .../core/v1/claimsource.go | 48 + .../applyconfigurations/core/v1/container.go | 67 +- .../core/v1/containerresizepolicy.go | 52 + .../core/v1/containerstatus.go | 40 +- .../core/v1/csipersistentvolumesource.go | 9 + .../core/v1/ephemeralcontainer.go | 21 + .../core/v1/ephemeralcontainercommon.go | 67 +- .../applyconfigurations/core/v1/hostip.go | 39 + .../core/v1/persistentvolumeclaimspec.go | 4 +- .../core/v1/persistentvolumeclaimstatus.go | 28 +- .../core/v1/persistentvolumestatus.go | 16 +- .../v1/podresourceclaim.go} | 30 +- .../core/v1/podresourceclaimstatus.go | 48 + .../core/v1/podschedulinggate.go | 39 + .../applyconfigurations/core/v1/podspec.go | 37 + .../applyconfigurations/core/v1/podstatus.go | 63 +- .../v1/resourceclaim.go} | 14 +- .../core/v1/resourcerequirements.go | 18 +- .../core/v1/servicespec.go | 12 +- .../core/v1/topologyspreadconstraint.go | 39 +- .../core/v1/typedobjectreference.go | 66 + .../extensions/v1beta1/allowedhostpath.go | 48 - .../v1beta1/fsgroupstrategyoptions.go | 57 - .../extensions/v1beta1/hostportrange.go | 48 - .../extensions/v1beta1/idrange.go | 48 - .../v1beta1/ingressloadbalanceringress.go | 62 + .../v1beta1/ingressloadbalancerstatus.go | 44 + .../extensions/v1beta1/ingressportstatus.go | 61 + .../extensions/v1beta1/ingressstatus.go | 8 +- .../extensions/v1beta1/networkpolicy.go | 11 +- .../v1beta1/podsecuritypolicyspec.go | 285 - .../v1beta1/runasgroupstrategyoptions.go | 57 - .../v1beta1/runasuserstrategyoptions.go | 57 - .../v1beta1/runtimeclassstrategyoptions.go | 50 - .../v1beta1/selinuxstrategyoptions.go | 53 - .../supplementalgroupsstrategyoptions.go | 57 - .../exemptprioritylevelconfiguration.go | 48 + .../limitedprioritylevelconfiguration.go | 18 + .../prioritylevelconfigurationspec.go | 9 + .../exemptprioritylevelconfiguration.go | 48 + .../limitedprioritylevelconfiguration.go | 18 + .../v1beta1/prioritylevelconfigurationspec.go | 9 + .../exemptprioritylevelconfiguration.go | 48 + .../limitedprioritylevelconfiguration.go | 18 + .../v1beta2/prioritylevelconfigurationspec.go | 9 + .../exemptprioritylevelconfiguration.go | 48 + .../v1beta3/flowdistinguishermethod.go | 43 + .../flowcontrol/v1beta3/flowschema.go | 256 + .../v1beta3/flowschemacondition.go | 80 + .../flowcontrol/v1beta3/flowschemaspec.go | 71 + .../v1beta3/flowschemastatus.go} | 20 +- .../flowcontrol/v1beta3/groupsubject.go | 39 + .../limitedprioritylevelconfiguration.go | 66 + .../flowcontrol/v1beta3/limitresponse.go | 52 + .../v1beta3/nonresourcepolicyrule.go | 52 + .../v1beta3/policyruleswithsubjects.go | 72 + .../v1beta3/prioritylevelconfiguration.go | 256 + .../prioritylevelconfigurationcondition.go | 80 + .../prioritylevelconfigurationreference.go | 39 + .../v1beta3/prioritylevelconfigurationspec.go | 61 + .../prioritylevelconfigurationstatus.go} | 20 +- .../v1beta3/queuingconfiguration.go | 57 + .../flowcontrol/v1beta3/resourcepolicyrule.go | 83 + .../v1beta3/serviceaccountsubject.go | 48 + .../flowcontrol/v1beta3/subject.go | 70 + .../flowcontrol/v1beta3/usersubject.go | 39 + .../applyconfigurations/internal/internal.go | 2448 +++- .../v1/ingressloadbalanceringress.go | 62 + .../v1/ingressloadbalancerstatus.go | 44 + .../networking/v1/ingressportstatus.go | 61 + .../networking/v1/ingressstatus.go | 8 +- .../networking/v1/networkpolicy.go | 11 +- .../networking/v1alpha1/clustercidr.go | 247 + .../networking/v1alpha1/clustercidrspec.go | 70 + .../v1alpha1/ipaddress.go} | 86 +- .../networking/v1alpha1/ipaddressspec.go | 39 + .../networking/v1alpha1/parentreference.go | 79 + .../v1beta1/ingressloadbalanceringress.go | 62 + .../v1beta1/ingressloadbalancerstatus.go | 44 + .../networking/v1beta1/ingressportstatus.go | 61 + .../networking/v1beta1/ingressstatus.go | 8 +- .../policy/v1/poddisruptionbudgetspec.go | 16 +- .../policy/v1beta1/poddisruptionbudgetspec.go | 16 +- .../resource/v1alpha2/allocationresult.go | 66 + .../resource/v1alpha2/podschedulingcontext.go | 258 + .../v1alpha2/podschedulingcontextspec.go | 50 + .../v1alpha2/podschedulingcontextstatus.go | 44 + .../resource/v1alpha2/resourceclaim.go | 258 + .../resourceclaimconsumerreference.go | 70 + .../resourceclaimparametersreference.go | 57 + .../v1alpha2/resourceclaimschedulingstatus.go | 50 + .../resource/v1alpha2/resourceclaimspec.go | 61 + .../resource/v1alpha2/resourceclaimstatus.go | 71 + .../v1alpha2/resourceclaimtemplate.go | 249 + .../v1alpha2/resourceclaimtemplatespec.go | 188 + .../resource/v1alpha2/resourceclass.go | 266 + .../resourceclassparametersreference.go | 66 + .../resource/v1alpha2/resourcehandle.go | 48 + .../storage/v1/csidriverspec.go | 9 + .../storage/v1beta1/csidriverspec.go | 9 + .../discovery/aggregated_discovery.go | 156 + .../client-go/discovery/discovery_client.go | 306 +- .../client-go/discovery/fake/discovery.go | 12 +- .../k8s.io/client-go/kubernetes/clientset.go | 171 +- vendor/k8s.io/client-go/kubernetes/doc.go | 7 +- .../kubernetes/fake/clientset_generated.go | 42 + .../client-go/kubernetes/fake/register.go | 26 +- .../client-go/kubernetes/scheme/register.go | 26 +- .../fake/fake_mutatingwebhookconfiguration.go | 59 +- .../fake_validatingwebhookconfiguration.go | 59 +- .../v1alpha1/admissionregistration_client.go | 112 + .../admissionregistration}/v1alpha1/doc.go | 8 +- .../v1alpha1/fake/doc.go | 20 + .../fake/fake_admissionregistration_client.go | 44 + .../fake/fake_validatingadmissionpolicy.go | 178 + .../fake_validatingadmissionpolicybinding.go | 145 + .../v1alpha1/generated_expansion.go | 23 + .../v1alpha1/validatingadmissionpolicy.go | 243 + .../validatingadmissionpolicybinding.go | 197 + .../v1beta1/admissionregistration_client.go | 10 + .../fake/fake_admissionregistration_client.go | 8 + .../fake/fake_mutatingwebhookconfiguration.go | 5 +- .../fake/fake_validatingadmissionpolicy.go | 178 + .../fake_validatingadmissionpolicybinding.go | 145 + .../fake_validatingwebhookconfiguration.go | 5 +- .../v1beta1/generated_expansion.go | 4 + .../v1beta1/validatingadmissionpolicy.go | 243 + .../validatingadmissionpolicybinding.go | 197 + .../v1alpha1/fake/fake_storageversion.go | 5 +- .../apps/v1/fake/fake_controllerrevision.go | 59 +- .../typed/apps/v1/fake/fake_daemonset.go | 71 +- .../typed/apps/v1/fake/fake_deployment.go | 77 +- .../typed/apps/v1/fake/fake_replicaset.go | 77 +- .../typed/apps/v1/fake/fake_statefulset.go | 77 +- .../v1beta1/fake/fake_controllerrevision.go | 5 +- .../apps/v1beta1/fake/fake_deployment.go | 5 +- .../apps/v1beta1/fake/fake_statefulset.go | 5 +- .../v1beta2/fake/fake_controllerrevision.go | 5 +- .../typed/apps/v1beta2/fake/fake_daemonset.go | 5 +- .../apps/v1beta2/fake/fake_deployment.go | 5 +- .../apps/v1beta2/fake/fake_replicaset.go | 5 +- .../apps/v1beta2/fake/fake_statefulset.go | 5 +- .../v1/authentication_client.go | 5 + .../v1/fake/fake_authentication_client.go | 4 + .../v1/fake/fake_selfsubjectreview.go | 46 + .../v1/fake/fake_tokenreview.go | 5 +- .../authentication/v1/generated_expansion.go | 2 + .../authentication/v1/selfsubjectreview.go | 64 + .../v1alpha1/authentication_client.go | 107 + .../typed/authentication/v1alpha1/doc.go | 20 + .../typed/authentication/v1alpha1/fake/doc.go | 20 + .../fake/fake_authentication_client.go | 40 + .../v1alpha1/fake/fake_selfsubjectreview.go | 46 + .../v1alpha1/generated_expansion.go} | 8 +- .../v1alpha1/selfsubjectreview.go | 64 + .../v1beta1/authentication_client.go | 5 + .../fake/fake_authentication_client.go | 4 + .../v1beta1/fake/fake_selfsubjectreview.go | 46 + .../v1beta1/fake/fake_tokenreview.go | 5 +- .../v1beta1/generated_expansion.go | 2 + .../v1beta1/selfsubjectreview.go | 64 + .../v1/fake/fake_localsubjectaccessreview.go | 5 +- .../v1/fake/fake_selfsubjectaccessreview.go | 5 +- .../v1/fake/fake_selfsubjectrulesreview.go | 5 +- .../v1/fake/fake_subjectaccessreview.go | 5 +- .../fake/fake_localsubjectaccessreview.go | 5 +- .../fake/fake_selfsubjectaccessreview.go | 5 +- .../fake/fake_selfsubjectrulesreview.go | 5 +- .../v1beta1/fake/fake_subjectaccessreview.go | 5 +- .../v1/fake/fake_horizontalpodautoscaler.go | 71 +- .../v2/fake/fake_horizontalpodautoscaler.go | 5 +- .../fake/fake_horizontalpodautoscaler.go | 5 +- .../fake/fake_horizontalpodautoscaler.go | 5 +- .../typed/batch/v1/fake/fake_cronjob.go | 71 +- .../typed/batch/v1/fake/fake_job.go | 71 +- .../typed/batch/v1beta1/fake/fake_cronjob.go | 5 +- .../v1/fake/fake_certificatesigningrequest.go | 77 +- .../v1alpha1/certificates_client.go | 107 + .../v1alpha1/clustertrustbundle.go | 197 + .../typed/certificates/v1alpha1/doc.go | 20 + .../typed/certificates/v1alpha1/fake/doc.go | 20 + .../v1alpha1/fake/fake_certificates_client.go | 40 + .../v1alpha1/fake/fake_clustertrustbundle.go | 145 + .../v1alpha1/generated_expansion.go | 21 + .../fake/fake_certificatesigningrequest.go | 5 +- .../typed/coordination/v1/fake/fake_lease.go | 59 +- .../coordination/v1beta1/fake/fake_lease.go | 5 +- .../core/v1/fake/fake_componentstatus.go | 59 +- .../typed/core/v1/fake/fake_configmap.go | 59 +- .../typed/core/v1/fake/fake_endpoints.go | 59 +- .../typed/core/v1/fake/fake_event.go | 59 +- .../core/v1/fake/fake_event_expansion.go | 18 +- .../typed/core/v1/fake/fake_limitrange.go | 59 +- .../typed/core/v1/fake/fake_namespace.go | 67 +- .../typed/core/v1/fake/fake_node.go | 71 +- .../core/v1/fake/fake_persistentvolume.go | 71 +- .../v1/fake/fake_persistentvolumeclaim.go | 71 +- .../kubernetes/typed/core/v1/fake/fake_pod.go | 77 +- .../typed/core/v1/fake/fake_pod_expansion.go | 4 +- .../typed/core/v1/fake/fake_podtemplate.go | 59 +- .../v1/fake/fake_replicationcontroller.go | 75 +- .../typed/core/v1/fake/fake_resourcequota.go | 71 +- .../typed/core/v1/fake/fake_secret.go | 59 +- .../typed/core/v1/fake/fake_service.go | 67 +- .../typed/core/v1/fake/fake_serviceaccount.go | 61 +- .../discovery/v1/fake/fake_endpointslice.go | 59 +- .../v1beta1/fake/fake_endpointslice.go | 5 +- .../typed/events/v1/fake/fake_event.go | 59 +- .../typed/events/v1beta1/event_expansion.go | 2 +- .../typed/events/v1beta1/fake/fake_event.go | 5 +- .../extensions/v1beta1/extensions_client.go | 5 - .../extensions/v1beta1/fake/fake_daemonset.go | 5 +- .../v1beta1/fake/fake_deployment.go | 5 +- .../v1beta1/fake/fake_extensions_client.go | 4 - .../extensions/v1beta1/fake/fake_ingress.go | 5 +- .../v1beta1/fake/fake_networkpolicy.go | 40 +- .../v1beta1/fake/fake_podsecuritypolicy.go | 146 - .../v1beta1/fake/fake_replicaset.go | 5 +- .../extensions/v1beta1/generated_expansion.go | 2 - .../typed/extensions/v1beta1/networkpolicy.go | 48 - .../extensions/v1beta1/podsecuritypolicy.go | 197 - .../v1alpha1/fake/fake_flowschema.go | 5 +- .../fake/fake_prioritylevelconfiguration.go | 5 +- .../v1beta1/fake/fake_flowschema.go | 5 +- .../fake/fake_prioritylevelconfiguration.go | 5 +- .../v1beta2/fake/fake_flowschema.go | 5 +- .../fake/fake_prioritylevelconfiguration.go | 5 +- .../typed/flowcontrol/v1beta3/doc.go | 20 + .../typed/flowcontrol/v1beta3/fake/doc.go | 20 + .../v1beta3/fake/fake_flowcontrol_client.go | 44 + .../v1beta3/fake/fake_flowschema.go | 178 + .../fake/fake_prioritylevelconfiguration.go | 178 + .../flowcontrol/v1beta3/flowcontrol_client.go | 112 + .../typed/flowcontrol/v1beta3/flowschema.go | 243 + .../v1beta3/generated_expansion.go | 23 + .../v1beta3/prioritylevelconfiguration.go | 243 + .../typed/networking/v1/fake/fake_ingress.go | 71 +- .../networking/v1/fake/fake_ingressclass.go | 59 +- .../networking/v1/fake/fake_networkpolicy.go | 94 +- .../typed/networking/v1/networkpolicy.go | 48 - .../typed/networking/v1alpha1/clustercidr.go | 197 + .../typed/networking/v1alpha1/doc.go | 20 + .../typed/networking/v1alpha1/fake/doc.go | 20 + .../v1alpha1/fake/fake_clustercidr.go | 145 + .../v1alpha1/fake/fake_ipaddress.go | 145 + .../v1alpha1/fake/fake_networking_client.go | 44 + .../v1alpha1/generated_expansion.go | 23 + .../typed/networking/v1alpha1/ipaddress.go | 197 + .../networking/v1alpha1/networking_client.go | 112 + .../networking/v1beta1/fake/fake_ingress.go | 5 +- .../v1beta1/fake/fake_ingressclass.go | 5 +- .../typed/node/v1/fake/fake_runtimeclass.go | 59 +- .../node/v1alpha1/fake/fake_runtimeclass.go | 5 +- .../node/v1beta1/fake/fake_runtimeclass.go | 5 +- .../v1/fake/fake_poddisruptionbudget.go | 71 +- .../v1beta1/fake/fake_poddisruptionbudget.go | 5 +- .../v1beta1/fake/fake_podsecuritypolicy.go | 5 +- .../typed/rbac/v1/fake/fake_clusterrole.go | 59 +- .../rbac/v1/fake/fake_clusterrolebinding.go | 59 +- .../typed/rbac/v1/fake/fake_role.go | 59 +- .../typed/rbac/v1/fake/fake_rolebinding.go | 59 +- .../rbac/v1alpha1/fake/fake_clusterrole.go | 5 +- .../v1alpha1/fake/fake_clusterrolebinding.go | 5 +- .../typed/rbac/v1alpha1/fake/fake_role.go | 5 +- .../rbac/v1alpha1/fake/fake_rolebinding.go | 5 +- .../rbac/v1beta1/fake/fake_clusterrole.go | 5 +- .../v1beta1/fake/fake_clusterrolebinding.go | 5 +- .../typed/rbac/v1beta1/fake/fake_role.go | 5 +- .../rbac/v1beta1/fake/fake_rolebinding.go | 5 +- .../kubernetes/typed/resource/v1alpha2/doc.go | 20 + .../typed/resource/v1alpha2/fake/doc.go | 20 + .../fake/fake_podschedulingcontext.go | 189 + .../v1alpha2/fake/fake_resource_client.go | 52 + .../v1alpha2/fake/fake_resourceclaim.go | 189 + .../fake/fake_resourceclaimtemplate.go | 154 + .../v1alpha2/fake/fake_resourceclass.go | 145 + .../resource/v1alpha2/generated_expansion.go | 27 + .../resource/v1alpha2/podschedulingcontext.go | 256 + .../resource/v1alpha2/resource_client.go | 122 + .../typed/resource/v1alpha2/resourceclaim.go | 256 + .../v1alpha2/resourceclaimtemplate.go | 208 + .../typed/resource/v1alpha2/resourceclass.go | 197 + .../scheduling/v1/fake/fake_priorityclass.go | 59 +- .../v1alpha1/fake/fake_priorityclass.go | 5 +- .../v1beta1/fake/fake_priorityclass.go | 5 +- .../typed/storage/v1/fake/fake_csidriver.go | 59 +- .../typed/storage/v1/fake/fake_csinode.go | 59 +- .../v1/fake/fake_csistoragecapacity.go | 59 +- .../storage/v1/fake/fake_storageclass.go | 59 +- .../storage/v1/fake/fake_volumeattachment.go | 71 +- .../v1alpha1/fake/fake_csistoragecapacity.go | 5 +- .../v1alpha1/fake/fake_volumeattachment.go | 5 +- .../storage/v1beta1/fake/fake_csidriver.go | 5 +- .../storage/v1beta1/fake/fake_csinode.go | 5 +- .../v1beta1/fake/fake_csistoragecapacity.go | 5 +- .../storage/v1beta1/fake/fake_storageclass.go | 5 +- .../v1beta1/fake/fake_volumeattachment.go | 5 +- vendor/k8s.io/client-go/openapi/OWNERS | 4 + vendor/k8s.io/client-go/openapi/client.go | 7 +- .../k8s.io/client-go/openapi/groupversion.go | 47 +- .../k8s.io/client-go/openapi/typeconverter.go | 48 + .../pkg/apis/clientauthentication/types.go | 5 + .../pkg/apis/clientauthentication/v1/types.go | 5 + .../v1/zz_generated.conversion.go | 2 + .../clientauthentication/v1beta1/types.go | 5 + .../v1beta1/zz_generated.conversion.go | 2 + vendor/k8s.io/client-go/pkg/version/base.go | 3 +- .../plugin/pkg/client/auth/exec/exec.go | 19 +- vendor/k8s.io/client-go/rest/client.go | 13 +- vendor/k8s.io/client-go/rest/config.go | 20 +- vendor/k8s.io/client-go/rest/exec.go | 9 +- vendor/k8s.io/client-go/rest/plugin.go | 7 +- vendor/k8s.io/client-go/rest/request.go | 150 +- vendor/k8s.io/client-go/rest/transport.go | 5 +- vendor/k8s.io/client-go/rest/url_utils.go | 4 +- vendor/k8s.io/client-go/rest/warnings.go | 6 +- vendor/k8s.io/client-go/rest/with_retry.go | 61 +- vendor/k8s.io/client-go/testing/fixture.go | 20 +- .../k8s.io/client-go/tools/auth/clientauth.go | 33 +- vendor/k8s.io/client-go/tools/cache/OWNERS | 4 +- .../client-go/tools/cache/controller.go | 165 +- .../client-go/tools/cache/delta_fifo.go | 180 +- .../client-go/tools/cache/expiration_cache.go | 17 +- vendor/k8s.io/client-go/tools/cache/fifo.go | 23 +- vendor/k8s.io/client-go/tools/cache/index.go | 10 +- .../k8s.io/client-go/tools/cache/listers.go | 20 +- .../client-go/tools/cache/object-names.go | 65 + .../k8s.io/client-go/tools/cache/reflector.go | 738 +- .../tools/cache/retry_with_deadline.go | 78 + .../client-go/tools/cache/shared_informer.go | 325 +- vendor/k8s.io/client-go/tools/cache/store.go | 44 +- .../client-go/tools/cache/synctrack/lazy.go | 83 + .../tools/cache/synctrack/synctrack.go | 120 + .../tools/cache/thread_safe_store.go | 307 +- .../client-go/tools/clientcmd/api/helpers.go | 91 +- .../client-go/tools/clientcmd/api/types.go | 19 +- .../client-go/tools/clientcmd/api/v1/types.go | 5 + .../api/v1/zz_generated.conversion.go | 2 + .../client-go/tools/clientcmd/auth_loaders.go | 3 +- .../tools/clientcmd/client_config.go | 7 +- .../client-go/tools/clientcmd/loader.go | 39 +- .../client-go/tools/clientcmd/overrides.go | 42 +- .../client-go/tools/clientcmd/validation.go | 15 +- .../k8s.io/client-go/tools/metrics/metrics.go | 65 + vendor/k8s.io/client-go/tools/pager/pager.go | 41 +- .../k8s.io/client-go/tools/reference/ref.go | 2 +- vendor/k8s.io/client-go/transport/cache.go | 29 +- .../k8s.io/client-go/transport/cache_go118.go | 24 + vendor/k8s.io/client-go/transport/config.go | 14 +- .../client-go/transport/round_trippers.go | 10 +- .../client-go/transport/token_source.go | 4 +- .../k8s.io/client-go/transport/transport.go | 28 +- vendor/k8s.io/client-go/util/cert/cert.go | 44 +- vendor/k8s.io/client-go/util/cert/io.go | 7 +- vendor/k8s.io/client-go/util/keyutil/key.go | 9 +- .../util/workqueue/delaying_queue.go | 61 +- vendor/k8s.io/client-go/util/workqueue/doc.go | 14 +- .../client-go/util/workqueue/metrics.go | 9 +- .../k8s.io/client-go/util/workqueue/queue.go | 53 +- .../util/workqueue/rate_limiting_queue.go | 60 +- vendor/k8s.io/component-base/config/OWNERS | 13 - vendor/k8s.io/component-base/config/types.go | 212 - .../config/v1alpha1/conversion.go | 61 - .../config/v1alpha1/defaults.go | 128 - .../config/v1alpha1/register.go | 31 - .../component-base/config/v1alpha1/types.go | 143 - .../v1alpha1/zz_generated.conversion.go | 266 - .../config/v1alpha1/zz_generated.deepcopy.go | 180 - .../featuregate/feature_gate.go | 10 + .../k8s.io/component-base/logs/api/v1/doc.go | 32 + .../logs/{ => api/v1}/kube_features.go | 29 +- .../component-base/logs/api/v1/options.go | 420 + .../component-base/logs/api/v1/pflags.go | 113 + .../component-base/logs/api/v1/registry.go | 135 + .../component-base/logs/api/v1/types.go | 133 + .../api/v1}/zz_generated.deepcopy.go | 67 +- vendor/k8s.io/component-base/logs/config.go | 116 - .../setverbositylevel/setverbositylevel.go | 34 + .../logs/klogflags/klogflags.go | 41 + vendor/k8s.io/component-base/logs/logs.go | 105 +- vendor/k8s.io/component-base/logs/options.go | 107 - .../component-base/logs/registry/registry.go | 110 - vendor/k8s.io/component-base/logs/validate.go | 70 - .../k8s.io/component-base/metrics/buckets.go | 43 + .../component-base/metrics/collector.go | 6 +- .../k8s.io/component-base/metrics/counter.go | 21 +- vendor/k8s.io/component-base/metrics/desc.go | 4 +- vendor/k8s.io/component-base/metrics/gauge.go | 73 +- .../component-base/metrics/histogram.go | 30 +- vendor/k8s.io/component-base/metrics/http.go | 18 +- .../metrics/legacyregistry/registry.go | 19 +- .../k8s.io/component-base/metrics/metric.go | 64 +- vendor/k8s.io/component-base/metrics/opts.go | 55 + .../metrics/prometheus/feature/metrics.go | 53 + .../metrics/prometheus/restclient/metrics.go | 95 +- .../prometheusextension/timing_histogram.go | 189 + .../timing_histogram_vec.go | 111 + .../prometheusextension/weighted_histogram.go | 203 + .../weighted_histogram_vec.go | 106 + .../k8s.io/component-base/metrics/registry.go | 56 +- .../k8s.io/component-base/metrics/summary.go | 24 +- .../metrics/timing_histogram.go | 270 + vendor/k8s.io/component-base/metrics/value.go | 12 +- .../k8s.io/component-base/metrics/wrappers.go | 72 + .../k8s.io/component-base/version/dynamic.go | 77 + .../k8s.io/component-base/version/version.go | 2 +- vendor/k8s.io/klog/v2/.golangci.yaml | 6 + vendor/k8s.io/klog/v2/OWNERS | 4 +- vendor/k8s.io/klog/v2/contextual.go | 30 +- vendor/k8s.io/klog/v2/contextual_slog.go | 31 + vendor/k8s.io/klog/v2/format.go | 65 + .../k8s.io/klog/v2/internal/buffer/buffer.go | 87 +- vendor/k8s.io/klog/v2/internal/clock/clock.go | 21 +- .../klog/v2/internal/serialize/keyvalues.go | 175 +- .../internal/serialize/keyvalues_no_slog.go | 97 + .../v2/internal/serialize/keyvalues_slog.go | 155 + .../internal/sloghandler/sloghandler_slog.go | 96 + vendor/k8s.io/klog/v2/k8s_references.go | 78 +- vendor/k8s.io/klog/v2/k8s_references_slog.go | 39 + vendor/k8s.io/klog/v2/klog.go | 237 +- vendor/k8s.io/klog/v2/klog_file.go | 4 +- vendor/k8s.io/klog/v2/klogr.go | 54 +- vendor/k8s.io/klog/v2/klogr_slog.go | 96 + vendor/k8s.io/klog/v2/safeptr.go | 34 + .../kube-openapi/pkg/builder3/util/util.go | 51 - .../k8s.io/kube-openapi/pkg/cached/cache.go | 290 + .../k8s.io/kube-openapi/pkg/common/common.go | 86 +- .../kube-openapi/pkg/handler3/handler.go | 246 +- .../k8s.io/kube-openapi/pkg/internal/flags.go | 5 + .../pkg/internal/handler/handler_cache.go | 57 - .../pkg/internal/serialization.go | 65 + .../go-json-experiment/json/arshal.go | 7 + .../go-json-experiment/json/arshal_any.go | 31 +- .../go-json-experiment/json/arshal_default.go | 147 +- .../go-json-experiment/json/arshal_inlined.go | 57 +- .../go-json-experiment/json/arshal_methods.go | 4 +- .../go-json-experiment/json/arshal_time.go | 99 +- .../go-json-experiment/json/decode.go | 12 +- .../go-json-experiment/json/doc.go | 9 +- .../go-json-experiment/json/encode.go | 24 + .../go-json-experiment/json/pools.go | 32 + .../go-json-experiment/json/state.go | 4 +- .../go-json-experiment/json/token.go | 10 +- .../go-json-experiment/json/value.go | 56 +- .../kube-openapi/pkg/openapiconv/convert.go | 322 - .../kube-openapi/pkg/schemaconv/openapi.go | 260 + .../pkg/schemaconv/proto_models.go | 178 + .../k8s.io/kube-openapi/pkg/schemaconv/smd.go | 306 +- .../kube-openapi/pkg/schemamutation/walker.go | 519 - .../k8s.io/kube-openapi/pkg/spec3/encoding.go | 43 +- .../k8s.io/kube-openapi/pkg/spec3/example.go | 39 +- .../pkg/spec3/external_documentation.go | 34 +- vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go | 281 + .../k8s.io/kube-openapi/pkg/spec3/header.go | 52 + .../kube-openapi/pkg/spec3/media_type.go | 42 +- .../kube-openapi/pkg/spec3/operation.go | 49 +- .../kube-openapi/pkg/spec3/parameter.go | 53 + vendor/k8s.io/kube-openapi/pkg/spec3/path.go | 129 +- .../kube-openapi/pkg/spec3/request_body.go | 44 +- .../k8s.io/kube-openapi/pkg/spec3/response.go | 173 +- .../pkg/spec3/security_requirement.go | 56 - .../kube-openapi/pkg/spec3/security_scheme.go | 19 +- .../k8s.io/kube-openapi/pkg/spec3/server.go | 67 +- vendor/k8s.io/kube-openapi/pkg/spec3/spec.go | 38 + .../kube-openapi/pkg/util/proto/document.go | 2 +- .../pkg/util/proto/document_v3.go | 6 +- .../kube-openapi/pkg/validation/spec/fuzz.go | 502 - .../pkg/validation/spec/gnostic.go | 2 +- .../pkg/validation/spec/header.go | 23 +- .../kube-openapi/pkg/validation/spec/info.go | 33 +- .../kube-openapi/pkg/validation/spec/items.go | 53 +- .../pkg/validation/spec/operation.go | 36 +- .../pkg/validation/spec/parameter.go | 36 +- .../pkg/validation/spec/path_item.go | 28 +- .../kube-openapi/pkg/validation/spec/paths.go | 24 +- .../kube-openapi/pkg/validation/spec/ref.go | 18 +- .../pkg/validation/spec/response.go | 36 +- .../pkg/validation/spec/responses.go | 24 +- .../pkg/validation/spec/schema.go | 79 +- .../pkg/validation/spec/security_scheme.go | 20 +- .../pkg/validation/spec/swagger.go | 82 +- .../kube-openapi/pkg/validation/spec/tag.go | 19 +- .../kubernetes/pkg/util/iptables/OWNERS | 10 +- .../kubernetes/pkg/util/iptables/iptables.go | 29 +- vendor/k8s.io/utils/net/ipfamily.go | 181 + vendor/k8s.io/utils/net/net.go | 126 +- vendor/k8s.io/utils/net/port.go | 18 +- vendor/k8s.io/utils/pointer/pointer.go | 283 +- vendor/k8s.io/utils/ptr/OWNERS | 10 + vendor/k8s.io/utils/ptr/README.md | 3 + vendor/k8s.io/utils/ptr/ptr.go | 73 + vendor/k8s.io/utils/trace/trace.go | 19 + vendor/modules.txt | 488 +- .../internal/golang/encoding/json/decode.go | 5 +- .../internal/golang/encoding/json/encode.go | 37 +- .../internal/golang/encoding/json/fold.go | 5 +- .../internal/golang/encoding/json/scanner.go | 2 + .../internal/golang/encoding/json/stream.go | 1 - vendor/sigs.k8s.io/json/json.go | 28 +- .../v4/fieldpath/pathelementmap.go | 45 +- .../v4/merge/conflict.go | 121 + .../structured-merge-diff/v4/merge/update.go | 358 + .../v4/schema/elements.go | 3 +- .../v4/schema/schemaschema.go | 3 +- .../structured-merge-diff/v4/typed/compare.go | 460 + .../structured-merge-diff/v4/typed/helpers.go | 21 +- .../structured-merge-diff/v4/typed/merge.go | 61 +- .../structured-merge-diff/v4/typed/parser.go | 12 +- .../structured-merge-diff/v4/typed/remove.go | 4 +- .../v4/typed/tofieldset.go | 24 +- .../structured-merge-diff/v4/typed/typed.go | 187 +- .../structured-merge-diff/v4/typed/union.go | 276 - .../v4/typed/validate.go | 14 +- .../v4/value/mapreflect.go | 2 +- .../v4/value/mapunstructured.go | 8 +- .../v4/value/reflectcache.go | 4 +- 2825 files changed, 250264 insertions(+), 58096 deletions(-) create mode 100644 vendor/github.com/DataDog/appsec-internal-go/LICENSE create mode 100644 vendor/github.com/DataDog/appsec-internal-go/appsec/config.go create mode 100644 vendor/github.com/DataDog/appsec-internal-go/appsec/embed.go create mode 100644 vendor/github.com/DataDog/appsec-internal-go/appsec/processors.json create mode 100644 vendor/github.com/DataDog/appsec-internal-go/appsec/rules.go create mode 100644 vendor/github.com/DataDog/appsec-internal-go/appsec/rules.json rename vendor/{gopkg.in/DataDog/dd-trace-go.v1/internal/appsec => github.com/DataDog/appsec-internal-go/limiter}/limiter.go (70%) create mode 100644 vendor/github.com/DataDog/appsec-internal-go/log/backend.go create mode 100644 vendor/github.com/DataDog/appsec-internal-go/log/log.go delete mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/README.md create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/agent_config.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_agent_task.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_asm.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/products.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go create mode 100644 vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/README.md delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/aggregator.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/buffer.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/event.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/format.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/metrics.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/noop.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/options.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/pipe.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/sender.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/service_check.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/statsd.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/telemetry.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/udp.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/uds.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/utils.go delete mode 100644 vendor/github.com/DataDog/datadog-go/statsd/worker.go rename vendor/{gopkg.in/DataDog/dd-trace-go.v1/internal => github.com/DataDog/datadog-go/v5/statsd}/container.go (68%) create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/.gitattributes create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/.gitignore create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/LICENSE create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/README.md create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/cgo_ref_pool.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/context.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/ctypes.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/decoder.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/encoder.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/handle.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/.version create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/README.md rename vendor/{gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/vendor.go => github.com/DataDog/go-libddwaf/v2/internal/lib/doc.go} (59%) create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_amd64.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_arm64.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_amd64.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_arm64.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-amd64.dylib create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-arm64.dylib create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-amd64.so create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-arm64.so rename vendor/{gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include => github.com/DataDog/go-libddwaf/v2/internal/log}/ddwaf.h (63%) create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/log/gostring.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_cgo.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_purego.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_unsupported.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.go rename vendor/{gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64/vendor.go => github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.s} (61%) create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/safe.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_cgo.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_purego.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_cgo_disabled.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_dl.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_dl_unsupported.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_manually_disabled.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_support.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_go.go create mode 100644 vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_target.go create mode 100644 vendor/github.com/DataDog/go-tuf/LICENSE create mode 100644 vendor/github.com/DataDog/go-tuf/client/client.go create mode 100644 vendor/github.com/DataDog/go-tuf/client/delegations.go create mode 100644 vendor/github.com/DataDog/go-tuf/client/errors.go create mode 100644 vendor/github.com/DataDog/go-tuf/client/file_store.go create mode 100644 vendor/github.com/DataDog/go-tuf/client/local_store.go create mode 100644 vendor/github.com/DataDog/go-tuf/client/remote_store.go create mode 100644 vendor/github.com/DataDog/go-tuf/data/hex_bytes.go create mode 100644 vendor/github.com/DataDog/go-tuf/data/types.go create mode 100644 vendor/github.com/DataDog/go-tuf/internal/roles/roles.go create mode 100644 vendor/github.com/DataDog/go-tuf/internal/sets/strings.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/deprecated_ecdsa.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/ecdsa.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/ed25519.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/keys.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/pkix.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/keys/rsa.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/targets/delegation.go create mode 100644 vendor/github.com/DataDog/go-tuf/pkg/targets/hash_bins.go create mode 100644 vendor/github.com/DataDog/go-tuf/util/util.go create mode 100644 vendor/github.com/DataDog/go-tuf/verify/db.go create mode 100644 vendor/github.com/DataDog/go-tuf/verify/errors.go create mode 100644 vendor/github.com/DataDog/go-tuf/verify/verify.go create mode 100644 vendor/github.com/Microsoft/go-winio/.gitattributes create mode 100644 vendor/github.com/Microsoft/go-winio/.golangci.yml create mode 100644 vendor/github.com/Microsoft/go-winio/SECURITY.md create mode 100644 vendor/github.com/Microsoft/go-winio/doc.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/doc.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/fs.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/security.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/socket.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go create mode 100644 vendor/github.com/Microsoft/go-winio/tools.go create mode 100644 vendor/github.com/coredns/coredns/core/dnsserver/quic.go create mode 100644 vendor/github.com/coredns/coredns/core/dnsserver/server_quic.go create mode 100644 vendor/github.com/coredns/coredns/plugin/loadbalance/weighted.go rename vendor/github.com/coredns/coredns/plugin/{forward => pkg/proxy}/connect.go (64%) create mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/proxy/errors.go rename vendor/github.com/coredns/coredns/plugin/{forward => pkg/proxy}/health.go (63%) create mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/proxy/metrics.go rename vendor/github.com/coredns/coredns/plugin/{forward => pkg/proxy}/persistent.go (92%) rename vendor/github.com/coredns/coredns/plugin/{forward => pkg/proxy}/proxy.go (53%) rename vendor/github.com/coredns/coredns/plugin/{forward => pkg/proxy}/type.go (94%) create mode 100644 vendor/github.com/coredns/coredns/plugin/rewrite/cname_target.go create mode 100644 vendor/github.com/coredns/coredns/plugin/rewrite/rcode.go create mode 100644 vendor/github.com/ebitengine/purego/.gitignore create mode 100644 vendor/github.com/ebitengine/purego/LICENSE create mode 100644 vendor/github.com/ebitengine/purego/README.md create mode 100644 vendor/github.com/ebitengine/purego/abi_amd64.h create mode 100644 vendor/github.com/ebitengine/purego/abi_arm64.h create mode 100644 vendor/github.com/ebitengine/purego/cgo.go create mode 100644 vendor/github.com/ebitengine/purego/dlerror.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_darwin.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_freebsd.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_linux.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_nocgo_freebsd.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_nocgo_linux.go create mode 100644 vendor/github.com/ebitengine/purego/dlfcn_stubs.s create mode 100644 vendor/github.com/ebitengine/purego/func.go create mode 100644 vendor/github.com/ebitengine/purego/go_runtime.go create mode 100644 vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go create mode 100644 vendor/github.com/ebitengine/purego/internal/cgo/empty.go create mode 100644 vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/abi_amd64.h create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/abi_arm64.h create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/asm_amd64.s create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/asm_arm64.s create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/freebsd.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s create mode 100644 vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s create mode 100644 vendor/github.com/ebitengine/purego/internal/strings/strings.go create mode 100644 vendor/github.com/ebitengine/purego/is_ios.go create mode 100644 vendor/github.com/ebitengine/purego/nocgo.go create mode 100644 vendor/github.com/ebitengine/purego/struct_amd64.go create mode 100644 vendor/github.com/ebitengine/purego/struct_arm64.go create mode 100644 vendor/github.com/ebitengine/purego/struct_other.go create mode 100644 vendor/github.com/ebitengine/purego/sys_amd64.s create mode 100644 vendor/github.com/ebitengine/purego/sys_arm64.s create mode 100644 vendor/github.com/ebitengine/purego/sys_unix_arm64.s create mode 100644 vendor/github.com/ebitengine/purego/syscall.go create mode 100644 vendor/github.com/ebitengine/purego/syscall_cgo_linux.go create mode 100644 vendor/github.com/ebitengine/purego/syscall_sysv.go create mode 100644 vendor/github.com/ebitengine/purego/syscall_windows.go create mode 100644 vendor/github.com/ebitengine/purego/zcallback_amd64.s create mode 100644 vendor/github.com/ebitengine/purego/zcallback_arm64.s create mode 100644 vendor/github.com/go-logr/logr/SECURITY.md create mode 100644 vendor/github.com/go-logr/logr/context.go create mode 100644 vendor/github.com/go-logr/logr/context_noslog.go create mode 100644 vendor/github.com/go-logr/logr/context_slog.go create mode 100644 vendor/github.com/go-logr/logr/sloghandler.go create mode 100644 vendor/github.com/go-logr/logr/slogr.go create mode 100644 vendor/github.com/go-logr/logr/slogsink.go delete mode 100644 vendor/github.com/go-openapi/jsonpointer/.travis.yml delete mode 100644 vendor/github.com/go-openapi/jsonreference/.travis.yml create mode 100644 vendor/github.com/go-openapi/swag/.gitattributes delete mode 100644 vendor/github.com/go-openapi/swag/.travis.yml create mode 100644 vendor/github.com/go-openapi/swag/file.go delete mode 100644 vendor/github.com/golang/glog/LICENSE delete mode 100644 vendor/github.com/golang/glog/README.md delete mode 100644 vendor/github.com/golang/glog/glog.go delete mode 100644 vendor/github.com/golang/glog/glog_file.go delete mode 100644 vendor/github.com/golang/glog/glog_flags.go delete mode 100644 vendor/github.com/golang/glog/internal/logsink/logsink.go delete mode 100644 vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go delete mode 100644 vendor/github.com/golang/glog/internal/stackdump/stackdump.go delete mode 100644 vendor/github.com/golang/protobuf/jsonpb/decode.go delete mode 100644 vendor/github.com/golang/protobuf/jsonpb/encode.go delete mode 100644 vendor/github.com/golang/protobuf/jsonpb/json.go rename vendor/github.com/google/{gnostic => gnostic-models}/LICENSE (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/README.md (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/context.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/error.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/extensions.go (97%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/helpers.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/main.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/compiler/reader.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/extensions/README.md (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/extensions/extension.pb.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/extensions/extension.proto (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/extensions/extensions.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/README.md (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/base.go (90%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/display.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/models.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/operations.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/reader.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/schema.json (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/jsonschema/writer.go (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/OpenAPIv2.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/OpenAPIv2.pb.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/OpenAPIv2.proto (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/README.md (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/document.go (96%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv2/openapi-2.0.json (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv3/OpenAPIv3.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv3/OpenAPIv3.pb.go (99%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv3/OpenAPIv3.proto (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv3/README.md (100%) rename vendor/github.com/google/{gnostic => gnostic-models}/openapiv3/document.go (96%) delete mode 100644 vendor/github.com/google/gnostic/openapiv3/openapi-3.0.json delete mode 100644 vendor/github.com/google/gnostic/openapiv3/openapi-3.1.json rename vendor/github.com/google/go-cmp/cmp/{export_unsafe.go => export.go} (94%) delete mode 100644 vendor/github.com/google/go-cmp/cmp/export_panic.go rename vendor/github.com/google/go-cmp/cmp/internal/value/{pointer_unsafe.go => pointer.go} (95%) delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go create mode 100644 vendor/github.com/google/pprof/AUTHORS create mode 100644 vendor/github.com/google/pprof/CONTRIBUTORS rename vendor/{google.golang.org/genproto => github.com/google/pprof}/LICENSE (100%) create mode 100644 vendor/github.com/google/pprof/profile/encode.go create mode 100644 vendor/github.com/google/pprof/profile/filter.go create mode 100644 vendor/github.com/google/pprof/profile/index.go create mode 100644 vendor/github.com/google/pprof/profile/legacy_java_profile.go create mode 100644 vendor/github.com/google/pprof/profile/legacy_profile.go create mode 100644 vendor/github.com/google/pprof/profile/merge.go create mode 100644 vendor/github.com/google/pprof/profile/profile.go create mode 100644 vendor/github.com/google/pprof/profile/proto.go create mode 100644 vendor/github.com/google/pprof/profile/prune.go delete mode 100644 vendor/github.com/google/uuid/.travis.yml create mode 100644 vendor/github.com/google/uuid/CHANGELOG.md create mode 100644 vendor/github.com/google/uuid/version6.go create mode 100644 vendor/github.com/google/uuid/version7.go create mode 100644 vendor/github.com/hashicorp/errwrap/LICENSE create mode 100644 vendor/github.com/hashicorp/errwrap/README.md create mode 100644 vendor/github.com/hashicorp/errwrap/errwrap.go create mode 100644 vendor/github.com/hashicorp/go-multierror/LICENSE create mode 100644 vendor/github.com/hashicorp/go-multierror/Makefile create mode 100644 vendor/github.com/hashicorp/go-multierror/README.md create mode 100644 vendor/github.com/hashicorp/go-multierror/append.go create mode 100644 vendor/github.com/hashicorp/go-multierror/flatten.go create mode 100644 vendor/github.com/hashicorp/go-multierror/format.go create mode 100644 vendor/github.com/hashicorp/go-multierror/group.go create mode 100644 vendor/github.com/hashicorp/go-multierror/multierror.go create mode 100644 vendor/github.com/hashicorp/go-multierror/prefix.go create mode 100644 vendor/github.com/hashicorp/go-multierror/sort.go delete mode 100644 vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go delete mode 100644 vendor/github.com/mailru/easyjson/.gitignore delete mode 100644 vendor/github.com/mailru/easyjson/.travis.yml delete mode 100644 vendor/github.com/mailru/easyjson/Makefile delete mode 100644 vendor/github.com/mailru/easyjson/README.md delete mode 100644 vendor/github.com/mailru/easyjson/helpers.go delete mode 100644 vendor/github.com/mailru/easyjson/raw.go delete mode 100644 vendor/github.com/mailru/easyjson/unknown_fields.go delete mode 100644 vendor/github.com/miekg/dns/singleinflight.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/LICENSE create mode 100644 vendor/github.com/onsi/ginkgo/v2/config/deprecated.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/code_location.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/config.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/enum_support.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/errors.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/file_filter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/flags.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/label_filter.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/report_entry.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/types.go create mode 100644 vendor/github.com/onsi/ginkgo/v2/types/version.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_exact_elements.go rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/.deepsource.toml (100%) create mode 100644 vendor/github.com/outcaste-io/ristretto/.mailmap rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/CHANGELOG.md (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/LICENSE (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/README.md (82%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/cache.go (72%) create mode 100644 vendor/github.com/outcaste-io/ristretto/metrics.go rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/policy.go (67%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/ring.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/sketch.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/store.go (73%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/test.sh (77%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/ttl.go (93%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/LICENSE (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/README.md (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/allocator.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/bbloom.go (97%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/btree.go (99%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/buffer.go (99%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/calloc.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/calloc_32bit.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/calloc_64bit.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/calloc_jemalloc.go (94%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/calloc_nojemalloc.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/file.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/file_default.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/file_linux.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/flags.go (92%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/histogram.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap_darwin.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap_linux.go (93%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap_plan9.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap_unix.go (93%) rename vendor/{google.golang.org/grpc/internal/channelz/util_linux.go => github.com/outcaste-io/ristretto/z/mmap_wasip1.go} (61%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/mmap_windows.go (96%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/rtutil.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/rtutil.s (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/simd/baseline.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/simd/search.go (96%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/simd/search_amd64.s (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/simd/stub_search_amd64.go (100%) rename vendor/github.com/{dgraph-io => outcaste-io}/ristretto/z/z.go (94%) create mode 100644 vendor/github.com/philhofer/fwd/writer_tinygo.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/collectors.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/dbstats_collector.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/expvar_collector.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_go116.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_latest.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/collectors/process_collector.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/vnext.go create mode 100644 vendor/github.com/prometheus/common/model/labelset_string.go create mode 100644 vendor/github.com/prometheus/common/model/labelset_string_go120.go create mode 100644 vendor/github.com/prometheus/common/model/metadata.go create mode 100644 vendor/github.com/prometheus/common/model/value_float.go create mode 100644 vendor/github.com/prometheus/common/model/value_histogram.go create mode 100644 vendor/github.com/prometheus/common/model/value_type.go create mode 100644 vendor/github.com/prometheus/procfs/cpuinfo_loong64.go create mode 100644 vendor/github.com/prometheus/procfs/fs_statfs_notype.go create mode 100644 vendor/github.com/prometheus/procfs/fs_statfs_type.go create mode 100644 vendor/github.com/prometheus/procfs/net_route.go create mode 100644 vendor/github.com/prometheus/procfs/net_wireless.go create mode 100644 vendor/github.com/prometheus/procfs/proc_interrupts.go create mode 100644 vendor/github.com/prometheus/procfs/thread.go create mode 100644 vendor/github.com/quic-go/quic-go/.gitignore create mode 100644 vendor/github.com/quic-go/quic-go/.golangci.yml create mode 100644 vendor/github.com/quic-go/quic-go/Changelog.md create mode 100644 vendor/github.com/quic-go/quic-go/LICENSE create mode 100644 vendor/github.com/quic-go/quic-go/README.md create mode 100644 vendor/github.com/quic-go/quic-go/SECURITY.md create mode 100644 vendor/github.com/quic-go/quic-go/buffer_pool.go create mode 100644 vendor/github.com/quic-go/quic-go/client.go create mode 100644 vendor/github.com/quic-go/quic-go/closed_conn.go create mode 100644 vendor/github.com/quic-go/quic-go/codecov.yml create mode 100644 vendor/github.com/quic-go/quic-go/config.go create mode 100644 vendor/github.com/quic-go/quic-go/conn_id_generator.go create mode 100644 vendor/github.com/quic-go/quic-go/conn_id_manager.go create mode 100644 vendor/github.com/quic-go/quic-go/connection.go create mode 100644 vendor/github.com/quic-go/quic-go/connection_timer.go create mode 100644 vendor/github.com/quic-go/quic-go/crypto_stream.go create mode 100644 vendor/github.com/quic-go/quic-go/crypto_stream_manager.go create mode 100644 vendor/github.com/quic-go/quic-go/datagram_queue.go create mode 100644 vendor/github.com/quic-go/quic-go/errors.go create mode 100644 vendor/github.com/quic-go/quic-go/frame_sorter.go create mode 100644 vendor/github.com/quic-go/quic-go/framer.go create mode 100644 vendor/github.com/quic-go/quic-go/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/clock.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/aead.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/conn.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/retry.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/logutils/frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/params.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/stream.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/protocol/version.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/qerr/errors.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/qtls/qtls.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/ip.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/log.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/minmax.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/rand.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/ringbuffer/ringbuffer.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/utils/timer.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/header.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/log.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/pool.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/short_header.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go create mode 100644 vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/connection_tracer.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/frame.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/interface.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/packet_header.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/tracer.go create mode 100644 vendor/github.com/quic-go/quic-go/logging/types.go create mode 100644 vendor/github.com/quic-go/quic-go/mockgen.go create mode 100644 vendor/github.com/quic-go/quic-go/mtu_discoverer.go create mode 100644 vendor/github.com/quic-go/quic-go/multiplexer.go create mode 100644 vendor/github.com/quic-go/quic-go/oss-fuzz.sh create mode 100644 vendor/github.com/quic-go/quic-go/packet_handler_map.go create mode 100644 vendor/github.com/quic-go/quic-go/packet_packer.go create mode 100644 vendor/github.com/quic-go/quic-go/packet_unpacker.go create mode 100644 vendor/github.com/quic-go/quic-go/quicvarint/io.go create mode 100644 vendor/github.com/quic-go/quic-go/quicvarint/varint.go create mode 100644 vendor/github.com/quic-go/quic-go/receive_stream.go create mode 100644 vendor/github.com/quic-go/quic-go/retransmission_queue.go create mode 100644 vendor/github.com/quic-go/quic-go/send_conn.go create mode 100644 vendor/github.com/quic-go/quic-go/send_queue.go create mode 100644 vendor/github.com/quic-go/quic-go/send_stream.go create mode 100644 vendor/github.com/quic-go/quic-go/server.go create mode 100644 vendor/github.com/quic-go/quic-go/stream.go create mode 100644 vendor/github.com/quic-go/quic-go/streams_map.go create mode 100644 vendor/github.com/quic-go/quic-go/streams_map_incoming.go create mode 100644 vendor/github.com/quic-go/quic-go/streams_map_outgoing.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_buffers.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_df.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_oob.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_windows.go create mode 100644 vendor/github.com/quic-go/quic-go/token_store.go rename vendor/github.com/{onsi/gomega/tools => quic-go/quic-go/tools.go} (61%) create mode 100644 vendor/github.com/quic-go/quic-go/transport.go create mode 100644 vendor/github.com/quic-go/quic-go/window_update_queue.go rename vendor/github.com/{DataDog/datadog-go/LICENSE.txt => secure-systems-lab/go-securesystemslib/LICENSE} (94%) create mode 100644 vendor/github.com/secure-systems-lab/go-securesystemslib/cjson/canonicaljson.go create mode 100644 vendor/github.com/tinylib/msgp/msgp/elsize_default.go create mode 100644 vendor/github.com/tinylib/msgp/msgp/elsize_tinygo.go create mode 100644 vendor/github.com/tinylib/msgp/msgp/errors_default.go create mode 100644 vendor/github.com/tinylib/msgp/msgp/errors_tinygo.go create mode 100644 vendor/github.com/vishvananda/netns/.golangci.yml create mode 100644 vendor/github.com/vishvananda/netns/doc.go rename vendor/github.com/vishvananda/netns/{netns_unspecified.go => netns_others.go} (63%) rename vendor/github.com/vishvananda/netns/{netns.go => nshandle_linux.go} (75%) create mode 100644 vendor/github.com/vishvananda/netns/nshandle_others.go create mode 100644 vendor/go.uber.org/atomic/float32.go create mode 100644 vendor/go.uber.org/atomic/float32_ext.go create mode 100644 vendor/go.uber.org/atomic/pointer_go118.go create mode 100644 vendor/go.uber.org/atomic/pointer_go118_pre119.go create mode 100644 vendor/go.uber.org/atomic/pointer_go119.go create mode 100644 vendor/go.uber.org/mock/AUTHORS create mode 100644 vendor/go.uber.org/mock/LICENSE create mode 100644 vendor/go.uber.org/mock/mockgen/generic_go118.go create mode 100644 vendor/go.uber.org/mock/mockgen/generic_notgo118.go create mode 100644 vendor/go.uber.org/mock/mockgen/mockgen.go create mode 100644 vendor/go.uber.org/mock/mockgen/model/model.go create mode 100644 vendor/go.uber.org/mock/mockgen/parse.go create mode 100644 vendor/go.uber.org/mock/mockgen/reflect.go create mode 100644 vendor/go.uber.org/mock/mockgen/version.go delete mode 100644 vendor/go.uber.org/multierr/.travis.yml create mode 100644 vendor/go.uber.org/multierr/error_post_go120.go rename vendor/go.uber.org/multierr/{go113.go => error_pre_go120.go} (66%) delete mode 100644 vendor/go.uber.org/multierr/glide.yaml create mode 100644 vendor/golang.org/x/crypto/LICENSE create mode 100644 vendor/golang.org/x/crypto/PATENTS create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_arm64.go create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_arm64.s create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_generic.go create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_noasm.go create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_s390x.go create mode 100644 vendor/golang.org/x/crypto/chacha20/chacha_s390x.s create mode 100644 vendor/golang.org/x/crypto/chacha20/xor.go create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go create mode 100644 vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go create mode 100644 vendor/golang.org/x/crypto/hkdf/hkdf.go create mode 100644 vendor/golang.org/x/crypto/internal/alias/alias.go create mode 100644 vendor/golang.org/x/crypto/internal/alias/alias_purego.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/poly1305.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s create mode 100644 vendor/golang.org/x/exp/LICENSE create mode 100644 vendor/golang.org/x/exp/PATENTS create mode 100644 vendor/golang.org/x/exp/rand/exp.go create mode 100644 vendor/golang.org/x/exp/rand/normal.go create mode 100644 vendor/golang.org/x/exp/rand/rand.go create mode 100644 vendor/golang.org/x/exp/rand/rng.go create mode 100644 vendor/golang.org/x/exp/rand/zipf.go create mode 100644 vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go create mode 100644 vendor/golang.org/x/mod/modfile/print.go create mode 100644 vendor/golang.org/x/mod/modfile/read.go create mode 100644 vendor/golang.org/x/mod/modfile/rule.go create mode 100644 vendor/golang.org/x/mod/modfile/work.go create mode 100644 vendor/golang.org/x/mod/module/module.go create mode 100644 vendor/golang.org/x/mod/module/pseudo.go delete mode 100644 vendor/golang.org/x/net/http2/go111.go delete mode 100644 vendor/golang.org/x/net/http2/go115.go delete mode 100644 vendor/golang.org/x/net/http2/go118.go delete mode 100644 vendor/golang.org/x/net/http2/not_go111.go delete mode 100644 vendor/golang.org/x/net/http2/not_go115.go delete mode 100644 vendor/golang.org/x/net/http2/not_go118.go create mode 100644 vendor/golang.org/x/net/http2/testsync.go create mode 100644 vendor/golang.org/x/oauth2/deviceauth.go create mode 100644 vendor/golang.org/x/oauth2/pkce.go create mode 100644 vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s create mode 100644 vendor/golang.org/x/sys/cpu/byteorder.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_aix.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_arm64.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gc_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c create mode 100644 vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_loong64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_mipsx.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_arm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_ppc64x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_riscv64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_s390x.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_wasm.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_x86.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_zos.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_zos_s390x.go create mode 100644 vendor/golang.org/x/sys/cpu/endian_big.go create mode 100644 vendor/golang.org/x/sys/cpu/endian_little.go create mode 100644 vendor/golang.org/x/sys/cpu/hwcap_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/parse.go create mode 100644 vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go create mode 100644 vendor/golang.org/x/sys/cpu/runtime_auxv.go create mode 100644 vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go delete mode 100644 vendor/golang.org/x/sys/execabs/execabs.go delete mode 100644 vendor/golang.org/x/sys/execabs/execabs_go118.go delete mode 100644 vendor/golang.org/x/sys/execabs/execabs_go119.go delete mode 100644 vendor/golang.org/x/time/AUTHORS delete mode 100644 vendor/golang.org/x/time/CONTRIBUTORS create mode 100644 vendor/golang.org/x/time/rate/sometimes.go create mode 100644 vendor/golang.org/x/tools/cmd/stringer/stringer.go create mode 100644 vendor/golang.org/x/tools/go/ast/astutil/enclosing.go create mode 100644 vendor/golang.org/x/tools/go/ast/astutil/imports.go create mode 100644 vendor/golang.org/x/tools/go/ast/astutil/rewrite.go create mode 100644 vendor/golang.org/x/tools/go/ast/astutil/util.go create mode 100644 vendor/golang.org/x/tools/go/types/objectpath/objectpath.go create mode 100644 vendor/golang.org/x/tools/imports/forward.go create mode 100644 vendor/golang.org/x/tools/internal/event/keys/util.go create mode 100644 vendor/golang.org/x/tools/internal/event/tag/tag.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/bexport.go create mode 100644 vendor/golang.org/x/tools/internal/gopathwalk/walk.go create mode 100644 vendor/golang.org/x/tools/internal/imports/fix.go create mode 100644 vendor/golang.org/x/tools/internal/imports/imports.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mod.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mod_cache.go create mode 100644 vendor/golang.org/x/tools/internal/imports/sortimports.go create mode 100644 vendor/golang.org/x/tools/internal/imports/zstdlib.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go create mode 100644 vendor/golang.org/x/tools/internal/versions/gover.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types_go121.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types_go122.go create mode 100644 vendor/golang.org/x/tools/internal/versions/versions.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/LICENSE create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/rpc/LICENSE delete mode 100644 vendor/google.golang.org/grpc/balancer_conn_wrappers.go create mode 100644 vendor/google.golang.org/grpc/balancer_wrapper.go delete mode 100644 vendor/google.golang.org/grpc/idle.go create mode 100644 vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/config.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/channel.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/channelmap.go delete mode 100644 vendor/google.golang.org/grpc/internal/channelz/id.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/server.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/socket.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/subchannel.go rename vendor/google.golang.org/grpc/internal/channelz/{types_linux.go => syscall_linux.go} (83%) rename vendor/google.golang.org/grpc/internal/channelz/{types_nonlinux.go => syscall_nonlinux.go} (90%) create mode 100644 vendor/google.golang.org/grpc/internal/channelz/trace.go delete mode 100644 vendor/google.golang.org/grpc/internal/channelz/types.go create mode 100644 vendor/google.golang.org/grpc/internal/experimental.go create mode 100644 vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go create mode 100644 vendor/google.golang.org/grpc/internal/grpcsync/pubsub.go create mode 100644 vendor/google.golang.org/grpc/internal/idle/idle.go create mode 100644 vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go rename vendor/google.golang.org/grpc/internal/{channelz/util_nonlinux.go => tcp_keepalive_others.go} (69%) create mode 100644 vendor/google.golang.org/grpc/internal/tcp_keepalive_unix.go create mode 100644 vendor/google.golang.org/grpc/internal/tcp_keepalive_windows.go delete mode 100644 vendor/google.golang.org/grpc/internal/xds_handshake_cluster.go create mode 100644 vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go delete mode 100644 vendor/google.golang.org/grpc/resolver_conn_wrapper.go create mode 100644 vendor/google.golang.org/grpc/resolver_wrapper.go create mode 100644 vendor/google.golang.org/grpc/shared_buffer_pool.go create mode 100644 vendor/google.golang.org/grpc/trace_notrace.go create mode 100644 vendor/google.golang.org/grpc/trace_withtrace.go create mode 100644 vendor/google.golang.org/protobuf/encoding/protodelim/protodelim.go create mode 100644 vendor/google.golang.org/protobuf/internal/editiondefaults/defaults.go create mode 100644 vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb create mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/editions.go create mode 100644 vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go rename vendor/google.golang.org/protobuf/internal/strs/{strings_unsafe.go => strings_unsafe_go120.go} (96%) create mode 100644 vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go121.go create mode 100644 vendor/google.golang.org/protobuf/protoadapt/convert.go create mode 100644 vendor/google.golang.org/protobuf/reflect/protodesc/editions.go rename vendor/google.golang.org/protobuf/reflect/protoreflect/{value_unsafe.go => value_unsafe_go120.go} (97%) create mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go create mode 100644 vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go create mode 100644 vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto rename vendor/gopkg.in/DataDog/dd-trace-go.v1/{internal/appsec/waf/lib/linux-amd64/vendor.go => datastreams/options/options.go} (61%) delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/cassandra.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/messaging.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/rpc.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/span_kind.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/span_link_msgp.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/abandonedspans.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/data_streams.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/dynamic_config.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagating_tags.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/remote_config.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/telemetry.go rename vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/{appsec/waf/waf_disabled_build_tag.go => active_span_key.go} (53%) create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/agent.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec_disabled.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/config.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/rules_manager.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/common.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/grpc.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/tags.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/http.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/tags.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/actions.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.html create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.json create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/shared.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/listener/listener.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/remoteconfig.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/rule.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/telemetry.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/telemetry_cgo.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64/libddwaf.a delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-amd64/libddwaf.a delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/types.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled_cgo.go delete mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled_target.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/container_linux.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/container_stub.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/fast_queue.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/hash_cache.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/pathway.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/payload.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/payload_msgp.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/processor.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/propagator.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams/transport.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/gitmetadata.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/gitmetadatabinary.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/gitmetadatabinary_legacy.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/azure/azure.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/cachedfetch/fetcher.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/ec2/ec2.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/ecs/aws.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/fqdn_nix.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/fqdn_windows.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/gce/gce.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/httputils/helpers.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/providers.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/hostname/validate/validate.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema/namingschema.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema/op.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema/service_name.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/normalizer/normalizer.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig/config.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig/remoteconfig.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig/types.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/statsd.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry/client.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry/message.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry/option.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry/telemetry.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry/utils.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/trace_context.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof/endpoint_counter.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof/profiler.go create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/utils.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/doc.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/generated.proto create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/register.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/types.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/doc.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/generated.pb.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/generated.proto create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/register.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/types.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/apidiscovery/v2beta1/zz_generated.prerelease-lifecycle.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/doc.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/generated.pb.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/generated.proto create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/register.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/types.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/authentication/v1alpha1/zz_generated.prerelease-lifecycle.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/doc.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/generated.pb.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/generated.proto create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/register.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/types.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/certificates/v1alpha1/zz_generated.prerelease-lifecycle.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/doc.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/generated.pb.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/generated.proto create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/register.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/types.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/flowcontrol/v1beta3/zz_generated.prerelease-lifecycle.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/doc.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/generated.pb.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/generated.proto create mode 100644 vendor/k8s.io/api/networking/v1alpha1/register.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/types.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/well_known_labels.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/api/networking/v1alpha1/zz_generated.prerelease-lifecycle.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/doc.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/generated.pb.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/generated.proto create mode 100644 vendor/k8s.io/api/resource/v1alpha2/register.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/types.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/types_swagger_doc_generated.go create mode 100644 vendor/k8s.io/api/resource/v1alpha2/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go create mode 100644 vendor/k8s.io/apimachinery/pkg/api/validation/doc.go create mode 100644 vendor/k8s.io/apimachinery/pkg/api/validation/generic.go create mode 100644 vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go create mode 100644 vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion/defaults.go create mode 100644 vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go create mode 100644 vendor/k8s.io/apimachinery/pkg/runtime/splice.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/clock/clock.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/dump/dump.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/endpoints.yaml create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/fieldmanager.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/atmostevery.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/buildmanagerinfo.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/capmanagers.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/conflict.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/fieldmanager.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/fields.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastapplied.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastappliedmanager.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastappliedupdater.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/managedfields.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/managedfieldsupdater.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/manager.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/pathelement.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/skipnonapplied.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/stripmeta.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/structuredmerge.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/typeconverter.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/versioncheck.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/versionconverter.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/node.yaml create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/pod.yaml create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/scalehandler.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/managedfields/typeconverter.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/sets/ordered.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/sets/set.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/backoff.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/delay.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/error.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/loop.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/poll.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/wait/timer.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/matchcondition.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/auditannotation.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/expressionwarning.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/matchcondition.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/matchresources.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/namedrulewithoperations.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/paramkind.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/paramref.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/typechecking.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicybindingspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicystatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validation.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/variable.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/auditannotation.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/expressionwarning.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/matchcondition.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/matchresources.go rename vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/{rulewithoperations.go => namedrulewithoperations.go} (56%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/paramkind.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/paramref.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/rule.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/typechecking.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingadmissionpolicybindingspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingadmissionpolicyspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingadmissionpolicystatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validation.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/variable.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetordinals.go rename vendor/k8s.io/client-go/applyconfigurations/{extensions/v1beta1/allowedflexvolume.go => apps/v1beta1/statefulsetordinals.go} (50%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetordinals.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicy.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyonexitcodesrequirement.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyonpodconditionspattern.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyrule.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/certificates/v1alpha1/clustertrustbundle.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/certificates/v1alpha1/clustertrustbundlespec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/claimsource.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/containerresizepolicy.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/hostip.go rename vendor/k8s.io/client-go/applyconfigurations/{autoscaling/v2/podresourcemetricsource.go => core/v1/podresourceclaim.go} (51%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/podresourceclaimstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/podschedulinggate.go rename vendor/k8s.io/client-go/applyconfigurations/{extensions/v1beta1/allowedcsidriver.go => core/v1/resourceclaim.go} (65%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/core/v1/typedobjectreference.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/allowedhostpath.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/fsgroupstrategyoptions.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/hostportrange.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/idrange.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressloadbalanceringress.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressloadbalancerstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressportstatus.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/podsecuritypolicyspec.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/runasgroupstrategyoptions.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/runasuserstrategyoptions.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/runtimeclassstrategyoptions.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/selinuxstrategyoptions.go delete mode 100644 vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/supplementalgroupsstrategyoptions.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/exemptprioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/exemptprioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/exemptprioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/exemptprioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowdistinguishermethod.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschema.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschemacondition.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschemaspec.go rename vendor/k8s.io/client-go/applyconfigurations/{networking/v1/networkpolicystatus.go => flowcontrol/v1beta3/flowschemastatus.go} (61%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/groupsubject.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/limitedprioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/limitresponse.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/nonresourcepolicyrule.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/policyruleswithsubjects.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationcondition.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationreference.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationspec.go rename vendor/k8s.io/client-go/applyconfigurations/{extensions/v1beta1/networkpolicystatus.go => flowcontrol/v1beta3/prioritylevelconfigurationstatus.go} (57%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/queuingconfiguration.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/resourcepolicyrule.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/serviceaccountsubject.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/subject.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/usersubject.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressloadbalanceringress.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressloadbalancerstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressportstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/clustercidr.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/clustercidrspec.go rename vendor/k8s.io/client-go/applyconfigurations/{extensions/v1beta1/podsecuritypolicy.go => networking/v1alpha1/ipaddress.go} (66%) create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/ipaddressspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/parentreference.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressloadbalanceringress.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressloadbalancerstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressportstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/allocationresult.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontext.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontextspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontextstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaim.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimconsumerreference.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimparametersreference.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimschedulingstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimspec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimstatus.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimtemplate.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimtemplatespec.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclass.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclassparametersreference.go create mode 100644 vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourcehandle.go create mode 100644 vendor/k8s.io/client-go/discovery/aggregated_discovery.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/admissionregistration_client.go rename vendor/k8s.io/{component-base/config => client-go/kubernetes/typed/admissionregistration}/v1alpha1/doc.go (74%) create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_admissionregistration_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/generated_expansion.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/fake/fake_validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/fake/fake_validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/validatingadmissionpolicy.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/validatingadmissionpolicybinding.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/fake/fake_selfsubjectreview.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/selfsubjectreview.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/authentication_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/fake/fake_authentication_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/fake/fake_selfsubjectreview.go rename vendor/k8s.io/{component-base/config/doc.go => client-go/kubernetes/typed/authentication/v1alpha1/generated_expansion.go} (78%) create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/selfsubjectreview.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/fake/fake_selfsubjectreview.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/selfsubjectreview.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/certificates_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/clustertrustbundle.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/fake/fake_certificates_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/fake/fake_clustertrustbundle.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/generated_expansion.go delete mode 100644 vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_podsecuritypolicy.go delete mode 100644 vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/podsecuritypolicy.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/fake/fake_flowcontrol_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/fake/fake_flowschema.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/fake/fake_prioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/flowcontrol_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/flowschema.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/generated_expansion.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/prioritylevelconfiguration.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/clustercidr.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/fake/fake_clustercidr.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/fake/fake_ipaddress.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/fake/fake_networking_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/generated_expansion.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/ipaddress.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/networking_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/doc.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/fake_podschedulingcontext.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/fake_resource_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/fake_resourceclaim.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/fake_resourceclaimtemplate.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/fake/fake_resourceclass.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/generated_expansion.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/podschedulingcontext.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resource_client.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclaim.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclaimtemplate.go create mode 100644 vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclass.go create mode 100644 vendor/k8s.io/client-go/openapi/OWNERS create mode 100644 vendor/k8s.io/client-go/openapi/typeconverter.go create mode 100644 vendor/k8s.io/client-go/tools/cache/object-names.go create mode 100644 vendor/k8s.io/client-go/tools/cache/retry_with_deadline.go create mode 100644 vendor/k8s.io/client-go/tools/cache/synctrack/lazy.go create mode 100644 vendor/k8s.io/client-go/tools/cache/synctrack/synctrack.go create mode 100644 vendor/k8s.io/client-go/transport/cache_go118.go delete mode 100644 vendor/k8s.io/component-base/config/OWNERS delete mode 100644 vendor/k8s.io/component-base/config/types.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/conversion.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/defaults.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/register.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/types.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go delete mode 100644 vendor/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/k8s.io/component-base/logs/api/v1/doc.go rename vendor/k8s.io/component-base/logs/{ => api/v1}/kube_features.go (53%) create mode 100644 vendor/k8s.io/component-base/logs/api/v1/options.go create mode 100644 vendor/k8s.io/component-base/logs/api/v1/pflags.go create mode 100644 vendor/k8s.io/component-base/logs/api/v1/registry.go create mode 100644 vendor/k8s.io/component-base/logs/api/v1/types.go rename vendor/k8s.io/component-base/{config => logs/api/v1}/zz_generated.deepcopy.go (71%) delete mode 100644 vendor/k8s.io/component-base/logs/config.go create mode 100644 vendor/k8s.io/component-base/logs/internal/setverbositylevel/setverbositylevel.go create mode 100644 vendor/k8s.io/component-base/logs/klogflags/klogflags.go delete mode 100644 vendor/k8s.io/component-base/logs/options.go delete mode 100644 vendor/k8s.io/component-base/logs/registry/registry.go delete mode 100644 vendor/k8s.io/component-base/logs/validate.go create mode 100644 vendor/k8s.io/component-base/metrics/buckets.go create mode 100644 vendor/k8s.io/component-base/metrics/prometheus/feature/metrics.go create mode 100644 vendor/k8s.io/component-base/metrics/prometheusextension/timing_histogram.go create mode 100644 vendor/k8s.io/component-base/metrics/prometheusextension/timing_histogram_vec.go create mode 100644 vendor/k8s.io/component-base/metrics/prometheusextension/weighted_histogram.go create mode 100644 vendor/k8s.io/component-base/metrics/prometheusextension/weighted_histogram_vec.go create mode 100644 vendor/k8s.io/component-base/metrics/timing_histogram.go create mode 100644 vendor/k8s.io/component-base/version/dynamic.go create mode 100644 vendor/k8s.io/klog/v2/.golangci.yaml create mode 100644 vendor/k8s.io/klog/v2/contextual_slog.go create mode 100644 vendor/k8s.io/klog/v2/format.go create mode 100644 vendor/k8s.io/klog/v2/internal/serialize/keyvalues_no_slog.go create mode 100644 vendor/k8s.io/klog/v2/internal/serialize/keyvalues_slog.go create mode 100644 vendor/k8s.io/klog/v2/internal/sloghandler/sloghandler_slog.go create mode 100644 vendor/k8s.io/klog/v2/k8s_references_slog.go create mode 100644 vendor/k8s.io/klog/v2/klogr_slog.go create mode 100644 vendor/k8s.io/klog/v2/safeptr.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/builder3/util/util.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/cached/cache.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/internal/handler/handler_cache.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/internal/serialization.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/openapiconv/convert.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/schemaconv/openapi.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/schemaconv/proto_models.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/schemamutation/walker.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/spec3/security_requirement.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/validation/spec/fuzz.go create mode 100644 vendor/k8s.io/utils/net/ipfamily.go create mode 100644 vendor/k8s.io/utils/ptr/OWNERS create mode 100644 vendor/k8s.io/utils/ptr/README.md create mode 100644 vendor/k8s.io/utils/ptr/ptr.go create mode 100644 vendor/sigs.k8s.io/structured-merge-diff/v4/merge/conflict.go create mode 100644 vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go create mode 100644 vendor/sigs.k8s.io/structured-merge-diff/v4/typed/compare.go delete mode 100644 vendor/sigs.k8s.io/structured-merge-diff/v4/typed/union.go diff --git a/vendor/github.com/DataDog/appsec-internal-go/LICENSE b/vendor/github.com/DataDog/appsec-internal-go/LICENSE new file mode 100644 index 000000000..9301dd7ab --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/LICENSE @@ -0,0 +1,200 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-present Datadog, Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/DataDog/appsec-internal-go/appsec/config.go b/vendor/github.com/DataDog/appsec-internal-go/appsec/config.go new file mode 100644 index 000000000..d6723703e --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/appsec/config.go @@ -0,0 +1,196 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + +package appsec + +import ( + "os" + "regexp" + "strconv" + "time" + "unicode" + "unicode/utf8" + + "github.com/DataDog/appsec-internal-go/log" +) + +// Configuration environment variables +const ( + // EnvAPISecEnabled is the env var used to enable API Security + EnvAPISecEnabled = "DD_API_SECURITY_ENABLED" + // EnvAPISecSampleRate is the env var used to set the sampling rate of API Security schema extraction + EnvAPISecSampleRate = "DD_API_SECURITY_REQUEST_SAMPLE_RATE" + // EnvObfuscatorKey is the env var used to provide the WAF key obfuscation regexp + EnvObfuscatorKey = "DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP" + // EnvObfuscatorValue is the env var used to provide the WAF value obfuscation regexp + EnvObfuscatorValue = "DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP" + // EnvWAFTimeout is the env var used to specify the timeout value for a WAF run + EnvWAFTimeout = "DD_APPSEC_WAF_TIMEOUT" + // EnvTraceRateLimit is the env var used to set the ASM trace limiting rate + EnvTraceRateLimit = "DD_APPSEC_TRACE_RATE_LIMIT" + // EnvRules is the env var used to provide a path to a local security rule file + EnvRules = "DD_APPSEC_RULES" +) + +// Configuration constants and default values +const ( + // DefaultAPISecSampleRate is the default rate at which API Security schemas are extracted from requests + DefaultAPISecSampleRate = .1 + // DefaultObfuscatorKeyRegex is the default regexp used to obfuscate keys + DefaultObfuscatorKeyRegex = `(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?)key)|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization` + // DefaultObfuscatorValueRegex is the default regexp used to obfuscate values + DefaultObfuscatorValueRegex = `(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\s*=[^;]|"\s*:\s*"[^"]+")|bearer\s+[a-z0-9\._\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\w=-]+\.ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}` + // DefaultWAFTimeout is the default time limit past which a WAF run will timeout + DefaultWAFTimeout = time.Millisecond + // DefaultTraceRate is the default limit (trace/sec) past which ASM traces are sampled out + DefaultTraceRate uint = 100 // up to 100 appsec traces/s +) + +// APISecConfig holds the configuration for API Security schemas reporting +// It is used to enabled/disable the feature as well as to configure the rate +// at which schemas get reported, +type APISecConfig struct { + Enabled bool + SampleRate float64 +} + +// ObfuscatorConfig wraps the key and value regexp to be passed to the WAF to perform obfuscation. +type ObfuscatorConfig struct { + KeyRegex string + ValueRegex string +} + +// NewAPISecConfig creates and returns a new API Security configuration by reading the env +func NewAPISecConfig() APISecConfig { + return APISecConfig{ + Enabled: apiSecurityEnabled(), + SampleRate: readAPISecuritySampleRate(), + } +} + +func apiSecurityEnabled() bool { + enabled := true + str, set := os.LookupEnv(EnvAPISecEnabled) + if set { + var err error + enabled, err = strconv.ParseBool(str) + if err != nil { + logEnvVarParsingError(EnvAPISecEnabled, str, err, enabled) + } + } + return enabled +} + +func readAPISecuritySampleRate() float64 { + value := os.Getenv(EnvAPISecSampleRate) + rate, err := strconv.ParseFloat(value, 64) + if err != nil { + logEnvVarParsingError(EnvAPISecSampleRate, value, err, DefaultAPISecSampleRate) + return DefaultAPISecSampleRate + } + // Clamp the value so that 0.0 <= rate <= 1.0 + if rate < 0. { + rate = 0. + } else if rate > 1. { + rate = 1. + } + return rate +} + +// NewObfuscatorConfig creates and returns a new WAF obfuscator configuration by reading the env +func NewObfuscatorConfig() ObfuscatorConfig { + keyRE := readObfuscatorConfigRegexp(EnvObfuscatorKey, DefaultObfuscatorKeyRegex) + valueRE := readObfuscatorConfigRegexp(EnvObfuscatorValue, DefaultObfuscatorValueRegex) + return ObfuscatorConfig{KeyRegex: keyRE, ValueRegex: valueRE} +} + +func readObfuscatorConfigRegexp(name, defaultValue string) string { + val, present := os.LookupEnv(name) + if !present { + log.Debug("appsec: %s not defined, starting with the default obfuscator regular expression", name) + return defaultValue + } + if _, err := regexp.Compile(val); err != nil { + logUnexpectedEnvVarValue(name, val, "could not compile the configured obfuscator regular expression", defaultValue) + return defaultValue + } + log.Debug("appsec: starting with the configured obfuscator regular expression %s", name) + return val +} + +// WAFTimeoutFromEnv reads and parses the WAF timeout value set through the env +// If not set, it defaults to `DefaultWAFTimeout` +func WAFTimeoutFromEnv() (timeout time.Duration) { + timeout = DefaultWAFTimeout + value := os.Getenv(EnvWAFTimeout) + if value == "" { + return + } + + // Check if the value ends with a letter, which means the user has + // specified their own time duration unit(s) such as 1s200ms. + // Otherwise, default to microseconds. + if lastRune, _ := utf8.DecodeLastRuneInString(value); !unicode.IsLetter(lastRune) { + value += "us" // Add the default microsecond time-duration suffix + } + + parsed, err := time.ParseDuration(value) + if err != nil { + logEnvVarParsingError(EnvWAFTimeout, value, err, timeout) + return + } + if parsed <= 0 { + logUnexpectedEnvVarValue(EnvWAFTimeout, parsed, "expecting a strictly positive duration", timeout) + return + } + return parsed +} + +// RateLimitFromEnv reads and parses the trace rate limit set through the env +// If not set, it defaults to `DefaultTraceRate` +func RateLimitFromEnv() (rate uint) { + rate = DefaultTraceRate + value := os.Getenv(EnvTraceRateLimit) + if value == "" { + return rate + } + parsed, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logEnvVarParsingError(EnvTraceRateLimit, value, err, rate) + return + } + if parsed == 0 { + logUnexpectedEnvVarValue(EnvTraceRateLimit, parsed, "expecting a value strictly greater than 0", rate) + return + } + return uint(parsed) +} + +// RulesFromEnv returns the security rules provided through the environment +// If the env var is not set, the default recommended rules are returned instead +func RulesFromEnv() ([]byte, error) { + filepath := os.Getenv(EnvRules) + if filepath == "" { + log.Debug("appsec: using the default built-in recommended security rules") + return DefaultRuleset() + } + buf, err := os.ReadFile(filepath) + if err != nil { + if os.IsNotExist(err) { + err = log.Errorf("appsec: could not find the rules file in path %s: %w.", filepath, err) + } + return nil, err + } + log.Debug("appsec: using the security rules from file %s", filepath) + return buf, nil +} + +func logEnvVarParsingError(name, value string, err error, defaultValue any) { + log.Debug("appsec: could not parse the env var %s=%s as a duration: %v. Using default value %v.", name, value, err, defaultValue) +} + +func logUnexpectedEnvVarValue(name string, value any, reason string, defaultValue any) { + log.Debug("appsec: unexpected configuration value of %s=%v: %s. Using default value %v.", name, value, reason, defaultValue) +} diff --git a/vendor/github.com/DataDog/appsec-internal-go/appsec/embed.go b/vendor/github.com/DataDog/appsec-internal-go/appsec/embed.go new file mode 100644 index 000000000..9617fb3d5 --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/appsec/embed.go @@ -0,0 +1,20 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package appsec + +import _ "embed" // Blank import comment for golint compliance + +// StaticRecommendedRules holds the recommended AppSec security rules (v1.11.0) +// Source: https://github.com/DataDog/appsec-event-rules/blob/1.11.0/build/recommended.json +// +//go:embed rules.json +var StaticRecommendedRules string + +// StaticProcessors holds the default processors and scanners used for API Security +// Not part of the recommended security rules +// +//go:embed processors.json +var StaticProcessors string diff --git a/vendor/github.com/DataDog/appsec-internal-go/appsec/processors.json b/vendor/github.com/DataDog/appsec-internal-go/appsec/processors.json new file mode 100644 index 000000000..968c53ddb --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/appsec/processors.json @@ -0,0 +1,208 @@ +{ + "processors": [ + { + "id": "processor-001", + "generator": "extract_schema", + "conditions": [ + { + "operator": "equals", + "parameters": { + "inputs": [ + { + "address": "waf.context.processor", + "key_path": [ + "extract-schema" + ] + } + ], + "type": "boolean", + "value": true + } + } + ], + "parameters": { + "mappings": [ + { + "inputs": [ + { + "address": "server.request.body" + } + ], + "output": "_dd.appsec.s.req.body" + }, + { + "inputs": [ + { + "address": "server.request.headers.no_cookies" + } + ], + "output": "_dd.appsec.s.req.headers" + }, + { + "inputs": [ + { + "address": "server.request.query" + } + ], + "output": "_dd.appsec.s.req.query" + }, + { + "inputs": [ + { + "address": "server.request.path_params" + } + ], + "output": "_dd.appsec.s.req.params" + }, + { + "inputs": [ + { + "address": "server.request.cookies" + } + ], + "output": "_dd.appsec.s.req.cookies" + }, + { + "inputs": [ + { + "address": "server.response.headers.no_cookies" + } + ], + "output": "_dd.appsec.s.res.headers" + }, + { + "inputs": [ + { + "address": "server.response.body" + } + ], + "output": "_dd.appsec.s.res.body" + } + ], + "scanners": [ + { + "tags": { + "category": "pii" + } + } + ] + }, + "evaluate": false, + "output": true + } + ], + "scanners": [ + { + "id": "d962f7ddb3f55041e39195a60ff79d4814a7c331", + "name": "US Passport Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "passport", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[0-9A-Z]{9}\\b|\\b[0-9]{6}[A-Z][0-9]{2}\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "tags": { + "type": "passport_number", + "category": "pii" + } + }, + { + "id": "ac6d683cbac77f6e399a14990793dd8fd0fca333", + "name": "US Vehicle Identification Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "vehicle[_\\s-]*identification[_\\s-]*number|vin", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[A-HJ-NPR-Z0-9]{17}\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "vin", + "category": "pii" + } + }, + { + "id": "de0899e0cbaaa812bb624cf04c912071012f616d", + "name": "UK National Insurance Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "national[\\s_]?(?:insurance(?:\\s+number)?)?|NIN|NI[\\s_]?number|insurance[\\s_]?number", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[A-Z]{2}\\d{6}[A-Z]?\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "tags": { + "type": "uk_nin", + "category": "pii" + } + }, + { + "id": "450239afc250a19799b6c03dc0e16fd6a4b2a1af", + "name": "Canadian Social Insurance Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "social[\\s_]?(?:insurance(?:\\s+number)?)?|SIN|Canadian[\\s_]?(?:social[\\s_]?(?:insurance)?|insurance[\\s_]?number)?", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b\\d{3}-\\d{3}-\\d{3}\\b", + "options": { + "case_sensitive": false, + "min_length": 11 + } + } + }, + "tags": { + "type": "canadian_sin", + "category": "pii" + } + } + ] +} \ No newline at end of file diff --git a/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.go b/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.go new file mode 100644 index 000000000..d2d672a84 --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.go @@ -0,0 +1,33 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. +package appsec + +import "encoding/json" + +// DefaultRuleset returns the marshaled default recommended security rules for AppSec +func DefaultRuleset() ([]byte, error) { + rules, err := DefaultRulesetMap() + if err != nil { + return nil, err + } + return json.Marshal(rules) +} + +// DefaultRulesetMap returns the unmarshaled default recommended security rules for AppSec +func DefaultRulesetMap() (map[string]any, error) { + var rules map[string]any + var processors map[string]any + if err := json.Unmarshal([]byte(StaticRecommendedRules), &rules); err != nil { + return nil, err + } + if err := json.Unmarshal([]byte(StaticProcessors), &processors); err != nil { + return nil, err + } + for k, v := range processors { + rules[k] = v + } + + return rules, nil +} diff --git a/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.json b/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.json new file mode 100644 index 000000000..7912743b4 --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/appsec/rules.json @@ -0,0 +1,9360 @@ +{ + "version": "2.2", + "metadata": { + "rules_version": "1.11.0" + }, + "rules": [ + { + "id": "blk-001-001", + "name": "Block IP Addresses", + "tags": { + "type": "block_ip", + "category": "security_response" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "http.client_ip" + } + ], + "data": "blocked_ips" + }, + "operator": "ip_match" + } + ], + "transformers": [], + "on_match": [ + "block" + ] + }, + { + "id": "blk-001-002", + "name": "Block User Addresses", + "tags": { + "type": "block_user", + "category": "security_response" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "usr.id" + } + ], + "data": "blocked_users" + }, + "operator": "exact_match" + } + ], + "transformers": [], + "on_match": [ + "block" + ] + }, + { + "id": "crs-913-110", + "name": "Acunetix", + "tags": { + "type": "commercial_scanner", + "crs_id": "913110", + "category": "attack_attempt", + "tool_name": "Acunetix", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies" + } + ], + "list": [ + "acunetix-product", + "(acunetix web vulnerability scanner", + "acunetix-scanning-agreement", + "acunetix-user-agreement", + "md5(acunetix_wvs_security_test)" + ] + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-913-120", + "name": "Known security scanner filename/argument", + "tags": { + "type": "security_scanner", + "crs_id": "913120", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "list": [ + "/.adsensepostnottherenonobook", + "/hello.html", + "/actsensepostnottherenonotive", + "/acunetix-wvs-test-for-some-inexistent-file", + "/antidisestablishmentarianism", + "/appscan_fingerprint/mac_address", + "/arachni-", + "/cybercop", + "/nessus_is_probing_you_", + "/nessustest", + "/netsparker-", + "/rfiinc.txt", + "/thereisnowaythat-you-canbethere", + "/w3af/remotefileinclude.html", + "appscan_fingerprint", + "w00tw00t.at.isc.sans.dfind", + "w00tw00t.at.blackhats.romanian.anti-sec" + ], + "options": { + "enforce_word_boundary": true + } + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-920-260", + "name": "Unicode Full/Half Width Abuse Attack Attempt", + "tags": { + "type": "http_protocol_violation", + "crs_id": "920260", + "category": "attack_attempt", + "cwe": "176", + "capec": "1000/255/153/267/71", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "\\%u[fF]{2}[0-9a-fA-F]{2}", + "options": { + "case_sensitive": true, + "min_length": 6 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-921-110", + "name": "HTTP Request Smuggling Attack", + "tags": { + "type": "http_protocol_violation", + "crs_id": "921110", + "category": "attack_attempt", + "cwe": "444", + "capec": "1000/210/272/220/33" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + } + ], + "regex": "(?:get|post|head|options|connect|put|delete|trace|track|patch|propfind|propatch|mkcol|copy|move|lock|unlock)\\s+[^\\s]+\\s+http/\\d", + "options": { + "case_sensitive": true, + "min_length": 12 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-921-160", + "name": "HTTP Header Injection Attack via payload (CR/LF and header-name detected)", + "tags": { + "type": "http_protocol_violation", + "crs_id": "921160", + "category": "attack_attempt", + "cwe": "113", + "capec": "1000/210/272/220/105" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.path_params" + } + ], + "regex": "[\\n\\r]+(?:refresh|(?:set-)?cookie|(?:x-)?(?:forwarded-(?:for|host|server)|via|remote-ip|remote-addr|originating-IP))\\s*:", + "options": { + "case_sensitive": true, + "min_length": 3 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-930-100", + "name": "Obfuscated Path Traversal Attack (/../)", + "tags": { + "type": "lfi", + "crs_id": "930100", + "category": "attack_attempt", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + }, + { + "address": "server.request.headers.no_cookies" + } + ], + "regex": "(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|\\/|\\x5c)(?:%(?:(?:f(?:(?:c%80|8)%8)?0%8|e)0%80%ae|2(?:(?:5(?:c0%25a|2))?e|%45)|u(?:(?:002|ff0)e|2024)|%32(?:%(?:%6|4)5|E)|c0(?:%[256aef]e|\\.))|\\.(?:%0[01])?|0x2e){2,3}(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|\\/|\\x5c)", + "options": { + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "normalizePath" + ] + }, + { + "id": "crs-930-110", + "name": "Simple Path Traversal Attack (/../)", + "tags": { + "type": "lfi", + "crs_id": "930110", + "category": "attack_attempt", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + }, + { + "address": "server.request.headers.no_cookies" + } + ], + "regex": "(?:(?:^|[\\x5c/])\\.{2,3}[\\x5c/]|[\\x5c/]\\.{2,3}(?:[\\x5c/]|$))", + "options": { + "case_sensitive": true, + "min_length": 3 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-930-120", + "name": "OS File Access Attempt", + "tags": { + "type": "lfi", + "crs_id": "930120", + "category": "attack_attempt", + "cwe": "22", + "capec": "1000/255/153/126", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "list": [ + "/.htaccess", + "/.htdigest", + "/.htpasswd", + "/.addressbook", + "/.aptitude/config", + ".aws/config", + ".aws/credentials", + "/.bash_config", + "/.bash_history", + "/.bash_logout", + "/.bash_profile", + "/.bashrc", + ".cache/notify-osd.log", + ".config/odesk/odesk team.conf", + "/.cshrc", + "/.dockerignore", + ".drush/", + "/.eslintignore", + "/.fbcindex", + "/.forward", + "/.git", + ".git/", + "/.gitattributes", + "/.gitconfig", + ".gnupg/", + ".hplip/hplip.conf", + "/.ksh_history", + "/.lesshst", + ".lftp/", + "/.lhistory", + "/.lldb-history", + ".local/share/mc/", + "/.lynx_cookies", + "/.my.cnf", + "/.mysql_history", + "/.nano_history", + "/.node_repl_history", + "/.pearrc", + "/.pgpass", + "/.php_history", + "/.pinerc", + ".pki/", + "/.proclog", + "/.procmailrc", + "/.psql_history", + "/.python_history", + "/.rediscli_history", + "/.rhistory", + "/.rhosts", + "/.sh_history", + "/.sqlite_history", + ".ssh/authorized_keys", + ".ssh/config", + ".ssh/id_dsa", + ".ssh/id_dsa.pub", + ".ssh/id_rsa", + ".ssh/id_rsa.pub", + ".ssh/identity", + ".ssh/identity.pub", + ".ssh/id_ecdsa", + ".ssh/id_ecdsa.pub", + ".ssh/known_hosts", + ".subversion/auth", + ".subversion/config", + ".subversion/servers", + ".tconn/tconn.conf", + "/.tcshrc", + ".vidalia/vidalia.conf", + "/.viminfo", + "/.vimrc", + "/.www_acl", + "/.wwwacl", + "/.xauthority", + "/.zhistory", + "/.zshrc", + "/.zsh_history", + "/.nsconfig", + "data/elasticsearch", + "data/kafka", + "etc/ansible", + "etc/bind", + "etc/centos-release", + "etc/centos-release-upstream", + "etc/clam.d", + "etc/elasticsearch", + "etc/freshclam.conf", + "etc/gshadow", + "etc/gshadow-", + "etc/httpd", + "etc/kafka", + "etc/kibana", + "etc/logstash", + "etc/lvm", + "etc/mongod.conf", + "etc/my.cnf", + "etc/nuxeo.conf", + "etc/pki", + "etc/postfix", + "etc/scw-release", + "etc/subgid", + "etc/subgid-", + "etc/sudoers.d", + "etc/sysconfig", + "etc/system-release-cpe", + "opt/nuxeo", + "opt/tomcat", + "tmp/kafka-logs", + "usr/lib/rpm/rpm.log", + "var/data/elasticsearch", + "var/lib/elasticsearch", + "etc/.java", + "etc/acpi", + "etc/alsa", + "etc/alternatives", + "etc/apache2", + "etc/apm", + "etc/apparmor", + "etc/apparmor.d", + "etc/apport", + "etc/apt", + "etc/asciidoc", + "etc/avahi", + "etc/bash_completion.d", + "etc/binfmt.d", + "etc/bluetooth", + "etc/bonobo-activation", + "etc/brltty", + "etc/ca-certificates", + "etc/calendar", + "etc/chatscripts", + "etc/chromium-browser", + "etc/clamav", + "etc/cni", + "etc/console-setup", + "etc/coraza-waf", + "etc/cracklib", + "etc/cron.d", + "etc/cron.daily", + "etc/cron.hourly", + "etc/cron.monthly", + "etc/cron.weekly", + "etc/cups", + "etc/cups.save", + "etc/cupshelpers", + "etc/dbus-1", + "etc/dconf", + "etc/default", + "etc/depmod.d", + "etc/dhcp", + "etc/dictionaries-common", + "etc/dkms", + "etc/dnsmasq.d", + "etc/dockeretc/dpkg", + "etc/emacs", + "etc/environment.d", + "etc/fail2ban", + "etc/firebird", + "etc/firefox", + "etc/fonts", + "etc/fwupd", + "etc/gconf", + "etc/gdb", + "etc/gdm3", + "etc/geoclue", + "etc/ghostscript", + "etc/gimp", + "etc/glvnd", + "etc/gnome", + "etc/gnome-vfs-2.0", + "etc/gnucash", + "etc/gnustep", + "etc/groff", + "etc/grub.d", + "etc/gss", + "etc/gtk-2.0", + "etc/gtk-3.0", + "etc/hp", + "etc/ifplugd", + "etc/imagemagick-6", + "etc/init", + "etc/init.d", + "etc/initramfs-tools", + "etc/insserv.conf.d", + "etc/iproute2", + "etc/iptables", + "etc/java", + "etc/java-11-openjdk", + "etc/java-17-oracle", + "etc/java-8-openjdk", + "etc/kernel", + "etc/ld.so.conf.d", + "etc/ldap", + "etc/libblockdev", + "etc/libibverbs.d", + "etc/libnl-3", + "etc/libpaper.d", + "etc/libreoffice", + "etc/lighttpd", + "etc/logcheck", + "etc/logrotate.d", + "etc/lynx", + "etc/mail", + "etc/mc", + "etc/menu", + "etc/menu-methods", + "etc/modprobe.d", + "etc/modsecurity", + "etc/modules-load.d", + "etc/monit", + "etc/mono", + "etc/mplayer", + "etc/mpv", + "etc/muttrc.d", + "etc/mysql", + "etc/netplan", + "etc/network", + "etc/networkd-dispatcher", + "etc/networkmanager", + "etc/newt", + "etc/nghttpx", + "etc/nikto", + "etc/odbcdatasources", + "etc/openal", + "etc/openmpi", + "etc/opt", + "etc/osync", + "etc/packagekit", + "etc/pam.d", + "etc/pcmcia", + "etc/perl", + "etc/php", + "etc/pki", + "etc/pm", + "etc/polkit-1", + "etc/postfix", + "etc/ppp", + "etc/profile.d", + "etc/proftpd", + "etc/pulse", + "etc/python", + "etc/rc0.d", + "etc/rc1.d", + "etc/rc2.d", + "etc/rc3.d", + "etc/rc4.d", + "etc/rc5.d", + "etc/rc6.d", + "etc/rcs.d", + "etc/resolvconf", + "etc/rsyslog.d", + "etc/samba", + "etc/sane.d", + "etc/security", + "etc/selinux", + "etc/sensors.d", + "etc/sgml", + "etc/signon-ui", + "etc/skel", + "etc/snmp", + "etc/sound", + "etc/spamassassin", + "etc/speech-dispatcher", + "etc/ssh", + "etc/ssl", + "etc/sudoers.d", + "etc/sysctl.d", + "etc/sysstat", + "etc/systemd", + "etc/terminfo", + "etc/texmf", + "etc/thermald", + "etc/thnuclnt", + "etc/thunderbird", + "etc/timidity", + "etc/tmpfiles.d", + "etc/ubuntu-advantage", + "etc/udev", + "etc/udisks2", + "etc/ufw", + "etc/update-manager", + "etc/update-motd.d", + "etc/update-notifier", + "etc/upower", + "etc/urlview", + "etc/usb_modeswitch.d", + "etc/vim", + "etc/vmware", + "etc/vmware-installer", + "etc/vmware-vix", + "etc/vulkan", + "etc/w3m", + "etc/wireshark", + "etc/wpa_supplicant", + "etc/x11", + "etc/xdg", + "etc/xml", + "etc/redis.conf", + "etc/redis-sentinel.conf", + "etc/php.ini", + "bin/php.ini", + "etc/httpd/php.ini", + "usr/lib/php.ini", + "usr/lib/php/php.ini", + "usr/local/etc/php.ini", + "usr/local/lib/php.ini", + "usr/local/php/lib/php.ini", + "usr/local/php4/lib/php.ini", + "usr/local/php5/lib/php.ini", + "usr/local/apache/conf/php.ini", + "etc/php4.4/fcgi/php.ini", + "etc/php4/apache/php.ini", + "etc/php4/apache2/php.ini", + "etc/php5/apache/php.ini", + "etc/php5/apache2/php.ini", + "etc/php/php.ini", + "etc/php/php4/php.ini", + "etc/php/apache/php.ini", + "etc/php/apache2/php.ini", + "web/conf/php.ini", + "usr/local/zend/etc/php.ini", + "opt/xampp/etc/php.ini", + "var/local/www/conf/php.ini", + "etc/php/cgi/php.ini", + "etc/php4/cgi/php.ini", + "etc/php5/cgi/php.ini", + "home2/bin/stable/apache/php.ini", + "home/bin/stable/apache/php.ini", + "etc/httpd/conf.d/php.conf", + "php5/php.ini", + "php4/php.ini", + "php/php.ini", + "windows/php.ini", + "winnt/php.ini", + "apache/php/php.ini", + "xampp/apache/bin/php.ini", + "netserver/bin/stable/apache/php.ini", + "volumes/macintosh_hd1/usr/local/php/lib/php.ini", + "etc/mono/1.0/machine.config", + "etc/mono/2.0/machine.config", + "etc/mono/2.0/web.config", + "etc/mono/config", + "usr/local/cpanel/logs/stats_log", + "usr/local/cpanel/logs/access_log", + "usr/local/cpanel/logs/error_log", + "usr/local/cpanel/logs/license_log", + "usr/local/cpanel/logs/login_log", + "var/cpanel/cpanel.config", + "usr/local/psa/admin/logs/httpsd_access_log", + "usr/local/psa/admin/logs/panel.log", + "usr/local/psa/admin/conf/php.ini", + "etc/sw-cp-server/applications.d/plesk.conf", + "usr/local/psa/admin/conf/site_isolation_settings.ini", + "usr/local/sb/config", + "etc/sw-cp-server/applications.d/00-sso-cpserver.conf", + "etc/sso/sso_config.ini", + "etc/mysql/conf.d/old_passwords.cnf", + "var/mysql.log", + "var/mysql-bin.index", + "var/data/mysql-bin.index", + "program files/mysql/mysql server 5.0/data/{host}.err", + "program files/mysql/mysql server 5.0/data/mysql.log", + "program files/mysql/mysql server 5.0/data/mysql.err", + "program files/mysql/mysql server 5.0/data/mysql-bin.log", + "program files/mysql/mysql server 5.0/data/mysql-bin.index", + "program files/mysql/data/{host}.err", + "program files/mysql/data/mysql.log", + "program files/mysql/data/mysql.err", + "program files/mysql/data/mysql-bin.log", + "program files/mysql/data/mysql-bin.index", + "mysql/data/{host}.err", + "mysql/data/mysql.log", + "mysql/data/mysql.err", + "mysql/data/mysql-bin.log", + "mysql/data/mysql-bin.index", + "usr/local/mysql/data/mysql.log", + "usr/local/mysql/data/mysql.err", + "usr/local/mysql/data/mysql-bin.log", + "usr/local/mysql/data/mysql-slow.log", + "usr/local/mysql/data/mysqlderror.log", + "usr/local/mysql/data/{host}.err", + "usr/local/mysql/data/mysql-bin.index", + "var/lib/mysql/my.cnf", + "etc/mysql/my.cnf", + "etc/my.cnf", + "program files/mysql/mysql server 5.0/my.ini", + "program files/mysql/mysql server 5.0/my.cnf", + "program files/mysql/my.ini", + "program files/mysql/my.cnf", + "mysql/my.ini", + "mysql/my.cnf", + "mysql/bin/my.ini", + "var/postgresql/log/postgresql.log", + "usr/internet/pgsql/data/postmaster.log", + "usr/local/pgsql/data/postgresql.log", + "usr/local/pgsql/data/pg_log", + "postgresql/log/pgadmin.log", + "var/lib/pgsql/data/postgresql.conf", + "var/postgresql/db/postgresql.conf", + "var/nm2/postgresql.conf", + "usr/local/pgsql/data/postgresql.conf", + "usr/local/pgsql/data/pg_hba.conf", + "usr/internet/pgsql/data/pg_hba.conf", + "usr/local/pgsql/data/passwd", + "usr/local/pgsql/bin/pg_passwd", + "etc/postgresql/postgresql.conf", + "etc/postgresql/pg_hba.conf", + "home/postgres/data/postgresql.conf", + "home/postgres/data/pg_version", + "home/postgres/data/pg_ident.conf", + "home/postgres/data/pg_hba.conf", + "program files/postgresql/8.3/data/pg_hba.conf", + "program files/postgresql/8.3/data/pg_ident.conf", + "program files/postgresql/8.3/data/postgresql.conf", + "program files/postgresql/8.4/data/pg_hba.conf", + "program files/postgresql/8.4/data/pg_ident.conf", + "program files/postgresql/8.4/data/postgresql.conf", + "program files/postgresql/9.0/data/pg_hba.conf", + "program files/postgresql/9.0/data/pg_ident.conf", + "program files/postgresql/9.0/data/postgresql.conf", + "program files/postgresql/9.1/data/pg_hba.conf", + "program files/postgresql/9.1/data/pg_ident.conf", + "program files/postgresql/9.1/data/postgresql.conf", + "wamp/logs/access.log", + "wamp/logs/apache_error.log", + "wamp/logs/genquery.log", + "wamp/logs/mysql.log", + "wamp/logs/slowquery.log", + "wamp/bin/apache/apache2.2.22/logs/access.log", + "wamp/bin/apache/apache2.2.22/logs/error.log", + "wamp/bin/apache/apache2.2.21/logs/access.log", + "wamp/bin/apache/apache2.2.21/logs/error.log", + "wamp/bin/mysql/mysql5.5.24/data/mysql-bin.index", + "wamp/bin/mysql/mysql5.5.16/data/mysql-bin.index", + "wamp/bin/apache/apache2.2.21/conf/httpd.conf", + "wamp/bin/apache/apache2.2.22/conf/httpd.conf", + "wamp/bin/apache/apache2.2.21/wampserver.conf", + "wamp/bin/apache/apache2.2.22/wampserver.conf", + "wamp/bin/apache/apache2.2.22/conf/wampserver.conf", + "wamp/bin/mysql/mysql5.5.24/my.ini", + "wamp/bin/mysql/mysql5.5.24/wampserver.conf", + "wamp/bin/mysql/mysql5.5.16/my.ini", + "wamp/bin/mysql/mysql5.5.16/wampserver.conf", + "wamp/bin/php/php5.3.8/php.ini", + "wamp/bin/php/php5.4.3/php.ini", + "xampp/apache/logs/access.log", + "xampp/apache/logs/error.log", + "xampp/mysql/data/mysql-bin.index", + "xampp/mysql/data/mysql.err", + "xampp/mysql/data/{host}.err", + "xampp/sendmail/sendmail.log", + "xampp/apache/conf/httpd.conf", + "xampp/filezillaftp/filezilla server.xml", + "xampp/mercurymail/mercury.ini", + "xampp/php/php.ini", + "xampp/phpmyadmin/config.inc.php", + "xampp/sendmail/sendmail.ini", + "xampp/webalizer/webalizer.conf", + "opt/lampp/etc/httpd.conf", + "xampp/htdocs/aca.txt", + "xampp/htdocs/admin.php", + "xampp/htdocs/leer.txt", + "usr/local/apache/logs/audit_log", + "usr/local/apache2/logs/audit_log", + "logs/security_debug_log", + "logs/security_log", + "usr/local/apache/conf/modsec.conf", + "usr/local/apache2/conf/modsec.conf", + "winnt/system32/logfiles/msftpsvc", + "winnt/system32/logfiles/msftpsvc1", + "winnt/system32/logfiles/msftpsvc2", + "windows/system32/logfiles/msftpsvc", + "windows/system32/logfiles/msftpsvc1", + "windows/system32/logfiles/msftpsvc2", + "etc/logrotate.d/proftpd", + "www/logs/proftpd.system.log", + "etc/pam.d/proftpd", + "etc/proftp.conf", + "etc/protpd/proftpd.conf", + "etc/vhcs2/proftpd/proftpd.conf", + "etc/proftpd/modules.conf", + "etc/vsftpd.chroot_list", + "etc/logrotate.d/vsftpd.log", + "etc/vsftpd/vsftpd.conf", + "etc/vsftpd.conf", + "etc/chrootusers", + "var/adm/log/xferlog", + "etc/wu-ftpd/ftpaccess", + "etc/wu-ftpd/ftphosts", + "etc/wu-ftpd/ftpusers", + "logs/pure-ftpd.log", + "usr/sbin/pure-config.pl", + "usr/etc/pure-ftpd.conf", + "etc/pure-ftpd/pure-ftpd.conf", + "usr/local/etc/pure-ftpd.conf", + "usr/local/etc/pureftpd.pdb", + "usr/local/pureftpd/etc/pureftpd.pdb", + "usr/local/pureftpd/sbin/pure-config.pl", + "usr/local/pureftpd/etc/pure-ftpd.conf", + "etc/pure-ftpd.conf", + "etc/pure-ftpd/pure-ftpd.pdb", + "etc/pureftpd.pdb", + "etc/pureftpd.passwd", + "etc/pure-ftpd/pureftpd.pdb", + "usr/ports/ftp/pure-ftpd/pure-ftpd.conf", + "usr/ports/ftp/pure-ftpd/pureftpd.pdb", + "usr/ports/ftp/pure-ftpd/pureftpd.passwd", + "usr/ports/net/pure-ftpd/pure-ftpd.conf", + "usr/ports/net/pure-ftpd/pureftpd.pdb", + "usr/ports/net/pure-ftpd/pureftpd.passwd", + "usr/pkgsrc/net/pureftpd/pure-ftpd.conf", + "usr/pkgsrc/net/pureftpd/pureftpd.pdb", + "usr/pkgsrc/net/pureftpd/pureftpd.passwd", + "usr/ports/contrib/pure-ftpd/pure-ftpd.conf", + "usr/ports/contrib/pure-ftpd/pureftpd.pdb", + "usr/ports/contrib/pure-ftpd/pureftpd.passwd", + "usr/sbin/mudlogd", + "etc/muddleftpd/mudlog", + "etc/muddleftpd.com", + "etc/muddleftpd/mudlogd.conf", + "etc/muddleftpd/muddleftpd.conf", + "usr/sbin/mudpasswd", + "etc/muddleftpd/muddleftpd.passwd", + "etc/muddleftpd/passwd", + "etc/logrotate.d/ftp", + "etc/ftpchroot", + "etc/ftphosts", + "etc/ftpusers", + "winnt/system32/logfiles/smtpsvc", + "winnt/system32/logfiles/smtpsvc1", + "winnt/system32/logfiles/smtpsvc2", + "winnt/system32/logfiles/smtpsvc3", + "winnt/system32/logfiles/smtpsvc4", + "winnt/system32/logfiles/smtpsvc5", + "windows/system32/logfiles/smtpsvc", + "windows/system32/logfiles/smtpsvc1", + "windows/system32/logfiles/smtpsvc2", + "windows/system32/logfiles/smtpsvc3", + "windows/system32/logfiles/smtpsvc4", + "windows/system32/logfiles/smtpsvc5", + "etc/osxhttpd/osxhttpd.conf", + "system/library/webobjects/adaptors/apache2.2/apache.conf", + "etc/apache2/sites-available/default", + "etc/apache2/sites-available/default-ssl", + "etc/apache2/sites-enabled/000-default", + "etc/apache2/sites-enabled/default", + "etc/apache2/apache2.conf", + "etc/apache2/ports.conf", + "usr/local/etc/apache/httpd.conf", + "usr/pkg/etc/httpd/httpd.conf", + "usr/pkg/etc/httpd/httpd-default.conf", + "usr/pkg/etc/httpd/httpd-vhosts.conf", + "etc/httpd/mod_php.conf", + "etc/httpd/extra/httpd-ssl.conf", + "etc/rc.d/rc.httpd", + "usr/local/apache/conf/httpd.conf.default", + "usr/local/apache/conf/access.conf", + "usr/local/apache22/conf/httpd.conf", + "usr/local/apache22/httpd.conf", + "usr/local/etc/apache22/conf/httpd.conf", + "usr/local/apps/apache22/conf/httpd.conf", + "etc/apache22/conf/httpd.conf", + "etc/apache22/httpd.conf", + "opt/apache22/conf/httpd.conf", + "usr/local/etc/apache2/vhosts.conf", + "usr/local/apache/conf/vhosts.conf", + "usr/local/apache2/conf/vhosts.conf", + "usr/local/apache/conf/vhosts-custom.conf", + "usr/local/apache2/conf/vhosts-custom.conf", + "etc/apache/default-server.conf", + "etc/apache2/default-server.conf", + "usr/local/apache2/conf/extra/httpd-ssl.conf", + "usr/local/apache2/conf/ssl.conf", + "etc/httpd/conf.d", + "usr/local/etc/apache22/httpd.conf", + "usr/local/etc/apache2/httpd.conf", + "etc/apache2/httpd2.conf", + "etc/apache2/ssl-global.conf", + "etc/apache2/vhosts.d/00_default_vhost.conf", + "apache/conf/httpd.conf", + "etc/apache/httpd.conf", + "etc/httpd/conf", + "http/httpd.conf", + "usr/local/apache1.3/conf/httpd.conf", + "usr/local/etc/httpd/conf", + "var/apache/conf/httpd.conf", + "var/www/conf", + "www/apache/conf/httpd.conf", + "www/conf/httpd.conf", + "etc/init.d", + "etc/apache/access.conf", + "etc/rc.conf", + "www/logs/freebsddiary-error.log", + "www/logs/freebsddiary-access_log", + "library/webserver/documents/index.html", + "library/webserver/documents/index.htm", + "library/webserver/documents/default.html", + "library/webserver/documents/default.htm", + "library/webserver/documents/index.php", + "library/webserver/documents/default.php", + "usr/local/etc/webmin/miniserv.conf", + "etc/webmin/miniserv.conf", + "usr/local/etc/webmin/miniserv.users", + "etc/webmin/miniserv.users", + "winnt/system32/logfiles/w3svc/inetsvn1.log", + "winnt/system32/logfiles/w3svc1/inetsvn1.log", + "winnt/system32/logfiles/w3svc2/inetsvn1.log", + "winnt/system32/logfiles/w3svc3/inetsvn1.log", + "windows/system32/logfiles/w3svc/inetsvn1.log", + "windows/system32/logfiles/w3svc1/inetsvn1.log", + "windows/system32/logfiles/w3svc2/inetsvn1.log", + "windows/system32/logfiles/w3svc3/inetsvn1.log", + "apache/logs/error.log", + "apache/logs/access.log", + "apache2/logs/error.log", + "apache2/logs/access.log", + "logs/error.log", + "logs/access.log", + "etc/httpd/logs/access_log", + "etc/httpd/logs/access.log", + "etc/httpd/logs/error_log", + "etc/httpd/logs/error.log", + "usr/local/apache/logs/access_log", + "usr/local/apache/logs/access.log", + "usr/local/apache/logs/error_log", + "usr/local/apache/logs/error.log", + "usr/local/apache2/logs/access_log", + "usr/local/apache2/logs/access.log", + "usr/local/apache2/logs/error_log", + "usr/local/apache2/logs/error.log", + "var/www/logs/access_log", + "var/www/logs/access.log", + "var/www/logs/error_log", + "var/www/logs/error.log", + "opt/lampp/logs/access_log", + "opt/lampp/logs/error_log", + "opt/xampp/logs/access_log", + "opt/xampp/logs/error_log", + "opt/lampp/logs/access.log", + "opt/lampp/logs/error.log", + "opt/xampp/logs/access.log", + "opt/xampp/logs/error.log", + "program files/apache group/apache/logs/access.log", + "program files/apache group/apache/logs/error.log", + "program files/apache software foundation/apache2.2/logs/error.log", + "program files/apache software foundation/apache2.2/logs/access.log", + "opt/apache/apache.conf", + "opt/apache/conf/apache.conf", + "opt/apache2/apache.conf", + "opt/apache2/conf/apache.conf", + "opt/httpd/apache.conf", + "opt/httpd/conf/apache.conf", + "etc/httpd/apache.conf", + "etc/apache2/apache.conf", + "etc/httpd/conf/apache.conf", + "usr/local/apache/apache.conf", + "usr/local/apache/conf/apache.conf", + "usr/local/apache2/apache.conf", + "usr/local/apache2/conf/apache.conf", + "usr/local/php/apache.conf.php", + "usr/local/php4/apache.conf.php", + "usr/local/php5/apache.conf.php", + "usr/local/php/apache.conf", + "usr/local/php4/apache.conf", + "usr/local/php5/apache.conf", + "private/etc/httpd/apache.conf", + "opt/apache/apache2.conf", + "opt/apache/conf/apache2.conf", + "opt/apache2/apache2.conf", + "opt/apache2/conf/apache2.conf", + "opt/httpd/apache2.conf", + "opt/httpd/conf/apache2.conf", + "etc/httpd/apache2.conf", + "etc/httpd/conf/apache2.conf", + "usr/local/apache/apache2.conf", + "usr/local/apache/conf/apache2.conf", + "usr/local/apache2/apache2.conf", + "usr/local/apache2/conf/apache2.conf", + "usr/local/php/apache2.conf.php", + "usr/local/php4/apache2.conf.php", + "usr/local/php5/apache2.conf.php", + "usr/local/php/apache2.conf", + "usr/local/php4/apache2.conf", + "usr/local/php5/apache2.conf", + "private/etc/httpd/apache2.conf", + "usr/local/apache/conf/httpd.conf", + "usr/local/apache2/conf/httpd.conf", + "etc/httpd/conf/httpd.conf", + "etc/apache/apache.conf", + "etc/apache/conf/httpd.conf", + "etc/apache2/httpd.conf", + "usr/apache2/conf/httpd.conf", + "usr/apache/conf/httpd.conf", + "usr/local/etc/apache/conf/httpd.conf", + "usr/local/apache/httpd.conf", + "usr/local/apache2/httpd.conf", + "usr/local/httpd/conf/httpd.conf", + "usr/local/etc/apache2/conf/httpd.conf", + "usr/local/etc/httpd/conf/httpd.conf", + "usr/local/apps/apache2/conf/httpd.conf", + "usr/local/apps/apache/conf/httpd.conf", + "usr/local/php/httpd.conf.php", + "usr/local/php4/httpd.conf.php", + "usr/local/php5/httpd.conf.php", + "usr/local/php/httpd.conf", + "usr/local/php4/httpd.conf", + "usr/local/php5/httpd.conf", + "etc/apache2/conf/httpd.conf", + "etc/http/conf/httpd.conf", + "etc/httpd/httpd.conf", + "etc/http/httpd.conf", + "etc/httpd.conf", + "opt/apache/conf/httpd.conf", + "opt/apache2/conf/httpd.conf", + "var/www/conf/httpd.conf", + "private/etc/httpd/httpd.conf", + "private/etc/httpd/httpd.conf.default", + "etc/apache2/vhosts.d/default_vhost.include", + "etc/apache2/conf.d/charset", + "etc/apache2/conf.d/security", + "etc/apache2/envvars", + "etc/apache2/mods-available/autoindex.conf", + "etc/apache2/mods-available/deflate.conf", + "etc/apache2/mods-available/dir.conf", + "etc/apache2/mods-available/mem_cache.conf", + "etc/apache2/mods-available/mime.conf", + "etc/apache2/mods-available/proxy.conf", + "etc/apache2/mods-available/setenvif.conf", + "etc/apache2/mods-available/ssl.conf", + "etc/apache2/mods-enabled/alias.conf", + "etc/apache2/mods-enabled/deflate.conf", + "etc/apache2/mods-enabled/dir.conf", + "etc/apache2/mods-enabled/mime.conf", + "etc/apache2/mods-enabled/negotiation.conf", + "etc/apache2/mods-enabled/php5.conf", + "etc/apache2/mods-enabled/status.conf", + "program files/apache group/apache/conf/httpd.conf", + "program files/apache group/apache2/conf/httpd.conf", + "program files/xampp/apache/conf/apache.conf", + "program files/xampp/apache/conf/apache2.conf", + "program files/xampp/apache/conf/httpd.conf", + "program files/apache group/apache/apache.conf", + "program files/apache group/apache/conf/apache.conf", + "program files/apache group/apache2/conf/apache.conf", + "program files/apache group/apache/apache2.conf", + "program files/apache group/apache/conf/apache2.conf", + "program files/apache group/apache2/conf/apache2.conf", + "program files/apache software foundation/apache2.2/conf/httpd.conf", + "volumes/macintosh_hd1/opt/httpd/conf/httpd.conf", + "volumes/macintosh_hd1/opt/apache/conf/httpd.conf", + "volumes/macintosh_hd1/opt/apache2/conf/httpd.conf", + "volumes/macintosh_hd1/usr/local/php/httpd.conf.php", + "volumes/macintosh_hd1/usr/local/php4/httpd.conf.php", + "volumes/macintosh_hd1/usr/local/php5/httpd.conf.php", + "volumes/webbackup/opt/apache2/conf/httpd.conf", + "volumes/webbackup/private/etc/httpd/httpd.conf", + "volumes/webbackup/private/etc/httpd/httpd.conf.default", + "usr/local/etc/apache/vhosts.conf", + "usr/local/jakarta/tomcat/conf/jakarta.conf", + "usr/local/jakarta/tomcat/conf/server.xml", + "usr/local/jakarta/tomcat/conf/context.xml", + "usr/local/jakarta/tomcat/conf/workers.properties", + "usr/local/jakarta/tomcat/conf/logging.properties", + "usr/local/jakarta/dist/tomcat/conf/jakarta.conf", + "usr/local/jakarta/dist/tomcat/conf/server.xml", + "usr/local/jakarta/dist/tomcat/conf/context.xml", + "usr/local/jakarta/dist/tomcat/conf/workers.properties", + "usr/local/jakarta/dist/tomcat/conf/logging.properties", + "usr/share/tomcat6/conf/server.xml", + "usr/share/tomcat6/conf/context.xml", + "usr/share/tomcat6/conf/workers.properties", + "usr/share/tomcat6/conf/logging.properties", + "var/cpanel/tomcat.options", + "usr/local/jakarta/tomcat/logs/catalina.out", + "usr/local/jakarta/tomcat/logs/catalina.err", + "opt/tomcat/logs/catalina.out", + "opt/tomcat/logs/catalina.err", + "usr/share/logs/catalina.out", + "usr/share/logs/catalina.err", + "usr/share/tomcat/logs/catalina.out", + "usr/share/tomcat/logs/catalina.err", + "usr/share/tomcat6/logs/catalina.out", + "usr/share/tomcat6/logs/catalina.err", + "usr/local/apache/logs/mod_jk.log", + "usr/local/jakarta/tomcat/logs/mod_jk.log", + "usr/local/jakarta/dist/tomcat/logs/mod_jk.log", + "opt/[jboss]/server/default/conf/jboss-minimal.xml", + "opt/[jboss]/server/default/conf/jboss-service.xml", + "opt/[jboss]/server/default/conf/jndi.properties", + "opt/[jboss]/server/default/conf/log4j.xml", + "opt/[jboss]/server/default/conf/login-config.xml", + "opt/[jboss]/server/default/conf/standardjaws.xml", + "opt/[jboss]/server/default/conf/standardjboss.xml", + "opt/[jboss]/server/default/conf/server.log.properties", + "opt/[jboss]/server/default/deploy/jboss-logging.xml", + "usr/local/[jboss]/server/default/conf/jboss-minimal.xml", + "usr/local/[jboss]/server/default/conf/jboss-service.xml", + "usr/local/[jboss]/server/default/conf/jndi.properties", + "usr/local/[jboss]/server/default/conf/log4j.xml", + "usr/local/[jboss]/server/default/conf/login-config.xml", + "usr/local/[jboss]/server/default/conf/standardjaws.xml", + "usr/local/[jboss]/server/default/conf/standardjboss.xml", + "usr/local/[jboss]/server/default/conf/server.log.properties", + "usr/local/[jboss]/server/default/deploy/jboss-logging.xml", + "private/tmp/[jboss]/server/default/conf/jboss-minimal.xml", + "private/tmp/[jboss]/server/default/conf/jboss-service.xml", + "private/tmp/[jboss]/server/default/conf/jndi.properties", + "private/tmp/[jboss]/server/default/conf/log4j.xml", + "private/tmp/[jboss]/server/default/conf/login-config.xml", + "private/tmp/[jboss]/server/default/conf/standardjaws.xml", + "private/tmp/[jboss]/server/default/conf/standardjboss.xml", + "private/tmp/[jboss]/server/default/conf/server.log.properties", + "private/tmp/[jboss]/server/default/deploy/jboss-logging.xml", + "tmp/[jboss]/server/default/conf/jboss-minimal.xml", + "tmp/[jboss]/server/default/conf/jboss-service.xml", + "tmp/[jboss]/server/default/conf/jndi.properties", + "tmp/[jboss]/server/default/conf/log4j.xml", + "tmp/[jboss]/server/default/conf/login-config.xml", + "tmp/[jboss]/server/default/conf/standardjaws.xml", + "tmp/[jboss]/server/default/conf/standardjboss.xml", + "tmp/[jboss]/server/default/conf/server.log.properties", + "tmp/[jboss]/server/default/deploy/jboss-logging.xml", + "program files/[jboss]/server/default/conf/jboss-minimal.xml", + "program files/[jboss]/server/default/conf/jboss-service.xml", + "program files/[jboss]/server/default/conf/jndi.properties", + "program files/[jboss]/server/default/conf/log4j.xml", + "program files/[jboss]/server/default/conf/login-config.xml", + "program files/[jboss]/server/default/conf/standardjaws.xml", + "program files/[jboss]/server/default/conf/standardjboss.xml", + "program files/[jboss]/server/default/conf/server.log.properties", + "program files/[jboss]/server/default/deploy/jboss-logging.xml", + "[jboss]/server/default/conf/jboss-minimal.xml", + "[jboss]/server/default/conf/jboss-service.xml", + "[jboss]/server/default/conf/jndi.properties", + "[jboss]/server/default/conf/log4j.xml", + "[jboss]/server/default/conf/login-config.xml", + "[jboss]/server/default/conf/standardjaws.xml", + "[jboss]/server/default/conf/standardjboss.xml", + "[jboss]/server/default/conf/server.log.properties", + "[jboss]/server/default/deploy/jboss-logging.xml", + "opt/[jboss]/server/default/log/server.log", + "opt/[jboss]/server/default/log/boot.log", + "usr/local/[jboss]/server/default/log/server.log", + "usr/local/[jboss]/server/default/log/boot.log", + "private/tmp/[jboss]/server/default/log/server.log", + "private/tmp/[jboss]/server/default/log/boot.log", + "tmp/[jboss]/server/default/log/server.log", + "tmp/[jboss]/server/default/log/boot.log", + "program files/[jboss]/server/default/log/server.log", + "program files/[jboss]/server/default/log/boot.log", + "[jboss]/server/default/log/server.log", + "[jboss]/server/default/log/boot.log", + "var/lighttpd.log", + "var/logs/access.log", + "usr/local/apache2/logs/lighttpd.error.log", + "usr/local/apache2/logs/lighttpd.log", + "usr/local/apache/logs/lighttpd.error.log", + "usr/local/apache/logs/lighttpd.log", + "usr/local/lighttpd/log/lighttpd.error.log", + "usr/local/lighttpd/log/access.log", + "usr/home/user/var/log/lighttpd.error.log", + "usr/home/user/var/log/apache.log", + "home/user/lighttpd/lighttpd.conf", + "usr/home/user/lighttpd/lighttpd.conf", + "etc/lighttpd/lighthttpd.conf", + "usr/local/etc/lighttpd.conf", + "usr/local/lighttpd/conf/lighttpd.conf", + "usr/local/etc/lighttpd.conf.new", + "var/www/.lighttpdpassword", + "logs/access_log", + "logs/error_log", + "etc/nginx/nginx.conf", + "usr/local/etc/nginx/nginx.conf", + "usr/local/nginx/conf/nginx.conf", + "usr/local/zeus/web/global.cfg", + "usr/local/zeus/web/log/errors", + "opt/lsws/conf/httpd_conf.xml", + "usr/local/lsws/conf/httpd_conf.xml", + "opt/lsws/logs/error.log", + "opt/lsws/logs/access.log", + "usr/local/lsws/logs/error.log", + "usr/local/logs/access.log", + "usr/local/samba/lib/log.user", + "usr/local/logs/samba.log", + "etc/samba/netlogon", + "etc/smbpasswd", + "etc/smb.conf", + "etc/samba/dhcp.conf", + "etc/samba/smb.conf", + "etc/samba/samba.conf", + "etc/samba/smb.conf.user", + "etc/samba/smbpasswd", + "etc/samba/smbusers", + "etc/samba/private/smbpasswd", + "usr/local/etc/smb.conf", + "usr/local/samba/lib/smb.conf.user", + "etc/dhcp3/dhclient.conf", + "etc/dhcp3/dhcpd.conf", + "etc/dhcp/dhclient.conf", + "program files/vidalia bundle/polipo/polipo.conf", + "etc/tor/tor-tsocks.conf", + "etc/stunnel/stunnel.conf", + "etc/tsocks.conf", + "etc/tinyproxy/tinyproxy.conf", + "etc/miredo-server.conf", + "etc/miredo.conf", + "etc/miredo/miredo-server.conf", + "etc/miredo/miredo.conf", + "etc/wicd/dhclient.conf.template.default", + "etc/wicd/manager-settings.conf", + "etc/wicd/wired-settings.conf", + "etc/wicd/wireless-settings.conf", + "etc/ipfw.rules", + "etc/ipfw.conf", + "etc/firewall.rules", + "winnt/system32/logfiles/firewall/pfirewall.log", + "winnt/system32/logfiles/firewall/pfirewall.log.old", + "windows/system32/logfiles/firewall/pfirewall.log", + "windows/system32/logfiles/firewall/pfirewall.log.old", + "etc/clamav/clamd.conf", + "etc/clamav/freshclam.conf", + "etc/x11/xorg.conf", + "etc/x11/xorg.conf-vesa", + "etc/x11/xorg.conf-vmware", + "etc/x11/xorg.conf.beforevmwaretoolsinstall", + "etc/x11/xorg.conf.orig", + "etc/bluetooth/input.conf", + "etc/bluetooth/main.conf", + "etc/bluetooth/network.conf", + "etc/bluetooth/rfcomm.conf", + "etc/bash_completion.d/debconf", + "root/.bash_logout", + "root/.bash_history", + "root/.bash_config", + "root/.bashrc", + "etc/bash.bashrc", + "var/adm/syslog", + "var/adm/sulog", + "var/adm/utmp", + "var/adm/utmpx", + "var/adm/wtmp", + "var/adm/wtmpx", + "var/adm/lastlog/username", + "usr/spool/lp/log", + "var/adm/lp/lpd-errs", + "usr/lib/cron/log", + "var/adm/loginlog", + "var/adm/pacct", + "var/adm/dtmp", + "var/adm/acct/sum/loginlog", + "var/adm/x0msgs", + "var/adm/crash/vmcore", + "var/adm/crash/unix", + "etc/newsyslog.conf", + "var/adm/qacct", + "var/adm/ras/errlog", + "var/adm/ras/bootlog", + "var/adm/cron/log", + "etc/utmp", + "etc/security/lastlog", + "etc/security/failedlogin", + "usr/spool/mqueue/syslog", + "var/adm/messages", + "var/adm/aculogs", + "var/adm/aculog", + "var/adm/vold.log", + "var/adm/log/asppp.log", + "var/lp/logs/lpsched", + "var/lp/logs/lpnet", + "var/lp/logs/requests", + "var/cron/log", + "var/saf/_log", + "var/saf/port/log", + "tmp/access.log", + "etc/sensors.conf", + "etc/sensors3.conf", + "etc/host.conf", + "etc/pam.conf", + "etc/resolv.conf", + "etc/apt/apt.conf", + "etc/inetd.conf", + "etc/syslog.conf", + "etc/sysctl.conf", + "etc/sysctl.d/10-console-messages.conf", + "etc/sysctl.d/10-network-security.conf", + "etc/sysctl.d/10-process-security.conf", + "etc/sysctl.d/wine.sysctl.conf", + "etc/security/access.conf", + "etc/security/group.conf", + "etc/security/limits.conf", + "etc/security/namespace.conf", + "etc/security/pam_env.conf", + "etc/security/sepermit.conf", + "etc/security/time.conf", + "etc/ssh/sshd_config", + "etc/adduser.conf", + "etc/deluser.conf", + "etc/avahi/avahi-daemon.conf", + "etc/ca-certificates.conf", + "etc/ca-certificates.conf.dpkg-old", + "etc/casper.conf", + "etc/chkrootkit.conf", + "etc/debconf.conf", + "etc/dns2tcpd.conf", + "etc/e2fsck.conf", + "etc/esound/esd.conf", + "etc/etter.conf", + "etc/fuse.conf", + "etc/foremost.conf", + "etc/hdparm.conf", + "etc/kernel-img.conf", + "etc/kernel-pkg.conf", + "etc/ld.so.conf", + "etc/ltrace.conf", + "etc/mail/sendmail.conf", + "etc/manpath.config", + "etc/kbd/config", + "etc/ldap/ldap.conf", + "etc/logrotate.conf", + "etc/mtools.conf", + "etc/smi.conf", + "etc/updatedb.conf", + "etc/pulse/client.conf", + "usr/share/adduser/adduser.conf", + "etc/hostname", + "etc/networks", + "etc/timezone", + "etc/modules", + "etc/passwd", + "etc/shadow", + "etc/fstab", + "etc/motd", + "etc/hosts", + "etc/group", + "etc/alias", + "etc/crontab", + "etc/crypttab", + "etc/exports", + "etc/mtab", + "etc/hosts.allow", + "etc/hosts.deny", + "etc/os-release", + "etc/password.master", + "etc/profile", + "etc/default/grub", + "etc/resolvconf/update-libc.d/sendmail", + "etc/inittab", + "etc/issue", + "etc/issue.net", + "etc/login.defs", + "etc/sudoers", + "etc/sysconfig/network-scripts/ifcfg-eth0", + "etc/redhat-release", + "etc/scw-release", + "etc/system-release-cpe", + "etc/debian_version", + "etc/fedora-release", + "etc/mandrake-release", + "etc/slackware-release", + "etc/suse-release", + "etc/security/group", + "etc/security/passwd", + "etc/security/user", + "etc/security/environ", + "etc/security/limits", + "etc/security/opasswd", + "boot/grub/grub.cfg", + "boot/grub/menu.lst", + "root/.ksh_history", + "root/.xauthority", + "usr/lib/security/mkuser.default", + "var/lib/squirrelmail/prefs/squirrelmail.log", + "etc/squirrelmail/apache.conf", + "etc/squirrelmail/config_local.php", + "etc/squirrelmail/default_pref", + "etc/squirrelmail/index.php", + "etc/squirrelmail/config_default.php", + "etc/squirrelmail/config.php", + "etc/squirrelmail/filters_setup.php", + "etc/squirrelmail/sqspell_config.php", + "etc/squirrelmail/config/config.php", + "etc/httpd/conf.d/squirrelmail.conf", + "usr/share/squirrelmail/config/config.php", + "private/etc/squirrelmail/config/config.php", + "srv/www/htdos/squirrelmail/config/config.php", + "var/www/squirrelmail/config/config.php", + "var/www/html/squirrelmail/config/config.php", + "var/www/html/squirrelmail-1.2.9/config/config.php", + "usr/share/squirrelmail/plugins/squirrel_logger/setup.php", + "usr/local/squirrelmail/www/readme", + "windows/system32/drivers/etc/hosts", + "windows/system32/drivers/etc/lmhosts.sam", + "windows/system32/drivers/etc/networks", + "windows/system32/drivers/etc/protocol", + "windows/system32/drivers/etc/services", + "/boot.ini", + "windows/debug/netsetup.log", + "windows/comsetup.log", + "windows/repair/setup.log", + "windows/setupact.log", + "windows/setupapi.log", + "windows/setuperr.log", + "windows/updspapi.log", + "windows/wmsetup.log", + "windows/windowsupdate.log", + "windows/odbc.ini", + "usr/local/psa/admin/htdocs/domains/databases/phpmyadmin/libraries/config.default.php", + "etc/apache2/conf.d/phpmyadmin.conf", + "etc/phpmyadmin/config.inc.php", + "etc/openldap/ldap.conf", + "etc/cups/acroread.conf", + "etc/cups/cupsd.conf", + "etc/cups/cupsd.conf.default", + "etc/cups/pdftops.conf", + "etc/cups/printers.conf", + "windows/system32/macromed/flash/flashinstall.log", + "windows/system32/macromed/flash/install.log", + "etc/cvs-cron.conf", + "etc/cvs-pserver.conf", + "etc/subversion/config", + "etc/modprobe.d/vmware-tools.conf", + "etc/updatedb.conf.beforevmwaretoolsinstall", + "etc/vmware-tools/config", + "etc/vmware-tools/tpvmlp.conf", + "etc/vmware-tools/vmware-tools-libraries.conf", + "var/log", + "var/log/sw-cp-server/error_log", + "var/log/sso/sso.log", + "var/log/dpkg.log", + "var/log/btmp", + "var/log/utmp", + "var/log/wtmp", + "var/log/mysql/mysql-bin.log", + "var/log/mysql/mysql-bin.index", + "var/log/mysql/data/mysql-bin.index", + "var/log/mysql.log", + "var/log/mysql.err", + "var/log/mysqlderror.log", + "var/log/mysql/mysql.log", + "var/log/mysql/mysql-slow.log", + "var/log/mysql-bin.index", + "var/log/data/mysql-bin.index", + "var/log/postgresql/postgresql.log", + "var/log/postgres/pg_backup.log", + "var/log/postgres/postgres.log", + "var/log/postgresql.log", + "var/log/pgsql/pgsql.log", + "var/log/postgresql/postgresql-8.1-main.log", + "var/log/postgresql/postgresql-8.3-main.log", + "var/log/postgresql/postgresql-8.4-main.log", + "var/log/postgresql/postgresql-9.0-main.log", + "var/log/postgresql/postgresql-9.1-main.log", + "var/log/pgsql8.log", + "var/log/postgresql/postgres.log", + "var/log/pgsql_log", + "var/log/postgresql/main.log", + "var/log/cron", + "var/log/postgres.log", + "var/log/proftpd", + "var/log/proftpd/xferlog.legacy", + "var/log/proftpd.access_log", + "var/log/proftpd.xferlog", + "var/log/vsftpd.log", + "var/log/xferlog", + "var/log/pure-ftpd/pure-ftpd.log", + "var/log/pureftpd.log", + "var/log/muddleftpd", + "var/log/muddleftpd.conf", + "var/log/ftp-proxy/ftp-proxy.log", + "var/log/ftp-proxy", + "var/log/ftplog", + "var/log/exim_mainlog", + "var/log/exim/mainlog", + "var/log/maillog", + "var/log/exim_paniclog", + "var/log/exim/paniclog", + "var/log/exim/rejectlog", + "var/log/exim_rejectlog", + "var/log/webmin/miniserv.log", + "var/log/httpd/access_log", + "var/log/httpd/error_log", + "var/log/httpd/access.log", + "var/log/httpd/error.log", + "var/log/apache/access_log", + "var/log/apache/access.log", + "var/log/apache/error_log", + "var/log/apache/error.log", + "var/log/apache2/access_log", + "var/log/apache2/access.log", + "var/log/apache2/error_log", + "var/log/apache2/error.log", + "var/log/access_log", + "var/log/access.log", + "var/log/error_log", + "var/log/error.log", + "var/log/tomcat6/catalina.out", + "var/log/lighttpd.error.log", + "var/log/lighttpd.access.log", + "var/logs/access.log", + "var/log/lighttpd/", + "var/log/lighttpd/error.log", + "var/log/lighttpd/access.www.log", + "var/log/lighttpd/error.www.log", + "var/log/lighttpd/access.log", + "var/log/lighttpd/{domain}/access.log", + "var/log/lighttpd/{domain}/error.log", + "var/log/nginx/access_log", + "var/log/nginx/error_log", + "var/log/nginx/access.log", + "var/log/nginx/error.log", + "var/log/nginx.access_log", + "var/log/nginx.error_log", + "var/log/samba/log.smbd", + "var/log/samba/log.nmbd", + "var/log/samba.log", + "var/log/samba.log1", + "var/log/samba.log2", + "var/log/log.smb", + "var/log/ipfw.log", + "var/log/ipfw", + "var/log/ipfw/ipfw.log", + "var/log/ipfw.today", + "var/log/poplog", + "var/log/authlog", + "var/log/news.all", + "var/log/news/news.all", + "var/log/news/news.crit", + "var/log/news/news.err", + "var/log/news/news.notice", + "var/log/news/suck.err", + "var/log/news/suck.notice", + "var/log/messages", + "var/log/messages.1", + "var/log/user.log", + "var/log/user.log.1", + "var/log/auth.log", + "var/log/pm-powersave.log", + "var/log/xorg.0.log", + "var/log/daemon.log", + "var/log/daemon.log.1", + "var/log/kern.log", + "var/log/kern.log.1", + "var/log/mail.err", + "var/log/mail.info", + "var/log/mail.warn", + "var/log/ufw.log", + "var/log/boot.log", + "var/log/syslog", + "var/log/syslog.1", + "var/log/squirrelmail.log", + "var/log/apache2/squirrelmail.log", + "var/log/apache2/squirrelmail.err.log", + "var/log/mail.log", + "var/log/vmware/hostd.log", + "var/log/vmware/hostd-1.log", + "/wp-config.php", + "/wp-config.bak", + "/wp-config.old", + "/wp-config.temp", + "/wp-config.tmp", + "/wp-config.txt", + "/config.yml", + "/config_dev.yml", + "/config_prod.yml", + "/config_test.yml", + "/parameters.yml", + "/routing.yml", + "/security.yml", + "/services.yml", + "sites/default/default.settings.php", + "sites/default/settings.php", + "sites/default/settings.local.php", + "app/etc/local.xml", + "/sftp-config.json", + "/web.config", + "includes/config.php", + "includes/configure.php", + "/config.inc.php", + "/localsettings.php", + "inc/config.php", + "typo3conf/localconf.php", + "config/app.php", + "config/custom.php", + "config/database.php", + "/configuration.php", + "/config.php", + "var/mail/www-data", + "etc/network/", + "etc/init/", + "inetpub/wwwroot/global.asa", + "system32/inetsrv/config/applicationhost.config", + "system32/inetsrv/config/administration.config", + "system32/inetsrv/config/redirection.config", + "system32/config/default", + "system32/config/sam", + "system32/config/system", + "system32/config/software", + "winnt/repair/sam._", + "/package.json", + "/package-lock.json", + "/gruntfile.js", + "/npm-debug.log", + "/ormconfig.json", + "/tsconfig.json", + "/webpack.config.js", + "/yarn.lock", + "proc/0", + "proc/1", + "proc/2", + "proc/3", + "proc/4", + "proc/5", + "proc/6", + "proc/7", + "proc/8", + "proc/9", + "proc/acpi", + "proc/asound", + "proc/bootconfig", + "proc/buddyinfo", + "proc/bus", + "proc/cgroups", + "proc/cmdline", + "proc/config.gz", + "proc/consoles", + "proc/cpuinfo", + "proc/crypto", + "proc/devices", + "proc/diskstats", + "proc/dma", + "proc/docker", + "proc/driver", + "proc/dynamic_debug", + "proc/execdomains", + "proc/fb", + "proc/filesystems", + "proc/fs", + "proc/interrupts", + "proc/iomem", + "proc/ioports", + "proc/ipmi", + "proc/irq", + "proc/kallsyms", + "proc/kcore", + "proc/keys", + "proc/keys", + "proc/key-users", + "proc/kmsg", + "proc/kpagecgroup", + "proc/kpagecount", + "proc/kpageflags", + "proc/latency_stats", + "proc/loadavg", + "proc/locks", + "proc/mdstat", + "proc/meminfo", + "proc/misc", + "proc/modules", + "proc/mounts", + "proc/mpt", + "proc/mtd", + "proc/mtrr", + "proc/net", + "proc/net/tcp", + "proc/net/udp", + "proc/pagetypeinfo", + "proc/partitions", + "proc/pressure", + "proc/sched_debug", + "proc/schedstat", + "proc/scsi", + "proc/self", + "proc/self/cmdline", + "proc/self/environ", + "proc/self/fd/0", + "proc/self/fd/1", + "proc/self/fd/10", + "proc/self/fd/11", + "proc/self/fd/12", + "proc/self/fd/13", + "proc/self/fd/14", + "proc/self/fd/15", + "proc/self/fd/2", + "proc/self/fd/3", + "proc/self/fd/4", + "proc/self/fd/5", + "proc/self/fd/6", + "proc/self/fd/7", + "proc/self/fd/8", + "proc/self/fd/9", + "proc/self/mounts", + "proc/self/stat", + "proc/self/status", + "proc/slabinfo", + "proc/softirqs", + "proc/stat", + "proc/swaps", + "proc/sys", + "proc/sysrq-trigger", + "proc/sysvipc", + "proc/thread-self", + "proc/timer_list", + "proc/timer_stats", + "proc/tty", + "proc/uptime", + "proc/version", + "proc/version_signature", + "proc/vmallocinfo", + "proc/vmstat", + "proc/zoneinfo", + "sys/block", + "sys/bus", + "sys/class", + "sys/dev", + "sys/devices", + "sys/firmware", + "sys/fs", + "sys/hypervisor", + "sys/kernel", + "sys/module", + "sys/power", + "windows\\win.ini", + "default\\ntuser.dat", + "/var/run/secrets/kubernetes.io/serviceaccount" + ], + "options": { + "enforce_word_boundary": true + } + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase", + "normalizePath" + ] + }, + { + "id": "crs-931-110", + "name": "RFI: Common RFI Vulnerable Parameter Name used w/ URL Payload", + "tags": { + "type": "rfi", + "crs_id": "931110", + "category": "attack_attempt", + "cwe": "98", + "capec": "1000/152/175/253/193", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + } + ], + "regex": "(?:\\binclude\\s*\\([^)]*|mosConfig_absolute_path|_CONF\\[path\\]|_SERVER\\[DOCUMENT_ROOT\\]|GALLERY_BASEDIR|path\\[docroot\\]|appserv_root|config\\[root_dir\\])=(?:file|ftps?|https?)://", + "options": { + "min_length": 15 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-931-120", + "name": "RFI: URL Payload Used w/Trailing Question Mark Character (?)", + "tags": { + "type": "rfi", + "crs_id": "931120", + "category": "attack_attempt", + "cwe": "98", + "capec": "1000/152/175/253/193" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^(?i:file|ftps?)://.*?\\?+$", + "options": { + "case_sensitive": true, + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-932-160", + "name": "Remote Command Execution: Unix Shell Code Found", + "tags": { + "type": "command_injection", + "crs_id": "932160", + "category": "attack_attempt", + "cwe": "77", + "capec": "1000/152/248/88", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "options": { + "enforce_word_boundary": true + }, + "list": [ + "${cdpath}", + "${dirstack}", + "${home}", + "${hostname}", + "${ifs}", + "${oldpwd}", + "${ostype}", + "${path}", + "${pwd}", + "$cdpath", + "$dirstack", + "$home", + "$hostname", + "$ifs", + "$oldpwd", + "$ostype", + "$path", + "$pwd", + "dev/fd/", + "dev/null", + "dev/stderr", + "dev/stdin", + "dev/stdout", + "dev/tcp/", + "dev/udp/", + "dev/zero", + "etc/master.passwd", + "etc/pwd.db", + "etc/shells", + "etc/spwd.db", + "proc/self/", + "bin/7z", + "bin/7za", + "bin/7zr", + "bin/ab", + "bin/agetty", + "bin/ansible-playbook", + "bin/apt", + "bin/apt-get", + "bin/ar", + "bin/aria2c", + "bin/arj", + "bin/arp", + "bin/as", + "bin/ascii-xfr", + "bin/ascii85", + "bin/ash", + "bin/aspell", + "bin/at", + "bin/atobm", + "bin/awk", + "bin/base32", + "bin/base64", + "bin/basenc", + "bin/bash", + "bin/bpftrace", + "bin/bridge", + "bin/bundler", + "bin/bunzip2", + "bin/busctl", + "bin/busybox", + "bin/byebug", + "bin/bzcat", + "bin/bzcmp", + "bin/bzdiff", + "bin/bzegrep", + "bin/bzexe", + "bin/bzfgrep", + "bin/bzgrep", + "bin/bzip2", + "bin/bzip2recover", + "bin/bzless", + "bin/bzmore", + "bin/bzz", + "bin/c89", + "bin/c99", + "bin/cancel", + "bin/capsh", + "bin/cat", + "bin/cc", + "bin/certbot", + "bin/check_by_ssh", + "bin/check_cups", + "bin/check_log", + "bin/check_memory", + "bin/check_raid", + "bin/check_ssl_cert", + "bin/check_statusfile", + "bin/chmod", + "bin/choom", + "bin/chown", + "bin/chroot", + "bin/clang", + "bin/clang++", + "bin/cmp", + "bin/cobc", + "bin/column", + "bin/comm", + "bin/composer", + "bin/core_perl/zipdetails", + "bin/cowsay", + "bin/cowthink", + "bin/cp", + "bin/cpan", + "bin/cpio", + "bin/cpulimit", + "bin/crash", + "bin/crontab", + "bin/csh", + "bin/csplit", + "bin/csvtool", + "bin/cupsfilter", + "bin/curl", + "bin/cut", + "bin/dash", + "bin/date", + "bin/dd", + "bin/dev/fd/", + "bin/dev/null", + "bin/dev/stderr", + "bin/dev/stdin", + "bin/dev/stdout", + "bin/dev/tcp/", + "bin/dev/udp/", + "bin/dev/zero", + "bin/dialog", + "bin/diff", + "bin/dig", + "bin/dmesg", + "bin/dmidecode", + "bin/dmsetup", + "bin/dnf", + "bin/docker", + "bin/dosbox", + "bin/dpkg", + "bin/du", + "bin/dvips", + "bin/easy_install", + "bin/eb", + "bin/echo", + "bin/ed", + "bin/efax", + "bin/emacs", + "bin/env", + "bin/eqn", + "bin/es", + "bin/esh", + "bin/etc/group", + "bin/etc/master.passwd", + "bin/etc/passwd", + "bin/etc/pwd.db", + "bin/etc/shadow", + "bin/etc/shells", + "bin/etc/spwd.db", + "bin/ex", + "bin/exiftool", + "bin/expand", + "bin/expect", + "bin/expr", + "bin/facter", + "bin/fetch", + "bin/file", + "bin/find", + "bin/finger", + "bin/fish", + "bin/flock", + "bin/fmt", + "bin/fold", + "bin/fping", + "bin/ftp", + "bin/gawk", + "bin/gcc", + "bin/gcore", + "bin/gdb", + "bin/gem", + "bin/genie", + "bin/genisoimage", + "bin/ghc", + "bin/ghci", + "bin/gimp", + "bin/ginsh", + "bin/git", + "bin/grc", + "bin/grep", + "bin/gtester", + "bin/gunzip", + "bin/gzexe", + "bin/gzip", + "bin/hd", + "bin/head", + "bin/hexdump", + "bin/highlight", + "bin/hping3", + "bin/iconv", + "bin/id", + "bin/iftop", + "bin/install", + "bin/ionice", + "bin/ip", + "bin/irb", + "bin/ispell", + "bin/jjs", + "bin/join", + "bin/journalctl", + "bin/jq", + "bin/jrunscript", + "bin/knife", + "bin/ksh", + "bin/ksshell", + "bin/latex", + "bin/ld", + "bin/ldconfig", + "bin/less", + "bin/lftp", + "bin/ln", + "bin/loginctl", + "bin/logsave", + "bin/look", + "bin/lp", + "bin/ls", + "bin/ltrace", + "bin/lua", + "bin/lualatex", + "bin/luatex", + "bin/lwp-download", + "bin/lwp-request", + "bin/lz", + "bin/lz4", + "bin/lz4c", + "bin/lz4cat", + "bin/lzcat", + "bin/lzcmp", + "bin/lzdiff", + "bin/lzegrep", + "bin/lzfgrep", + "bin/lzgrep", + "bin/lzless", + "bin/lzma", + "bin/lzmadec", + "bin/lzmainfo", + "bin/lzmore", + "bin/mail", + "bin/make", + "bin/man", + "bin/mawk", + "bin/mkfifo", + "bin/mknod", + "bin/more", + "bin/mosquitto", + "bin/mount", + "bin/msgattrib", + "bin/msgcat", + "bin/msgconv", + "bin/msgfilter", + "bin/msgmerge", + "bin/msguniq", + "bin/mtr", + "bin/mv", + "bin/mysql", + "bin/nano", + "bin/nasm", + "bin/nawk", + "bin/nc", + "bin/ncat", + "bin/neofetch", + "bin/nice", + "bin/nl", + "bin/nm", + "bin/nmap", + "bin/node", + "bin/nohup", + "bin/npm", + "bin/nroff", + "bin/nsenter", + "bin/octave", + "bin/od", + "bin/openssl", + "bin/openvpn", + "bin/openvt", + "bin/opkg", + "bin/paste", + "bin/pax", + "bin/pdb", + "bin/pdflatex", + "bin/pdftex", + "bin/pdksh", + "bin/perf", + "bin/perl", + "bin/pg", + "bin/php", + "bin/php-cgi", + "bin/php5", + "bin/php7", + "bin/pic", + "bin/pico", + "bin/pidstat", + "bin/pigz", + "bin/pip", + "bin/pkexec", + "bin/pkg", + "bin/pr", + "bin/printf", + "bin/proc/self/", + "bin/pry", + "bin/ps", + "bin/psed", + "bin/psftp", + "bin/psql", + "bin/ptx", + "bin/puppet", + "bin/pxz", + "bin/python", + "bin/python2", + "bin/python3", + "bin/rake", + "bin/rbash", + "bin/rc", + "bin/readelf", + "bin/red", + "bin/redcarpet", + "bin/restic", + "bin/rev", + "bin/rlogin", + "bin/rlwrap", + "bin/rpm", + "bin/rpmquery", + "bin/rsync", + "bin/ruby", + "bin/run-mailcap", + "bin/run-parts", + "bin/rview", + "bin/rvim", + "bin/sash", + "bin/sbin/capsh", + "bin/sbin/logsave", + "bin/sbin/service", + "bin/sbin/start-stop-daemon", + "bin/scp", + "bin/screen", + "bin/script", + "bin/sed", + "bin/service", + "bin/setarch", + "bin/sftp", + "bin/sg", + "bin/sh", + "bin/shuf", + "bin/sleep", + "bin/slsh", + "bin/smbclient", + "bin/snap", + "bin/socat", + "bin/soelim", + "bin/sort", + "bin/split", + "bin/sqlite3", + "bin/ss", + "bin/ssh", + "bin/ssh-keygen", + "bin/ssh-keyscan", + "bin/sshpass", + "bin/start-stop-daemon", + "bin/stdbuf", + "bin/strace", + "bin/strings", + "bin/su", + "bin/sysctl", + "bin/systemctl", + "bin/systemd-resolve", + "bin/tac", + "bin/tail", + "bin/tar", + "bin/task", + "bin/taskset", + "bin/tbl", + "bin/tclsh", + "bin/tcpdump", + "bin/tcsh", + "bin/tee", + "bin/telnet", + "bin/tex", + "bin/tftp", + "bin/tic", + "bin/time", + "bin/timedatectl", + "bin/timeout", + "bin/tmux", + "bin/top", + "bin/troff", + "bin/tshark", + "bin/ul", + "bin/uname", + "bin/uncompress", + "bin/unexpand", + "bin/uniq", + "bin/unlz4", + "bin/unlzma", + "bin/unpigz", + "bin/unrar", + "bin/unshare", + "bin/unxz", + "bin/unzip", + "bin/unzstd", + "bin/update-alternatives", + "bin/uudecode", + "bin/uuencode", + "bin/valgrind", + "bin/vi", + "bin/view", + "bin/vigr", + "bin/vim", + "bin/vimdiff", + "bin/vipw", + "bin/virsh", + "bin/volatility", + "bin/wall", + "bin/watch", + "bin/wc", + "bin/wget", + "bin/whiptail", + "bin/who", + "bin/whoami", + "bin/whois", + "bin/wireshark", + "bin/wish", + "bin/xargs", + "bin/xelatex", + "bin/xetex", + "bin/xmodmap", + "bin/xmore", + "bin/xpad", + "bin/xxd", + "bin/xz", + "bin/xzcat", + "bin/xzcmp", + "bin/xzdec", + "bin/xzdiff", + "bin/xzegrep", + "bin/xzfgrep", + "bin/xzgrep", + "bin/xzless", + "bin/xzmore", + "bin/yarn", + "bin/yelp", + "bin/yes", + "bin/yum", + "bin/zathura", + "bin/zip", + "bin/zipcloak", + "bin/zipcmp", + "bin/zipdetails", + "bin/zipgrep", + "bin/zipinfo", + "bin/zipmerge", + "bin/zipnote", + "bin/zipsplit", + "bin/ziptool", + "bin/zsh", + "bin/zsoelim", + "bin/zstd", + "bin/zstdcat", + "bin/zstdgrep", + "bin/zstdless", + "bin/zstdmt", + "bin/zypper" + ] + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase", + "cmdLine" + ] + }, + { + "id": "crs-932-171", + "name": "Remote Command Execution: Shellshock (CVE-2014-6271)", + "tags": { + "type": "command_injection", + "crs_id": "932171", + "category": "attack_attempt", + "cwe": "77", + "capec": "1000/152/248/88", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^\\(\\s*\\)\\s+{", + "options": { + "case_sensitive": true, + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-932-180", + "name": "Restricted File Upload Attempt", + "tags": { + "type": "command_injection", + "crs_id": "932180", + "category": "attack_attempt", + "cwe": "706", + "capec": "1000/225/122/17/177", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x-filename" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x_filename" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x-file-name" + ] + } + ], + "list": [ + ".htaccess", + ".htdigest", + ".htpasswd", + "wp-config.php", + "config.yml", + "config_dev.yml", + "config_prod.yml", + "config_test.yml", + "parameters.yml", + "routing.yml", + "security.yml", + "services.yml", + "default.settings.php", + "settings.php", + "settings.local.php", + "local.xml", + ".env" + ], + "options": { + "enforce_word_boundary": true + } + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-933-111", + "name": "PHP Injection Attack: PHP Script File Upload Found", + "tags": { + "type": "unrestricted_file_upload", + "crs_id": "933111", + "category": "attack_attempt", + "cwe": "434", + "capec": "1000/225/122/17/650", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x-filename" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x_filename" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x.filename" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "x-file-name" + ] + } + ], + "regex": ".*\\.(?:php\\d*|phtml)\\..*$", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-933-130", + "name": "PHP Injection Attack: Global Variables Found", + "tags": { + "type": "php_code_injection", + "crs_id": "933130", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/225/122/17/650", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "options": { + "enforce_word_boundary": true + }, + "list": [ + "$globals", + "$_cookie", + "$_env", + "$_files", + "$_get", + "$_post", + "$_request", + "$_server", + "$_session", + "$argc", + "$argv", + "$http_\\u200bresponse_\\u200bheader", + "$php_\\u200berrormsg", + "$http_cookie_vars", + "$http_env_vars", + "$http_get_vars", + "$http_post_files", + "$http_post_vars", + "$http_raw_post_data", + "$http_request_vars", + "$http_server_vars" + ] + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-933-131", + "name": "PHP Injection Attack: HTTP Headers Values Found", + "tags": { + "type": "php_code_injection", + "crs_id": "933131", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/225/122/17/650" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:HTTP_(?:ACCEPT(?:_(?:ENCODING|LANGUAGE|CHARSET))?|(?:X_FORWARDED_FO|REFERE)R|(?:USER_AGEN|HOS)T|CONNECTION|KEEP_ALIVE)|PATH_(?:TRANSLATED|INFO)|ORIG_PATH_INFO|QUERY_STRING|REQUEST_URI|AUTH_TYPE)", + "options": { + "case_sensitive": true, + "min_length": 9 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-933-140", + "name": "PHP Injection Attack: I/O Stream Found", + "tags": { + "type": "php_code_injection", + "crs_id": "933140", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/225/122/17/650", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "php://(?:std(?:in|out|err)|(?:in|out)put|fd|memory|temp|filter)", + "options": { + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-933-150", + "name": "PHP Injection Attack: High-Risk PHP Function Name Found", + "tags": { + "type": "php_code_injection", + "crs_id": "933150", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/225/122/17/650", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "list": [ + "__halt_compiler", + "apache_child_terminate", + "base64_decode", + "bzdecompress", + "call_user_func", + "call_user_func_array", + "call_user_method", + "call_user_method_array", + "convert_uudecode", + "file_get_contents", + "file_put_contents", + "fsockopen", + "get_class_methods", + "get_class_vars", + "get_defined_constants", + "get_defined_functions", + "get_defined_vars", + "gzdecode", + "gzinflate", + "gzuncompress", + "include_once", + "invokeargs", + "pcntl_exec", + "pcntl_fork", + "pfsockopen", + "posix_getcwd", + "posix_getpwuid", + "posix_getuid", + "posix_uname", + "reflectionfunction", + "require_once", + "shell_exec", + "str_rot13", + "sys_get_temp_dir", + "wp_remote_fopen", + "wp_remote_get", + "wp_remote_head", + "wp_remote_post", + "wp_remote_request", + "wp_safe_remote_get", + "wp_safe_remote_head", + "wp_safe_remote_post", + "wp_safe_remote_request", + "zlib_decode" + ], + "options": { + "enforce_word_boundary": true + } + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-933-160", + "name": "PHP Injection Attack: High-Risk PHP Function Call Found", + "tags": { + "type": "php_code_injection", + "crs_id": "933160", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/225/122/17/650" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:s(?:e(?:t(?:_(?:e(?:xception|rror)_handler|magic_quotes_runtime|include_path)|defaultstub)|ssion_s(?:et_save_handler|tart))|qlite_(?:(?:(?:unbuffered|single|array)_)?query|create_(?:aggregate|function)|p?open|exec)|tr(?:eam_(?:context_create|socket_client)|ipc?slashes|rev)|implexml_load_(?:string|file)|ocket_c(?:onnect|reate)|h(?:ow_sourc|a1_fil)e|pl_autoload_register|ystem)|p(?:r(?:eg_(?:replace(?:_callback(?:_array)?)?|match(?:_all)?|split)|oc_(?:(?:terminat|clos|nic)e|get_status|open)|int_r)|o(?:six_(?:get(?:(?:e[gu]|g)id|login|pwnam)|mk(?:fifo|nod)|ttyname|kill)|pen)|hp(?:_(?:strip_whitespac|unam)e|version|info)|g_(?:(?:execut|prepar)e|connect|query)|a(?:rse_(?:ini_file|str)|ssthru)|utenv)|r(?:unkit_(?:function_(?:re(?:defin|nam)e|copy|add)|method_(?:re(?:defin|nam)e|copy|add)|constant_(?:redefine|add))|e(?:(?:gister_(?:shutdown|tick)|name)_function|ad(?:(?:gz)?file|_exif_data|dir))|awurl(?:de|en)code)|i(?:mage(?:createfrom(?:(?:jpe|pn)g|x[bp]m|wbmp|gif)|(?:jpe|pn)g|g(?:d2?|if)|2?wbmp|xbm)|s_(?:(?:(?:execut|write?|read)ab|fi)le|dir)|ni_(?:get(?:_all)?|set)|terator_apply|ptcembed)|g(?:et(?:_(?:c(?:urrent_use|fg_va)r|meta_tags)|my(?:[gpu]id|inode)|(?:lastmo|cw)d|imagesize|env)|z(?:(?:(?:defla|wri)t|encod|fil)e|compress|open|read)|lob)|a(?:rray_(?:u(?:intersect(?:_u?assoc)?|diff(?:_u?assoc)?)|intersect_u(?:assoc|key)|diff_u(?:assoc|key)|filter|reduce|map)|ssert(?:_options)?|tob)|h(?:tml(?:specialchars(?:_decode)?|_entity_decode|entities)|(?:ash(?:_(?:update|hmac))?|ighlight)_file|e(?:ader_register_callback|x2bin))|f(?:i(?:le(?:(?:[acm]tim|inod)e|(?:_exist|perm)s|group)?|nfo_open)|tp_(?:nb_(?:ge|pu)|connec|ge|pu)t|(?:unction_exis|pu)ts|write|open)|o(?:b_(?:get_(?:c(?:ontents|lean)|flush)|end_(?:clean|flush)|clean|flush|start)|dbc_(?:result(?:_all)?|exec(?:ute)?|connect)|pendir)|m(?:b_(?:ereg(?:_(?:replace(?:_callback)?|match)|i(?:_replace)?)?|parse_str)|(?:ove_uploaded|d5)_file|ethod_exists|ysql_query|kdir)|e(?:x(?:if_(?:t(?:humbnail|agname)|imagetype|read_data)|ec)|scapeshell(?:arg|cmd)|rror_reporting|val)|c(?:url_(?:file_create|exec|init)|onvert_uuencode|reate_function|hr)|u(?:n(?:serialize|pack)|rl(?:de|en)code|[ak]?sort)|b(?:(?:son_(?:de|en)|ase64_en)code|zopen|toa)|(?:json_(?:de|en)cod|debug_backtrac|tmpfil)e|var_dump)(?:\\s|/\\*.*\\*/|//.*|#.*|\\\"|')*\\((?:(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:\\$\\w+|[A-Z\\d]\\w*|\\w+\\(.*\\)|\\\\?\"(?:[^\"]|\\\\\"|\"\"|\"\\+\")*\\\\?\"|\\\\?'(?:[^']|''|'\\+')*\\\\?')(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:(?:::|\\.|->)(?:\\s|/\\*.*\\*/|//.*|#.*)*\\w+(?:\\(.*\\))?)?,)*(?:(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:\\$\\w+|[A-Z\\d]\\w*|\\w+\\(.*\\)|\\\\?\"(?:[^\"]|\\\\\"|\"\"|\"\\+\")*\\\\?\"|\\\\?'(?:[^']|''|'\\+')*\\\\?')(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:(?:::|\\.|->)(?:\\s|/\\*.*\\*/|//.*|#.*)*\\w+(?:\\(.*\\))?)?)?\\)", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-933-170", + "name": "PHP Injection Attack: Serialized Object Injection", + "tags": { + "type": "php_code_injection", + "crs_id": "933170", + "category": "attack_attempt", + "cwe": "502", + "capec": "1000/152/586", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "[oOcC]:\\d+:\\\".+?\\\":\\d+:{[\\W\\w]*}", + "options": { + "case_sensitive": true, + "min_length": 12 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-933-200", + "name": "PHP Injection Attack: Wrapper scheme detected", + "tags": { + "type": "php_code_injection", + "crs_id": "933200", + "category": "attack_attempt", + "cwe": "502", + "capec": "1000/152/586" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:(?:bzip|ssh)2|z(?:lib|ip)|(?:ph|r)ar|expect|glob|ogg)://", + "options": { + "case_sensitive": true, + "min_length": 6 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-934-100", + "name": "Node.js Injection Attack 1/2", + "tags": { + "type": "js_code_injection", + "crs_id": "934100", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:(?:l(?:(?:utimes|chmod)(?:Sync)?|(?:stat|ink)Sync)|w(?:rite(?:(?:File|v)(?:Sync)?|Sync)|atchFile)|u(?:n(?:watchFile|linkSync)|times(?:Sync)?)|s(?:(?:ymlink|tat)Sync|pawn(?:File|Sync))|ex(?:ec(?:File(?:Sync)?|Sync)|istsSync)|a(?:ppendFile|ccess)(?:Sync)?|(?:Caveat|Inode)s|open(?:dir)?Sync|new\\s+Function|Availability|\\beval)\\s*\\(|m(?:ain(?:Module\\s*(?:\\W*\\s*(?:constructor|require)|\\[)|\\s*(?:\\W*\\s*(?:constructor|require)|\\[))|kd(?:temp(?:Sync)?|irSync)\\s*\\(|odule\\.exports\\s*=)|c(?:(?:(?:h(?:mod|own)|lose)Sync|reate(?:Write|Read)Stream|p(?:Sync)?)\\s*\\(|o(?:nstructor\\s*(?:\\W*\\s*_load|\\[)|pyFile(?:Sync)?\\s*\\())|f(?:(?:(?:s(?:(?:yncS)?|tatS)|datas(?:yncS)?)ync|ch(?:mod|own)(?:Sync)?)\\s*\\(|u(?:nction\\s*\\(\\s*\\)\\s*{|times(?:Sync)?\\s*\\())|r(?:e(?:(?:ad(?:(?:File|link|dir)?Sync|v(?:Sync)?)|nameSync)\\s*\\(|quire\\s*(?:\\W*\\s*main|\\[))|m(?:Sync)?\\s*\\()|process\\s*(?:\\W*\\s*(?:mainModule|binding)|\\[)|t(?:his\\.constructor|runcateSync\\s*\\()|_(?:\\$\\$ND_FUNC\\$\\$_|_js_function)|global\\s*(?:\\W*\\s*process|\\[)|String\\s*\\.\\s*fromCharCode|binding\\s*\\[)", + "options": { + "case_sensitive": true, + "min_length": 3 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-934-101", + "name": "Node.js Injection Attack 2/2", + "tags": { + "type": "js_code_injection", + "crs_id": "934101", + "category": "attack_attempt", + "confidence": "1", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:w(?:atch|rite)|(?:spaw|ope)n|exists|close|fork|read)\\s*\\(", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-941-110", + "name": "XSS Filter - Category 1: Script Tag Vector", + "tags": { + "type": "xss", + "crs_id": "941110", + "category": "attack_attempt", + "cwe": "80", + "capec": "1000/152/242/63/591", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "referer" + ] + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "]*>[\\s\\S]*?", + "options": { + "case_sensitive": false, + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls", + "urlDecodeUni" + ] + }, + { + "id": "crs-941-120", + "name": "XSS Filter - Category 2: Event Handler Vector", + "tags": { + "type": "xss", + "crs_id": "941120", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "referer" + ] + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bon(?:d(?:r(?:ag(?:en(?:ter|d)|leave|start|over)?|op)|urationchange|blclick)|s(?:e(?:ek(?:ing|ed)|arch|lect)|u(?:spend|bmit)|talled|croll|how)|m(?:ouse(?:(?:lea|mo)ve|o(?:ver|ut)|enter|down|up)|essage)|p(?:a(?:ge(?:hide|show)|(?:st|us)e)|lay(?:ing)?|rogress|aste|ointer(?:cancel|down|enter|leave|move|out|over|rawupdate|up))|c(?:anplay(?:through)?|o(?:ntextmenu|py)|hange|lick|ut)|a(?:nimation(?:iteration|start|end)|(?:fterprin|bor)t|uxclick|fterscriptexecute)|t(?:o(?:uch(?:cancel|start|move|end)|ggle)|imeupdate)|f(?:ullscreen(?:change|error)|ocus(?:out|in)?|inish)|(?:(?:volume|hash)chang|o(?:ff|n)lin)e|b(?:efore(?:unload|print)|lur)|load(?:ed(?:meta)?data|start|end)?|r(?:es(?:ize|et)|atechange)|key(?:press|down|up)|w(?:aiting|heel)|in(?:valid|put)|e(?:nded|rror)|unload)[\\s\\x0B\\x09\\x0C\\x3B\\x2C\\x28\\x3B]*?=[^=]", + "options": { + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls", + "urlDecodeUni" + ] + }, + { + "id": "crs-941-140", + "name": "XSS Filter - Category 4: Javascript URI Vector", + "tags": { + "type": "xss", + "crs_id": "941140", + "category": "attack_attempt", + "cwe": "84", + "capec": "1000/152/242/63/591/244", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "referer" + ] + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "[a-z]+=(?:[^:=]+:.+;)*?[^:=]+:url\\(javascript", + "options": { + "min_length": 18 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls", + "urlDecodeUni" + ] + }, + { + "id": "crs-941-170", + "name": "NoScript XSS InjectionChecker: Attribute Injection", + "tags": { + "type": "xss", + "crs_id": "941170", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "referer" + ] + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:\\W|^)(?:javascript:(?:[\\s\\S]+[=\\x5c\\(\\[\\.<]|[\\s\\S]*?(?:\\bname\\b|\\x5c[ux]\\d)))|@\\W*?i\\W*?m\\W*?p\\W*?o\\W*?r\\W*?t\\W*?(?:/\\*[\\s\\S]*?)?(?:[\\\"']|\\W*?u\\W*?r\\W*?l[\\s\\S]*?\\()|[^-]*?-\\W*?m\\W*?o\\W*?z\\W*?-\\W*?b\\W*?i\\W*?n\\W*?d\\W*?i\\W*?n\\W*?g[^:]*?:\\W*?u\\W*?r\\W*?l[\\s\\S]*?\\(", + "options": { + "min_length": 6 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls", + "urlDecodeUni" + ] + }, + { + "id": "crs-941-180", + "name": "Node-Validator Deny List Keywords", + "tags": { + "type": "xss", + "crs_id": "941180", + "category": "attack_attempt", + "cwe": "79", + "capec": "1000/152/242/63/591" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "options": { + "enforce_word_boundary": true + }, + "list": [ + "document.cookie", + "document.write", + ".parentnode", + ".innerhtml", + "window.location", + "-moz-binding" + ] + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "removeNulls", + "lowercase" + ] + }, + { + "id": "crs-941-200", + "name": "IE XSS Filters - Attack Detected via vmlframe tag", + "tags": { + "type": "xss", + "crs_id": "941200", + "category": "attack_attempt", + "cwe": "80", + "capec": "1000/152/242/63/591", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:<.*[:]?vmlframe.*?[\\s/+]*?src[\\s/+]*=)", + "options": { + "case_sensitive": true, + "min_length": 13 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-941-210", + "name": "IE XSS Filters - Obfuscated Attack Detected via javascript injection", + "tags": { + "type": "xss", + "crs_id": "941210", + "category": "attack_attempt", + "cwe": "80", + "capec": "1000/152/242/63/591", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:(?:j|&#x?0*(?:74|4A|106|6A);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:a|&#x?0*(?:65|41|97|61);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:v|&#x?0*(?:86|56|118|76);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:a|&#x?0*(?:65|41|97|61);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:s|&#x?0*(?:83|53|115|73);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:c|&#x?0*(?:67|43|99|63);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:r|&#x?0*(?:82|52|114|72);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:i|&#x?0*(?:73|49|105|69);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:p|&#x?0*(?:80|50|112|70);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:t|&#x?0*(?:84|54|116|74);?)(?:\\t|\\n|\\r|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?::|&(?:#x?0*(?:58|3A);?|colon;)).)", + "options": { + "case_sensitive": true, + "min_length": 12 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-941-220", + "name": "IE XSS Filters - Obfuscated Attack Detected via vbscript injection", + "tags": { + "type": "xss", + "crs_id": "941220", + "category": "attack_attempt", + "cwe": "80", + "capec": "1000/152/242/63/591", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:(?:v|&#x?0*(?:86|56|118|76);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:b|&#x?0*(?:66|42|98|62);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:s|&#x?0*(?:83|53|115|73);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:c|&#x?0*(?:67|43|99|63);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:r|&#x?0*(?:82|52|114|72);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:i|&#x?0*(?:73|49|105|69);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:p|&#x?0*(?:80|50|112|70);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?:t|&#x?0*(?:84|54|116|74);?)(?:\\t|&(?:#x?0*(?:9|13|10|A|D);?|tab;|newline;))*(?::|&(?:#x?0*(?:58|3A);?|colon;)).)", + "options": { + "case_sensitive": true, + "min_length": 10 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-941-230", + "name": "IE XSS Filters - Attack Detected via embed tag", + "tags": { + "type": "xss", + "crs_id": "941230", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "]", + "options": { + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-941-300", + "name": "IE XSS Filters - Attack Detected via object tag", + "tags": { + "type": "xss", + "crs_id": "941300", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": ")|<.*\\+AD4-", + "options": { + "case_sensitive": true, + "min_length": 6 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-941-360", + "name": "JSFuck / Hieroglyphy obfuscation detected", + "tags": { + "type": "xss", + "crs_id": "941360", + "category": "attack_attempt", + "cwe": "87", + "capec": "1000/152/242/63/591/199" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "![!+ ]\\[\\]", + "options": { + "case_sensitive": true, + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-941-390", + "name": "Javascript method detected", + "tags": { + "type": "xss", + "crs_id": "941390", + "category": "attack_attempt", + "confidence": "1", + "cwe": "79", + "capec": "1000/152/242/63/591" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?i:eval|settimeout|setinterval|new\\s+Function|alert|prompt)[\\s+]*\\([^\\)]", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-100", + "name": "SQL Injection Attack Detected via libinjection", + "tags": { + "type": "sql_injection", + "crs_id": "942100", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ] + }, + "operator": "is_sqli" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "crs-942-160", + "name": "Detects blind sqli tests using sleep() or benchmark()", + "tags": { + "type": "sql_injection", + "crs_id": "942160", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66/7", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:sleep\\(\\s*?\\d*?\\s*?\\)|benchmark\\(.*?\\,.*?\\))", + "options": { + "case_sensitive": true, + "min_length": 7 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-240", + "name": "Detects MySQL charset switch and MSSQL DoS attempts", + "tags": { + "type": "sql_injection", + "crs_id": "942240", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66/7", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:[\\\"'`](?:;*?\\s*?waitfor\\s+(?:delay|time)\\s+[\\\"'`]|;.*?:\\s*?goto)|alter\\s*?\\w+.*?cha(?:racte)?r\\s+set\\s+\\w+)", + "options": { + "min_length": 7 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-250", + "name": "Detects MATCH AGAINST, MERGE and EXECUTE IMMEDIATE injections", + "tags": { + "type": "sql_injection", + "crs_id": "942250", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:merge.*?using\\s*?\\(|execute\\s*?immediate\\s*?[\\\"'`]|match\\s*?[\\w(?:),+-]+\\s*?against\\s*?\\()", + "options": { + "case_sensitive": true, + "min_length": 11 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-270", + "name": "Basic SQL injection", + "tags": { + "type": "sql_injection", + "crs_id": "942270", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "union.*?select.*?from", + "options": { + "min_length": 15 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-280", + "name": "SQL Injection with delay functions", + "tags": { + "type": "sql_injection", + "crs_id": "942280", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66/7", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:;\\s*?shutdown\\s*?(?:[#;{]|\\/\\*|--)|waitfor\\s*?delay\\s?[\\\"'`]+\\s?\\d|select\\s*?pg_sleep)", + "options": { + "min_length": 10 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-290", + "name": "Finds basic MongoDB SQL injection attempts", + "tags": { + "type": "nosql_injection", + "crs_id": "942290", + "category": "attack_attempt", + "cwe": "943", + "capec": "1000/152/248/676" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:(?:\\[?\\$(?:(?:s(?:lic|iz)|wher)e|e(?:lemMatch|xists|q)|n(?:o[rt]|in?|e)|l(?:ike|te?)|t(?:ext|ype)|a(?:ll|nd)|jsonSchema|between|regex|x?or|div|mod)\\]?)\\b)", + "options": { + "case_sensitive": true, + "min_length": 3 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "keys_only" + ] + }, + { + "id": "crs-942-360", + "name": "Detects concatenated basic SQL injection and SQLLFI attempts", + "tags": { + "type": "sql_injection", + "crs_id": "942360", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66/470" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:^[\\W\\d]+\\s*?(?:alter\\s*(?:a(?:(?:pplication\\s*rol|ggregat)e|s(?:ymmetric\\s*ke|sembl)y|u(?:thorization|dit)|vailability\\s*group)|c(?:r(?:yptographic\\s*provider|edential)|o(?:l(?:latio|um)|nversio)n|ertificate|luster)|s(?:e(?:rv(?:ice|er)|curity|quence|ssion|arch)|y(?:mmetric\\s*key|nonym)|togroup|chema)|m(?:a(?:s(?:ter\\s*key|k)|terialized)|e(?:ssage\\s*type|thod)|odule)|l(?:o(?:g(?:file\\s*group|in)|ckdown)|a(?:ngua|r)ge|ibrary)|t(?:(?:abl(?:espac)?|yp)e|r(?:igger|usted)|hreshold|ext)|p(?:a(?:rtition|ckage)|ro(?:cedur|fil)e|ermission)|d(?:i(?:mension|skgroup)|atabase|efault|omain)|r(?:o(?:l(?:lback|e)|ute)|e(?:sourc|mot)e)|f(?:u(?:lltext|nction)|lashback|oreign)|e(?:xte(?:nsion|rnal)|(?:ndpoi|ve)nt)|in(?:dex(?:type)?|memory|stance)|b(?:roker\\s*priority|ufferpool)|x(?:ml\\s*schema|srobject)|w(?:ork(?:load)?|rapper)|hi(?:erarchy|stogram)|o(?:perator|utline)|(?:nicknam|queu)e|us(?:age|er)|group|java|view)|union\\s*(?:(?:distin|sele)ct|all))\\b|\\b(?:(?:(?:trunc|cre|upd)at|renam)e|(?:inser|selec)t|de(?:lete|sc)|alter|load)\\s+(?:group_concat|load_file|char)\\b\\s*\\(?|[\\s(]load_file\\s*?\\(|[\\\"'`]\\s+regexp\\W)", + "options": { + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-942-500", + "name": "MySQL in-line comment detected", + "tags": { + "type": "sql_injection", + "crs_id": "942500", + "category": "attack_attempt", + "cwe": "89", + "capec": "1000/152/248/66" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:/\\*[!+](?:[\\w\\s=_\\-(?:)]+)?\\*/)", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-943-100", + "name": "Possible Session Fixation Attack: Setting Cookie Values in HTML", + "tags": { + "type": "http_protocol_violation", + "crs_id": "943100", + "category": "attack_attempt", + "cwe": "384", + "capec": "1000/225/21/593/61", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i:\\.cookie\\b.*?;\\W*?(?:expires|domain)\\W*?=|\\bhttp-equiv\\W+set-cookie\\b)", + "options": { + "case_sensitive": true, + "min_length": 15 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-944-100", + "name": "Remote Command Execution: Suspicious Java class detected", + "tags": { + "type": "java_code_injection", + "crs_id": "944100", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "java\\.lang\\.(?:runtime|processbuilder)", + "options": { + "case_sensitive": true, + "min_length": 17 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-944-110", + "name": "Remote Command Execution: Java process spawn (CVE-2017-9805)", + "tags": { + "type": "java_code_injection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:unmarshaller|base64data|java\\.).*(?:runtime|processbuilder)", + "options": { + "case_sensitive": false, + "min_length": 13 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "crs-944-130", + "name": "Suspicious Java class detected", + "tags": { + "type": "java_code_injection", + "crs_id": "944130", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "list": [ + "com.opensymphony.xwork2", + "com.sun.org.apache", + "java.io.bufferedinputstream", + "java.io.bufferedreader", + "java.io.bytearrayinputstream", + "java.io.bytearrayoutputstream", + "java.io.chararrayreader", + "java.io.datainputstream", + "java.io.file", + "java.io.fileoutputstream", + "java.io.filepermission", + "java.io.filewriter", + "java.io.filterinputstream", + "java.io.filteroutputstream", + "java.io.filterreader", + "java.io.inputstream", + "java.io.inputstreamreader", + "java.io.linenumberreader", + "java.io.objectoutputstream", + "java.io.outputstream", + "java.io.pipedoutputstream", + "java.io.pipedreader", + "java.io.printstream", + "java.io.pushbackinputstream", + "java.io.reader", + "java.io.stringreader", + "java.lang.class", + "java.lang.integer", + "java.lang.number", + "java.lang.object", + "java.lang.process", + "java.lang.reflect", + "java.lang.runtime", + "java.lang.string", + "java.lang.stringbuilder", + "java.lang.system", + "javax.script.scriptenginemanager", + "org.apache.commons", + "org.apache.struts", + "org.apache.struts2", + "org.omg.corba", + "java.beans.xmldecode" + ], + "options": { + "enforce_word_boundary": true + } + }, + "operator": "phrase_match" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "crs-944-260", + "name": "Remote Command Execution: Malicious class-loading payload", + "tags": { + "type": "java_code_injection", + "crs_id": "944260", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:class\\.module\\.classLoader\\.resources\\.context\\.parent\\.pipeline|springframework\\.context\\.support\\.FileSystemXmlApplicationContext)", + "options": { + "case_sensitive": true, + "min_length": 58 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-000-001", + "name": "Look for Cassandra injections", + "tags": { + "type": "nosql_injection", + "category": "attack_attempt", + "cwe": "943", + "capec": "1000/152/248/676" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + }, + { + "address": "server.request.headers.no_cookies" + } + ], + "regex": "\\ballow\\s+filtering\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeComments" + ] + }, + { + "id": "dog-000-002", + "name": "OGNL - Look for formatting injection patterns", + "tags": { + "type": "java_code_injection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + }, + { + "address": "server.request.headers.no_cookies" + } + ], + "regex": "[#%$]{(?:[^}]+[^\\w\\s}\\-_][^}]+|\\d+-\\d+)}", + "options": { + "case_sensitive": true + } + } + } + ], + "transformers": [] + }, + { + "id": "dog-000-003", + "name": "OGNL - Detect OGNL exploitation primitives", + "tags": { + "type": "java_code_injection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "[@#]ognl", + "options": { + "case_sensitive": true + } + } + } + ], + "transformers": [] + }, + { + "id": "dog-000-004", + "name": "Spring4Shell - Attempts to exploit the Spring4shell vulnerability", + "tags": { + "type": "exploit_detection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.body" + } + ], + "regex": "^class\\.module\\.classLoader\\.", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [ + "keys_only" + ] + }, + { + "id": "dog-000-005", + "name": "Node.js: Prototype pollution through __proto__", + "tags": { + "type": "js_code_injection", + "category": "attack_attempt", + "cwe": "1321", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + } + ], + "regex": "^__proto__$" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "keys_only" + ] + }, + { + "id": "dog-000-006", + "name": "Node.js: Prototype pollution through constructor.prototype", + "tags": { + "type": "js_code_injection", + "category": "attack_attempt", + "cwe": "1321", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + } + ], + "regex": "^constructor$" + }, + "operator": "match_regex" + }, + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + } + ], + "regex": "^prototype$" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "keys_only" + ] + }, + { + "id": "dog-000-007", + "name": "Server side template injection: Velocity & Freemarker", + "tags": { + "type": "java_code_injection", + "category": "attack_attempt", + "cwe": "1336", + "capec": "1000/152/242/19", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "#(?:set|foreach|macro|parse|if)\\(.*\\)|<#assign.*>" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-001", + "name": "BurpCollaborator OOB domain", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "tool_name": "BurpCollaborator", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:burpcollaborator\\.net|oastify\\.com)\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-002", + "name": "Qualys OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Qualys", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bqualysperiscope\\.com\\b|\\.oscomm\\." + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-003", + "name": "Probely OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Probely", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bprbly\\.win\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-004", + "name": "Known malicious out-of-band interaction domain", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:webhook\\.site|\\.canarytokens\\.com|vii\\.one|act1on3\\.ru|gdsburp\\.com|arcticwolf\\.net|oob\\.li|htbiw\\.com|h4\\.vc|mochan\\.cloud|imshopping\\.com|bootstrapnodejs\\.com|mooo-ng\\.com|securitytrails\\.com|canyouhackit\\.io|7bae\\.xyz)\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-005", + "name": "Known suspicious out-of-band interaction domain", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:\\.ngrok\\.io|requestbin\\.com|requestbin\\.net)\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-006", + "name": "Rapid7 OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Rapid7", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bappspidered\\.rapid7\\." + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-007", + "name": "Interact.sh OOB domain", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "tool_name": "interact.sh", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:interact\\.sh|oast\\.(?:pro|live|site|online|fun|me)|indusfacefinder\\.in|where\\.land|syhunt\\.net|tssrt\\.de|boardofcyber\\.io|assetnote-callback\\.com|praetorianlabs\\.dev|netspi\\.sh)\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-008", + "name": "Netsparker OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Netsparker", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b(?:\\.|(?:\\\\|&#)(?:0*46|x0*2e);)?r87(?:\\.|(?:\\\\|&#)(?:0*46|x0*2e);)(?:me|com)\\b", + "options": { + "case_sensitive": false, + "min_length": 7 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-009", + "name": "WhiteHat Security OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "WhiteHatSecurity", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bwhsec(?:\\.|(?:\\\\|&#)(?:0*46|x0*2e);)us\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-010", + "name": "Nessus OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Nessus", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\b\\.nessus\\.org\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-011", + "name": "Watchtowr OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "Watchtowr", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bwatchtowr\\.com\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-913-012", + "name": "AppCheck NG OOB domain", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "tool_name": "AppCheckNG", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\bptst\\.io\\b", + "options": { + "case_sensitive": false, + "min_length": 7 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-920-001", + "name": "JWT authentication bypass", + "tags": { + "type": "http_protocol_violation", + "category": "attack_attempt", + "cwe": "287", + "capec": "1000/225/115", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.cookies" + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "authorization" + ] + } + ], + "regex": "^(?:Bearer )?ey[A-Za-z0-9+_\\-/]*([QY][UW]x[Hn]Ij([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiAi[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]Ij([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDogI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]IiA6ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]Ij([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiAi[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciOiAi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciIDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgOiJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]IiA6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]Ij([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciOiJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgOiAi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]IjogI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]IiA6I[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6I[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6I[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciIDogI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]IiA6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]IiA6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ciO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6I[km]5[Pv][Tb][km][U-X]|[QY][UW]x[Hn]IiA6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ID([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gI[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yIgO([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[\\x2b\\x2f-9A-Za-z]ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*ICJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]I([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*IDoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]A6I[km]5[Pv][Tb][km][U-X]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]y([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiJ[Ou][Tb][02]5[Fl]|[QY][UW]x[Hn]Ijoi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z]{2}[159BFJNRVZdhlptx][Bh][Tb][EG]ci([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[048AEIMQUYcgkosw]gOiAi[Tb][km]9[Ou][RZ][Q-Za-f]|[\\x2b\\x2f-9A-Za-z][02EGUWkm]F[Ms][RZ]yI6([048ACEIMQSUYcgikoswy]|[\\x2b\\x2f-9A-Za-z]I)*[CSiy]Ai[Tb][km]9[Ou][RZ][Q-Za-f])[A-Za-z0-9+-/]*\\.[A-Za-z0-9+_\\-/]+\\.(?:[A-Za-z0-9+_\\-/]+)?$", + "options": { + "case_sensitive": true + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-931-001", + "name": "RFI: URL Payload to well known RFI target", + "tags": { + "type": "rfi", + "category": "attack_attempt", + "cwe": "98", + "capec": "1000/152/175/253/193", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^(?i:file|ftps?|https?).*/rfiinc\\.txt\\?+$", + "options": { + "case_sensitive": true, + "min_length": 17 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-932-100", + "name": "Shell spawn executing network command", + "tags": { + "type": "command_injection", + "category": "attack_attempt", + "cwe": "77", + "capec": "1000/152/248/88", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:(?:['\"\\x60({|;&]|(?:^|['\"\\x60({|;&])(?:cmd(?:\\.exe)?\\s+(?:/\\w(?::\\w+)?\\s+)*))(?:ping|curl|wget|telnet)|\\bnslookup)[\\s,]", + "options": { + "case_sensitive": true, + "min_length": 5 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-934-001", + "name": "XXE - XML file loads external entity", + "tags": { + "type": "xxe", + "category": "attack_attempt", + "cwe": "91", + "capec": "1000/152/248/250", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.body" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?:<\\?xml[^>]*>.*)]+SYSTEM\\s+[^>]+>", + "options": { + "case_sensitive": false, + "min_length": 24 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "dog-941-001", + "name": "XSS in source property", + "tags": { + "type": "xss", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "referer" + ] + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "<(?:iframe|esi:include)(?:(?:\\s|/)*\\w+=[\"'\\w]+)*(?:\\s|/)*src(?:doc)?=[\"']?(?:data:|javascript:|http:|dns:|//)[^\\s'\"]+['\"]?", + "options": { + "min_length": 14 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls", + "urlDecodeUni" + ] + }, + { + "id": "dog-942-001", + "name": "Blind XSS callback domains", + "tags": { + "type": "xss", + "category": "attack_attempt", + "cwe": "83", + "capec": "1000/152/242/63/591/243", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "https?:\\/\\/(?:.*\\.)?(?:bxss\\.(?:in|me)|xss\\.ht|js\\.rip)", + "options": { + "case_sensitive": false + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "nfd-000-001", + "name": "Detect common directory discovery scans", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "phrase_match", + "parameters": { + "options": { + "enforce_word_boundary": true + }, + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "list": [ + "/wordpress/", + "/etc/", + "/login.php", + "/install.php", + "/administrator", + "/admin.php", + "/wp-config", + "/phpmyadmin", + "/fckeditor", + "/mysql", + "/manager/html", + ".htaccess", + "/config.php", + "/configuration", + "/cgi-bin/php", + "/search.php", + "/tinymce", + "/tiny_mce", + "/settings.php", + "../../..", + "/install/", + "/download.php", + "/webdav", + "/forum.php", + "/user.php", + "/style.php", + "/jmx-console", + "/modules.php", + "/include.php", + "/default.asp", + "/help.php", + "/database.yml", + "/database.yml.pgsql", + "/database.yml.sqlite3", + "/database.yml.sqlite", + "/database.yml.mysql", + ".%2e/", + "/view.php", + "/header.php", + "/search.asp", + "%5c%5c", + "/server/php/", + "/invoker/jmxinvokerservlet", + "/phpmyadmin/index.php", + "/data/admin/allowurl.txt", + "/verify.php", + "/misc/ajax.js", + "/.idea", + "/module.php", + "/backup.rar", + "/backup.tar", + "/backup.zip", + "/backup.7z", + "/backup.gz", + "/backup.tgz", + "/backup.tar.gz", + "waitfor%20delay", + "/calendar.php", + "/news.php", + "/dompdf.php", + "))))))))))))))))", + "/web.config", + "tree.php", + "/cgi-bin-sdb/printenv", + "/comments.php", + "/detail.asp", + "/license.txt", + "/admin.asp", + "/auth.php", + "/list.php", + "/content.php", + "/mod.php", + "/mini.php", + "/install.pgsql", + "/install.mysql", + "/install.sqlite", + "/install.sqlite3", + "/install.txt", + "/install.md", + "/doku.php", + "/main.asp", + "/myadmin", + "/force-download.php", + "/iisprotect/admin", + "/.gitignore", + "/print.php", + "/common.php", + "/mainfile.php", + "/functions.php", + "/scripts/setup.php", + "/faq.php", + "/op/op.login.php", + "/home.php", + "/includes/hnmain.inc.php3", + "/preview.php", + "/dump.rar", + "/dump.tar", + "/dump.zip", + "/dump.7z", + "/dump.gz", + "/dump.tgz", + "/dump.tar.gz", + "/thumbnail.php", + "/sendcard.php", + "/global.asax", + "/directory.php", + "/footer.php", + "/error.asp", + "/forum.asp", + "/save.php", + "/htmlsax3.php", + "/adm/krgourl.php", + "/includes/converter.inc.php", + "/nucleus/libs/pluginadmin.php", + "/base_qry_common.php", + "/fileadmin", + "/bitrix/admin/", + "/adm.php", + "/util/barcode.php", + "/action.php", + "/rss.asp", + "/downloads.php", + "/page.php", + "/snarf_ajax.php", + "/fck/editor", + "/sendmail.php", + "/detail.php", + "/iframe.php", + "/swfupload.swf", + "/jenkins/login", + "/phpmyadmin/main.php", + "/phpmyadmin/scripts/setup.php", + "/user/index.php", + "/checkout.php", + "/process.php", + "/ks_inc/ajax.js", + "/export.php", + "/register.php", + "/cart.php", + "/console.php", + "/friend.php", + "/readmsg.php", + "/install.asp", + "/dagent/downloadreport.asp", + "/system/index.php", + "/core/changelog.txt", + "/js/util.js", + "/interna.php", + "/gallery.php", + "/links.php", + "/data/admin/ver.txt", + "/language/zh-cn.xml", + "/productdetails.asp", + "/admin/template/article_more/config.htm", + "/components/com_moofaq/includes/file_includer.php", + "/licence.txt", + "/rss.xsl", + "/vtigerservice.php", + "/mysql/main.php", + "/passwiki.php", + "/scr/soustab.php", + "/global.php", + "/email.php", + "/user.asp", + "/msd", + "/products.php", + "/cultbooking.php", + "/cron.php", + "/static/js/admincp.js", + "/comment.php", + "/maintainers", + "/modules/plain/adminpart/addplain.php", + "/wp-content/plugins/ungallery/source_vuln.php", + "/upgrade.txt", + "/category.php", + "/index_logged.php", + "/members.asp", + "/script/html.js", + "/images/ad.js", + "/awstats/awstats.pl", + "/includes/esqueletos/skel_null.php", + "/modules/profile/user.php", + "/window_top.php", + "/openbrowser.php", + "/thread.php", + "tinfoil_xss", + "/includes/include.php", + "/urheber.php", + "/header.inc.php", + "/mysqldumper", + "/display.php", + "/website.php", + "/stats.php", + "/assets/plugins/mp3_id/mp3_id.php", + "/siteminderagent/forms/smpwservices.fcc" + ] + } + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "nfd-000-002", + "name": "Detect failed attempt to fetch readme files", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "readme\\.[\\.a-z0-9]+$", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-003", + "name": "Detect failed attempt to fetch Java EE resource files", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "^(?:.*web\\-inf)(?:.*web\\.xml).*$", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-004", + "name": "Detect failed attempt to fetch code files", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "\\.(java|pyc?|rb|class)\\b", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-005", + "name": "Detect failed attempt to fetch source code archives", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "\\.(sql|log|ndb|gz|zip|tar\\.gz|tar|regVV|reg|conf|bz2|ini|db|war|bat|inc|btr|server|ds|conf|config|admin|master|sln|bak)\\b(?:[^.]|$)", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-006", + "name": "Detect failed attempt to fetch sensitive files", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "\\.(cgi|bat|dll|exe|key|cert|crt|pem|der|pkcs|pkcs|pkcs[0-9]*|nsf|jsa|war|java|class|vb|vba|so|git|svn|hg|cvs)([^a-zA-Z0-9_]|$)", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-007", + "name": "Detect failed attempt to fetch archives", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "/[\\d\\-_]*\\.(rar|tar|zip|7z|gz|tgz|tar.gz)", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-008", + "name": "Detect failed attempt to trigger incorrect application behavior", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "(/(administrator/components/com.*\\.php|response\\.write\\(.+\\))|select\\(.+\\)from|\\(.*sleep\\(.+\\)|(%[a-zA-Z0-9]{2}[a-zA-Z]{0,1})+\\))", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-009", + "name": "Detect failed attempt to leak the structure of the application", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "/(login\\.rol|LICENSE|[\\w-]+\\.(plx|pwd))$", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "nfd-000-010", + "name": "Detect failed attempts to find API documentation", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "0" + }, + "conditions": [ + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.response.status" + } + ], + "regex": "^404$", + "options": { + "case_sensitive": true + } + } + }, + { + "operator": "match_regex", + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + } + ], + "regex": "(?:/swagger\\b|/api[-/]docs?\\b)", + "options": { + "case_sensitive": false + } + } + } + ], + "transformers": [] + }, + { + "id": "sqr-000-001", + "name": "SSRF: Try to access the credential manager of the main cloud services", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i)^\\W*((http|ftp)s?://)?\\W*((::f{4}:)?(169|(0x)?0*a9|0+251)\\.?(254|(0x)?0*fe|0+376)[0-9a-fx\\.:]+|metadata\\.google\\.internal|metadata\\.goog)\\W*/", + "options": { + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "sqr-000-002", + "name": "Server-side Javascript injection: Try to detect obvious JS injection", + "tags": { + "type": "js_code_injection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "require\\(['\"][\\w\\.]+['\"]\\)|process\\.\\w+\\([\\w\\.]*\\)|\\.toString\\(\\)", + "options": { + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [ + "removeNulls" + ] + }, + { + "id": "sqr-000-008", + "name": "Windows: Detect attempts to exfiltrate .ini files", + "tags": { + "type": "command_injection", + "category": "attack_attempt", + "cwe": "78", + "capec": "1000/152/248/88", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i)[&|]\\s*type\\s+%\\w+%\\\\+\\w+\\.ini\\s*[&|]" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "sqr-000-009", + "name": "Linux: Detect attempts to exfiltrate passwd files", + "tags": { + "type": "command_injection", + "category": "attack_attempt", + "cwe": "78", + "capec": "1000/152/248/88", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i)[&|]\\s*cat\\s*\\/etc\\/[\\w\\.\\/]*passwd\\s*[&|]" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "cmdLine" + ] + }, + { + "id": "sqr-000-010", + "name": "Windows: Detect attempts to timeout a shell", + "tags": { + "type": "command_injection", + "category": "attack_attempt", + "cwe": "78", + "capec": "1000/152/248/88", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(?i)[&|]\\s*timeout\\s+/t\\s+\\d+\\s*[&|]" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "sqr-000-011", + "name": "SSRF: Try to access internal OMI service (CVE-2021-38647)", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "http(s?):\\/\\/([A-Za-z0-9\\.\\-\\_]+|\\[[A-Fa-f0-9\\:]+\\]|):5986\\/wsman", + "options": { + "min_length": 4 + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "sqr-000-012", + "name": "SSRF: Detect SSRF attempt on internal service", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^(jar:)?(http|https):\\/\\/([0-9oq]{1,5}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}|[0-9]{1,10})(:[0-9]{1,5})?(\\/[^:@]*)?$" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "sqr-000-013", + "name": "SSRF: Detect SSRF attempts using IPv6 or octal/hexdecimal obfuscation", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^(jar:)?(http|https):\\/\\/((\\[)?[:0-9a-f\\.x]{2,}(\\])?)(:[0-9]{1,5})?(\\/[^:@]*)?$" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "sqr-000-014", + "name": "SSRF: Detect SSRF domain redirection bypass", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "grpc.server.request.message" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "(http|https):\\/\\/(?:.*\\.)?(?:burpcollaborator\\.net|localtest\\.me|mail\\.ebc\\.apple\\.com|bugbounty\\.dod\\.network|.*\\.[nx]ip\\.io|oastify\\.com|oast\\.(?:pro|live|site|online|fun|me)|sslip\\.io|requestbin\\.com|requestbin\\.net|hookbin\\.com|webhook\\.site|canarytokens\\.com|interact\\.sh|ngrok\\.io|bugbounty\\.click|prbly\\.win|qualysperiscope\\.com|vii\\.one|act1on3\\.ru)" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "sqr-000-015", + "name": "SSRF: Detect SSRF attempt using non HTTP protocol", + "tags": { + "type": "ssrf", + "category": "attack_attempt", + "cwe": "918", + "capec": "1000/225/115/664", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "^(jar:)?((file|netdoc):\\/\\/[\\\\\\/]+|(dict|gopher|ldap|sftp|tftp):\\/\\/.*:[0-9]{1,5})" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "lowercase" + ] + }, + { + "id": "sqr-000-017", + "name": "Log4shell: Attempt to exploit log4j CVE-2021-44228", + "tags": { + "type": "exploit_detection", + "category": "attack_attempt", + "cwe": "94", + "capec": "1000/152/242", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.uri.raw" + }, + { + "address": "server.request.query" + }, + { + "address": "server.request.body" + }, + { + "address": "server.request.path_params" + }, + { + "address": "server.request.headers.no_cookies" + }, + { + "address": "graphql.server.all_resolvers" + }, + { + "address": "graphql.server.resolver" + } + ], + "regex": "\\${[^j]*j[^n]*n[^d]*d[^i]*i[^:]*:[^}]*}" + }, + "operator": "match_regex" + } + ], + "transformers": [ + "unicode_normalize" + ] + }, + { + "id": "ua0-600-0xx", + "name": "Joomla exploitation tool", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Joomla exploitation tool", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "JDatabaseDriverMysqli" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-10x", + "name": "Nessus", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nessus", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)^Nessus(/|([ :]+SOAP))" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-12x", + "name": "Arachni", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Arachni", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^Arachni\\/v" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-13x", + "name": "Jorgee", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Jorgee", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bJorgee\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-14x", + "name": "Probely", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Probely", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bProbely\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-15x", + "name": "Metis", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Metis", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bmetis\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-16x", + "name": "SQL power injector", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "SQLPowerInjector", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "sql power injector" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-18x", + "name": "N-Stealth", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "N-Stealth", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bn-stealth\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-19x", + "name": "Brutus", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Brutus", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bbrutus\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-1xx", + "name": "Shellshock exploitation tool", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\(\\) \\{ :; *\\}" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-20x", + "name": "Netsparker", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Netsparker", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\bnetsparker\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-22x", + "name": "JAASCois", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "JAASCois", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bjaascois\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-26x", + "name": "Nsauditor", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nsauditor", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bnsauditor\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-27x", + "name": "Paros", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Paros", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)Mozilla/.* Paros/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-28x", + "name": "DirBuster", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "DirBuster", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bdirbuster\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-29x", + "name": "Pangolin", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Pangolin", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bpangolin\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-2xx", + "name": "Qualys", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Qualys", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bqualys\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-30x", + "name": "SQLNinja", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "SQLNinja", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bsqlninja\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-31x", + "name": "Nikto", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nikto", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\(Nikto/[\\d\\.]+\\)" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-33x", + "name": "BlackWidow", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "BlackWidow", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bblack\\s?widow\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-34x", + "name": "Grendel-Scan", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Grendel-Scan", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bgrendel-scan\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-35x", + "name": "Havij", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Havij", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bhavij\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-36x", + "name": "w3af", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "w3af", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bw3af\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-37x", + "name": "Nmap", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nmap", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "nmap (nse|scripting engine)" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-39x", + "name": "Nessus Scripted", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nessus", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)^'?[a-z0-9_]+\\.nasl'?$" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-3xx", + "name": "Evil Scanner", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "EvilScanner", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bevilScanner\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-40x", + "name": "WebFuck", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "WebFuck", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bWebFuck\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-42x", + "name": "OpenVAS", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "OpenVAS", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)OpenVAS\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-43x", + "name": "Spider-Pig", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Spider-Pig", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "Powered by Spider-Pig by tinfoilsecurity\\.com" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-44x", + "name": "Zgrab", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Zgrab", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "Mozilla/\\d+.\\d+ zgrab" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-45x", + "name": "Zmeu", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Zmeu", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bZmEu\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-47x", + "name": "GoogleSecurityScanner", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "GoogleSecurityScanner", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bGoogleSecurityScanner\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-48x", + "name": "Commix", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Commix", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^commix\\/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-49x", + "name": "Gobuster", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Gobuster", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^gobuster\\/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-4xx", + "name": "CGIchk", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "CGIchk", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bcgichk\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-51x", + "name": "FFUF", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "FFUF", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)^Fuzz Faster U Fool\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-52x", + "name": "Nuclei", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nuclei", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)^Nuclei\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-53x", + "name": "Tsunami", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Tsunami", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bTsunamiSecurityScanner\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-54x", + "name": "Nimbostratus", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Nimbostratus", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bnimbostratus-bot\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-55x", + "name": "Datadog test scanner: user-agent", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Datadog Canary Test", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "grpc.server.request.metadata", + "key_path": [ + "dd-canary" + ] + } + ], + "regex": "^dd-test-scanner-log(?:$|/|\\s)" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-56x", + "name": "Datadog test scanner - blocking version: user-agent", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Datadog Canary Test", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + }, + { + "address": "grpc.server.request.metadata", + "key_path": [ + "dd-canary" + ] + } + ], + "regex": "^dd-test-scanner-log-block(?:$|/|\\s)" + }, + "operator": "match_regex" + } + ], + "transformers": [], + "on_match": [ + "block" + ] + }, + { + "id": "ua0-600-57x", + "name": "AlertLogic", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "AlertLogic", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\bAlertLogic-MDR-" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-58x", + "name": "wfuzz", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "wfuzz", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\bwfuzz\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-59x", + "name": "Detectify", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Detectify", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "\\bdetectify\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-5xx", + "name": "Blind SQL Injection Brute Forcer", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "BSQLBF", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)\\bbsqlbf\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-60x", + "name": "masscan", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "masscan", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^masscan/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-61x", + "name": "WPScan", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "WPScan", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^wpscan\\b" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-62x", + "name": "Aon pentesting services", + "tags": { + "type": "commercial_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Aon", + "confidence": "0" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^Aon/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-63x", + "name": "FeroxBuster", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "feroxbuster", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^feroxbuster/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-6xx", + "name": "Stealthy scanner", + "tags": { + "type": "security_scanner", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "mozilla/4\\.0 \\(compatible(; msie (?:6\\.0; (?:win32|Windows NT 5\\.0)|4\\.0; Windows NT))?\\)", + "options": { + "case_sensitive": false + } + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-7xx", + "name": "SQLmap", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "SQLmap", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "^sqlmap/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + }, + { + "id": "ua0-600-9xx", + "name": "Skipfish", + "tags": { + "type": "attack_tool", + "category": "attack_attempt", + "cwe": "200", + "capec": "1000/118/169", + "tool_name": "Skipfish", + "confidence": "1" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.headers.no_cookies", + "key_path": [ + "user-agent" + ] + } + ], + "regex": "(?i)mozilla/5\\.0 sf/" + }, + "operator": "match_regex" + } + ], + "transformers": [] + } + ], + "processors": [ + { + "id": "extract-content", + "generator": "extract_schema", + "conditions": [ + { + "operator": "equals", + "parameters": { + "inputs": [ + { + "address": "waf.context.processor", + "key_path": [ + "extract-schema" + ] + } + ], + "type": "boolean", + "value": true + } + } + ], + "parameters": { + "mappings": [ + { + "inputs": [ + { + "address": "server.request.body" + } + ], + "output": "_dd.appsec.s.req.body" + }, + { + "inputs": [ + { + "address": "server.request.cookies" + } + ], + "output": "_dd.appsec.s.req.cookies" + }, + { + "inputs": [ + { + "address": "server.request.query" + } + ], + "output": "_dd.appsec.s.req.query" + }, + { + "inputs": [ + { + "address": "server.request.path_params" + } + ], + "output": "_dd.appsec.s.req.params" + }, + { + "inputs": [ + { + "address": "server.response.body" + } + ], + "output": "_dd.appsec.s.res.body" + }, + { + "inputs": [ + { + "address": "graphql.server.all_resolvers" + } + ], + "output": "_dd.appsec.s.graphql.all_resolvers" + }, + { + "inputs": [ + { + "address": "graphql.server.resolver" + } + ], + "output": "_dd.appsec.s.graphql.resolver" + } + ], + "scanners": [ + { + "tags": { + "category": "payment" + } + }, + { + "tags": { + "category": "pii" + } + } + ] + }, + "evaluate": false, + "output": true + }, + { + "id": "extract-headers", + "generator": "extract_schema", + "conditions": [ + { + "operator": "equals", + "parameters": { + "inputs": [ + { + "address": "waf.context.processor", + "key_path": [ + "extract-schema" + ] + } + ], + "type": "boolean", + "value": true + } + } + ], + "parameters": { + "mappings": [ + { + "inputs": [ + { + "address": "server.request.headers.no_cookies" + } + ], + "output": "_dd.appsec.s.req.headers" + }, + { + "inputs": [ + { + "address": "server.response.headers.no_cookies" + } + ], + "output": "_dd.appsec.s.res.headers" + } + ], + "scanners": [ + { + "tags": { + "category": "credentials" + } + }, + { + "tags": { + "category": "pii" + } + } + ] + }, + "evaluate": false, + "output": true + } + ], + "scanners": [ + { + "id": "JU1sRk3mSzqSUJn6GrVn7g", + "name": "American Express Card Scanner (4+4+4+3 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b3[47]\\d{2}(?:(?:\\s\\d{4}\\s\\d{4}\\s\\d{3})|(?:\\,\\d{4}\\,\\d{4}\\,\\d{3})|(?:-\\d{4}-\\d{4}-\\d{3})|(?:\\.\\d{4}\\.\\d{4}\\.\\d{3}))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "amex", + "category": "payment" + } + }, + { + "id": "edmH513UTQWcRiQ9UnzHlw-mod", + "name": "American Express Card Scanner (4+6|5+5|6 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b3[47]\\d{2}(?:(?:\\s\\d{5,6}\\s\\d{5,6})|(?:\\.\\d{5,6}\\.\\d{5,6})|(?:-\\d{5,6}-\\d{5,6})|(?:,\\d{5,6},\\d{5,6}))\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "card", + "card_type": "amex", + "category": "payment" + } + }, + { + "id": "e6K4h_7qTLaMiAbaNXoSZA", + "name": "American Express Card Scanner (8+7 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b3[47]\\d{6}(?:(?:\\s\\d{7})|(?:\\,\\d{7})|(?:-\\d{7})|(?:\\.\\d{7}))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "amex", + "category": "payment" + } + }, + { + "id": "K2rZflWzRhGM9HiTc6whyQ", + "name": "American Express Card Scanner (1x15 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b3[47]\\d{13}\\b", + "options": { + "case_sensitive": false, + "min_length": 15 + } + } + }, + "tags": { + "type": "card", + "card_type": "amex", + "category": "payment" + } + }, + { + "id": "9d7756e343cefa22a5c098e1092590f806eb5446", + "name": "Basic Authentication Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\bauthorization\\b", + "options": { + "case_sensitive": false, + "min_length": 13 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "^basic\\s+[A-Za-z0-9+/=]+", + "options": { + "case_sensitive": false, + "min_length": 7 + } + } + }, + "tags": { + "type": "basic_auth", + "category": "credentials" + } + }, + { + "id": "mZy8XjZLReC9smpERXWnnw", + "name": "Bearer Authentication Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\bauthorization\\b", + "options": { + "case_sensitive": false, + "min_length": 13 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "^bearer\\s+[-a-z0-9._~+/]{4,}", + "options": { + "case_sensitive": false, + "min_length": 11 + } + } + }, + "tags": { + "type": "bearer_token", + "category": "credentials" + } + }, + { + "id": "450239afc250a19799b6c03dc0e16fd6a4b2a1af", + "name": "Canadian Social Insurance Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:social[\\s_]?(?:insurance(?:\\s+number)?)?|SIN|Canadian[\\s_]?(?:social[\\s_]?(?:insurance)?|insurance[\\s_]?number)?)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b\\d{3}-\\d{3}-\\d{3}\\b", + "options": { + "case_sensitive": false, + "min_length": 11 + } + } + }, + "tags": { + "type": "canadian_sin", + "category": "pii" + } + }, + { + "id": "87a879ff33693b46c8a614d8211f5a2c289beca0", + "name": "Digest Authentication Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\bauthorization\\b", + "options": { + "case_sensitive": false, + "min_length": 13 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "^digest\\s+", + "options": { + "case_sensitive": false, + "min_length": 7 + } + } + }, + "tags": { + "type": "digest_auth", + "category": "credentials" + } + }, + { + "id": "qWumeP1GQUa_E4ffAnT-Yg", + "name": "American Express Card Scanner (1x14 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "(?:30[0-59]\\d|3[689]\\d{2})(?:\\d{10})", + "options": { + "case_sensitive": false, + "min_length": 14 + } + } + }, + "tags": { + "type": "card", + "card_type": "diners", + "category": "payment" + } + }, + { + "id": "NlTWWM5LS6W0GSqBLuvtRw", + "name": "Diners Card Scanner (4+4+4+2 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:30[0-59]\\d|3[689]\\d{2})(?:(?:\\s\\d{4}\\s\\d{4}\\s\\d{2})|(?:\\,\\d{4}\\,\\d{4}\\,\\d{2})|(?:-\\d{4}-\\d{4}-\\d{2})|(?:\\.\\d{4}\\.\\d{4}\\.\\d{2}))\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "card", + "card_type": "diners", + "category": "payment" + } + }, + { + "id": "Xr5VdbQSTXitYGGiTfxBpw", + "name": "Diners Card Scanner (4+6+4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:30[0-59]\\d|3[689]\\d{2})(?:(?:\\s\\d{6}\\s\\d{4})|(?:\\.\\d{6}\\.\\d{4})|(?:-\\d{6}-\\d{4})|(?:,\\d{6},\\d{4}))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "diners", + "category": "payment" + } + }, + { + "id": "gAbunN_WQNytxu54DjcbAA-mod", + "name": "Diners Card Scanner (8+6 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:30[0-59]\\d{5}|3[689]\\d{6})\\s?(?:(?:\\s\\d{6})|(?:\\,\\d{6})|(?:-\\d{6})|(?:\\.\\d{6}))\\b", + "options": { + "case_sensitive": false, + "min_length": 14 + } + } + }, + "tags": { + "type": "card", + "card_type": "diners", + "category": "payment" + } + }, + { + "id": "9cs4qCfEQBeX17U7AepOvQ", + "name": "MasterCard Scanner (2x8 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:6221(?:2[6-9]|[3-9][0-9])\\d{2}(?:,\\d{8}|\\s\\d{8}|-\\d{8}|\\.\\d{8})|6229(?:[01][0-9]|2[0-5])\\d{2}(?:,\\d{8}|\\s\\d{8}|-\\d{8}|\\.\\d{8})|(?:6011|65\\d{2}|64[4-9]\\d|622[2-8])\\d{4}(?:,\\d{8}|\\s\\d{8}|-\\d{8}|\\.\\d{8}))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "discover", + "category": "payment" + } + }, + { + "id": "YBIDWJIvQWW_TFOyU0CGJg", + "name": "Discover Card Scanner (4x4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:(?:(?:6221(?:2[6-9]|[3-9][0-9])\\d{2}(?:,\\d{4}){2})|(?:6221\\s(?:2[6-9]|[3-9][0-9])\\d{2}(?:\\s\\d{4}){2})|(?:6221\\.(?:2[6-9]|[3-9][0-9])\\d{2}(?:\\.\\d{4}){2})|(?:6221-(?:2[6-9]|[3-9][0-9])\\d{2}(?:-\\d{4}){2}))|(?:(?:6229(?:[01][0-9]|2[0-5])\\d{2}(?:,\\d{4}){2})|(?:6229\\s(?:[01][0-9]|2[0-5])\\d{2}(?:\\s\\d{4}){2})|(?:6229\\.(?:[01][0-9]|2[0-5])\\d{2}(?:\\.\\d{4}){2})|(?:6229-(?:[01][0-9]|2[0-5])\\d{2}(?:-\\d{4}){2}))|(?:(?:6011|65\\d{2}|64[4-9]\\d|622[2-8])(?:(?:\\s\\d{4}){3}|(?:\\.\\d{4}){3}|(?:-\\d{4}){3}|(?:,\\d{4}){3})))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "discover", + "category": "payment" + } + }, + { + "id": "12cpbjtVTMaMutFhh9sojQ", + "name": "Discover Card Scanner (1x16 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:6221(?:2[6-9]|[3-9][0-9])\\d{10}|6229(?:[01][0-9]|2[0-5])\\d{10}|(?:6011|65\\d{2}|64[4-9]\\d|622[2-8])\\d{12})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "discover", + "category": "payment" + } + }, + { + "id": "PuXiVTCkTHOtj0Yad1ppsw", + "name": "Standard E-mail Address", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:(?:e[-\\s]?)?mail|address|sender|\\bto\\b|from|recipient)\\b", + "options": { + "case_sensitive": false, + "min_length": 2 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*(%40|@)(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}\\b", + "options": { + "case_sensitive": false, + "min_length": 5 + } + } + }, + "tags": { + "type": "email", + "category": "pii" + } + }, + { + "id": "8VS2RKxzR8a_95L5fuwaXQ", + "name": "IBAN", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:iban|account|sender|receiver)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:NO\\d{2}(?:[ \\-]?\\d{4}){2}[ \\-]?\\d{3}|BE\\d{2}(?:[ \\-]?\\d{4}){3}|(?:DK|FO|FI|GL|SD)\\d{2}(?:[ \\-]?\\d{4}){3}[ \\-]?\\d{2}|NL\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?\\d{4}){2}[ \\-]?\\d{2}|MK\\d{2}[ \\-]?\\d{3}[A-Z0-9](?:[ \\-]?[A-Z0-9]{4}){2}[ \\-]?[A-Z0-9]\\d{2}|SI\\d{17}|(?:AT|BA|EE|LT|XK)\\d{18}|(?:LU|KZ|EE|LT)\\d{5}[A-Z0-9]{13}|LV\\d{2}[A-Z]{4}[A-Z0-9]{13}|(?:LI|CH)\\d{2}[ \\-]?\\d{4}[ \\-]?\\d[A-Z0-9]{3}(?:[ \\-]?[A-Z0-9]{4}){2}[ \\-]?[A-Z0-9]|HR\\d{2}(?:[ \\-]?\\d{4}){4}[ \\-]?\\d|GE\\d{2}[ \\-]?[A-Z0-9]{2}\\d{2}\\d{14}|VA\\d{20}|BG\\d{2}[A-Z]{4}\\d{6}[A-Z0-9]{8}|BH\\d{2}[A-Z]{4}[A-Z0-9]{14}|GB\\d{2}[A-Z]{4}(?:[ \\-]?\\d{4}){3}[ \\-]?\\d{2}|IE\\d{2}[ \\-]?[A-Z0-9]{4}(?:[ \\-]?\\d{4}){3}[ \\-]?\\d{2}|(?:CR|DE|ME|RS)\\d{2}(?:[ \\-]?\\d{4}){4}[ \\-]?\\d{2}|(?:AE|TL|IL)\\d{2}(?:[ \\-]?\\d{4}){4}[ \\-]?\\d{3}|GI\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?[A-Z0-9]{4}){3}[ \\-]?[A-Z0-9]{3}|IQ\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?\\d{4}){3}[ \\-]?\\d{3}|MD\\d{2}(?:[ \\-]?[A-Z0-9]{4}){5}|SA\\d{2}[ \\-]?\\d{2}[A-Z0-9]{2}(?:[ \\-]?[A-Z0-9]{4}){4}|RO\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?[A-Z0-9]{4}){4}|(?:PK|VG)\\d{2}[ \\-]?[A-Z0-9]{4}(?:[ \\-]?\\d{4}){4}|AD\\d{2}(?:[ \\-]?\\d{4}){2}(?:[ \\-]?[A-Z0-9]{4}){3}|(?:CZ|SK|ES|SE|TN)\\d{2}(?:[ \\-]?\\d{4}){5}|(?:LY|PT|ST)\\d{2}(?:[ \\-]?\\d{4}){5}[ \\-]?\\d|TR\\d{2}[ \\-]?\\d{4}[ \\-]?\\d[A-Z0-9]{3}(?:[ \\-]?[A-Z0-9]{4}){3}[ \\-]?[A-Z0-9]{2}|IS\\d{2}(?:[ \\-]?\\d{4}){5}[ \\-]?\\d{2}|(?:IT|SM)\\d{2}[ \\-]?[A-Z]\\d{3}[ \\-]?\\d{4}[ \\-]?\\d{3}[A-Z0-9](?:[ \\-]?[A-Z0-9]{4}){2}[ \\-]?[A-Z0-9]{3}|GR\\d{2}[ \\-]?\\d{4}[ \\-]?\\d{3}[A-Z0-9](?:[ \\-]?[A-Z0-9]{4}){3}[A-Z0-9]{3}|(?:FR|MC)\\d{2}(?:[ \\-]?\\d{4}){2}[ \\-]?\\d{2}[A-Z0-9]{2}(?:[ \\-]?[A-Z0-9]{4}){2}[ \\-]?[A-Z0-9]\\d{2}|MR\\d{2}(?:[ \\-]?\\d{4}){5}[ \\-]?\\d{3}|(?:SV|DO)\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?\\d{4}){5}|BY\\d{2}[ \\-]?[A-Z]{4}[ \\-]?\\d{4}(?:[ \\-]?[A-Z0-9]{4}){4}|GT\\d{2}(?:[ \\-]?[A-Z0-9]{4}){6}|AZ\\d{2}[ \\-]?[A-Z0-9]{4}(?:[ \\-]?\\d{5}){4}|LB\\d{2}[ \\-]?\\d{4}(?:[ \\-]?[A-Z0-9]{5}){4}|(?:AL|CY)\\d{2}(?:[ \\-]?\\d{4}){2}(?:[ \\-]?[A-Z0-9]{4}){4}|(?:HU|PL)\\d{2}(?:[ \\-]?\\d{4}){6}|QA\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?[A-Z0-9]{4}){5}[ \\-]?[A-Z0-9]|PS\\d{2}[ \\-]?[A-Z0-9]{4}(?:[ \\-]?\\d{4}){5}[ \\-]?\\d|UA\\d{2}[ \\-]?\\d{4}[ \\-]?\\d{2}[A-Z0-9]{2}(?:[ \\-]?[A-Z0-9]{4}){4}[ \\-]?[A-Z0-9]|BR\\d{2}(?:[ \\-]?\\d{4}){5}[ \\-]?\\d{3}[A-Z0-9][ \\-]?[A-Z0-9]|EG\\d{2}(?:[ \\-]?\\d{4}){6}\\d|MU\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?\\d{4}){4}\\d{3}[A-Z][ \\-]?[A-Z]{2}|(?:KW|JO)\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?[A-Z0-9]{4}){5}[ \\-]?[A-Z0-9]{2}|MT\\d{2}[ \\-]?[A-Z]{4}[ \\-]?\\d{4}[ \\-]?\\d[A-Z0-9]{3}(?:[ \\-]?[A-Z0-9]{3}){4}[ \\-]?[A-Z0-9]{3}|SC\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?\\d{4}){5}[ \\-]?[A-Z]{3}|LC\\d{2}[ \\-]?[A-Z]{4}(?:[ \\-]?[A-Z0-9]{4}){6})\\b", + "options": { + "case_sensitive": false, + "min_length": 15 + } + } + }, + "tags": { + "type": "iban", + "category": "payment" + } + }, + { + "id": "h6WJcecQTwqvN9KeEtwDvg", + "name": "JCB Card Scanner (1x16 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b35(?:2[89]|[3-9][0-9])(?:\\d{12})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "jcb", + "category": "payment" + } + }, + { + "id": "gcEaMu_VSJ2-bGCEkgyC0w", + "name": "JCB Card Scanner (2x8 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b35(?:2[89]|[3-9][0-9])\\d{4}(?:(?:,\\d{8})|(?:-\\d{8})|(?:\\s\\d{8})|(?:\\.\\d{8}))\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "card", + "card_type": "jcb", + "category": "payment" + } + }, + { + "id": "imTliuhXT5GAeRNhqChXQQ", + "name": "JCB Card Scanner (4x4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b35(?:2[89]|[3-9][0-9])(?:(?:\\s\\d{4}){3}|(?:\\.\\d{4}){3}|(?:-\\d{4}){3}|(?:,\\d{4}){3})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "jcb", + "category": "payment" + } + }, + { + "id": "9osY3xc9Q7ONAV0zw9Uz4A", + "name": "JSON Web Token", + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\bey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(\\.[\\w.+\\/=-]+)?\\b", + "options": { + "case_sensitive": false, + "min_length": 20 + } + } + }, + "tags": { + "type": "json_web_token", + "category": "credentials" + } + }, + { + "id": "d1Q9D3YMRxuVKf6CZInJPw", + "name": "Maestro Card Scanner (1x16 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:5[06-9]\\d{2}|6\\d{3})(?:\\d{12})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "maestro", + "category": "payment" + } + }, + { + "id": "M3YIQKKjRVmoeQuM3pjzrw", + "name": "Maestro Card Scanner (2x8 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:5[06-9]\\d{6}|6\\d{7})(?:\\s\\d{8}|\\.\\d{8}|-\\d{8}|,\\d{8})\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "card", + "card_type": "maestro", + "category": "payment" + } + }, + { + "id": "hRxiQBlSSVKcjh5U7LZYLA", + "name": "Maestro Card Scanner (4x4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:5[06-9]\\d{2}|6\\d{3})(?:(?:\\s\\d{4}){3}|(?:\\.\\d{4}){3}|(?:-\\d{4}){3}|(?:,\\d{4}){3})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "maestro", + "category": "payment" + } + }, + { + "id": "NwhIYNS4STqZys37WlaIKA", + "name": "MasterCard Scanner (2x8 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:(?:5[1-5]\\d{2})|(?:222[1-9])|(?:22[3-9]\\d)|(?:2[3-6]\\d{2})|(?:27[0-1]\\d)|(?:2720))(?:(?:\\d{4}(?:(?:,\\d{8})|(?:-\\d{8})|(?:\\s\\d{8})|(?:\\.\\d{8}))))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "mastercard", + "category": "payment" + } + }, + { + "id": "axxJkyjhRTOuhjwlsA35Vw", + "name": "MasterCard Scanner (4x4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:(?:5[1-5]\\d{2})|(?:222[1-9])|(?:22[3-9]\\d)|(?:2[3-6]\\d{2})|(?:27[0-1]\\d)|(?:2720))(?:(?:\\s\\d{4}){3}|(?:\\.\\d{4}){3}|(?:-\\d{4}){3}|(?:,\\d{4}){3})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "mastercard", + "category": "payment" + } + }, + { + "id": "76EhmoK3TPqJcpM-fK0pLw", + "name": "MasterCard Scanner (1x16 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:(?:5[1-5]\\d{2})|(?:222[1-9])|(?:22[3-9]\\d)|(?:2[3-6]\\d{2})|(?:27[0-1]\\d)|(?:2720))(?:\\d{12})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "mastercard", + "category": "payment" + } + }, + { + "id": "de0899e0cbaaa812bb624cf04c912071012f616d-mod", + "name": "UK National Insurance Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "^nin$|\\binsurance\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[A-Z]{2}[\\s-]?\\d{6}[\\s-]?[A-Z]?\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "tags": { + "type": "uk_nin", + "category": "pii" + } + }, + { + "id": "d962f7ddb3f55041e39195a60ff79d4814a7c331", + "name": "US Passport Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\bpassport\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[0-9A-Z]{9}\\b|\\b[0-9]{6}[A-Z][0-9]{2}\\b", + "options": { + "case_sensitive": false, + "min_length": 8 + } + } + }, + "tags": { + "type": "passport_number", + "category": "pii" + } + }, + { + "id": "7771fc3b-b205-4b93-bcef-28608c5c1b54", + "name": "United States Social Security Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:SSN|(?:(?:social)?[\\s_]?(?:security)?[\\s_]?(?:number)?)?)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b\\d{3}[-\\s\\.]{1}\\d{2}[-\\s\\.]{1}\\d{4}\\b", + "options": { + "case_sensitive": false, + "min_length": 11 + } + } + }, + "tags": { + "type": "us_ssn", + "category": "pii" + } + }, + { + "id": "ac6d683cbac77f6e399a14990793dd8fd0fca333", + "name": "US Vehicle Identification Number Scanner", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:vehicle[_\\s-]*identification[_\\s-]*number|vin)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b[A-HJ-NPR-Z0-9]{17}\\b", + "options": { + "case_sensitive": false, + "min_length": 17 + } + } + }, + "tags": { + "type": "vin", + "category": "pii" + } + }, + { + "id": "wJIgOygRQhKkR69b_9XbRQ", + "name": "Visa Card Scanner (2x8 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b4\\d{3}(?:(?:\\d{4}(?:(?:,\\d{8})|(?:-\\d{8})|(?:\\s\\d{8})|(?:\\.\\d{8}))))\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "visa", + "category": "payment" + } + }, + { + "id": "0o71SJxXQNK7Q6gMbBesFQ", + "name": "Visa Card Scanner (4x4 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "\\b4\\d{3}(?:(?:,\\d{4}){3}|(?:\\s\\d{4}){3}|(?:\\.\\d{4}){3}|(?:-\\d{4}){3})\\b", + "options": { + "case_sensitive": false, + "min_length": 16 + } + } + }, + "tags": { + "type": "card", + "card_type": "visa", + "category": "payment" + } + }, + { + "id": "QrHD6AfgQm6z-j0wStxTvA", + "name": "Visa Card Scanner (1x15 & 1x16 & 1x19 digits)", + "key": { + "operator": "match_regex", + "parameters": { + "regex": "\\b(?:card|cc|credit|debit|payment|amex|visa|mastercard|maestro|discover|jcb|diner)\\b", + "options": { + "case_sensitive": false, + "min_length": 3 + } + } + }, + "value": { + "operator": "match_regex", + "parameters": { + "regex": "4[0-9]{12}(?:[0-9]{3})?", + "options": { + "case_sensitive": false, + "min_length": 13 + } + } + }, + "tags": { + "type": "card", + "card_type": "visa", + "category": "payment" + } + } + ] +} \ No newline at end of file diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/limiter.go b/vendor/github.com/DataDog/appsec-internal-go/limiter/limiter.go similarity index 70% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/limiter.go rename to vendor/github.com/DataDog/appsec-internal-go/limiter/limiter.go index c022ecfc6..f1f16d363 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/limiter.go +++ b/vendor/github.com/DataDog/appsec-internal-go/limiter/limiter.go @@ -1,16 +1,15 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2022 Datadog, Inc. +// Copyright 2022-present Datadog, Inc. -//go:build appsec -// +build appsec - -package appsec +// Package limiter provides simple rate limiting primitives, and an implementation of a token bucket rate limiter. +package limiter import ( - "sync/atomic" "time" + + "sync/atomic" ) // Limiter is used to abstract the rate limiter implementation to only expose the needed function for rate limiting. @@ -27,35 +26,43 @@ type Limiter interface { // TokenTicker.Allow() and TokenTicker.Stop() *must* be called once done using. Note that calling TokenTicker.Allow() // before TokenTicker.Start() is valid, but it means the bucket won't be refilling until the call to TokenTicker.Start() is made type TokenTicker struct { - tokens int64 - maxTokens int64 - ticker *time.Ticker - stopChan chan struct{} + tokens atomic.Int64 // The amount of tokens currently available + maxTokens int64 // The maximum amount of tokens the bucket can hold + ticker *time.Ticker // The ticker used to update the bucket (nil if not started yet) + stopChan chan struct{} // The channel to stop the ticker updater (nil if not started yet) } // NewTokenTicker is a utility function that allocates a token ticker, initializes necessary fields and returns it func NewTokenTicker(tokens, maxTokens int64) *TokenTicker { - return &TokenTicker{ - tokens: tokens, + t := &TokenTicker{ maxTokens: maxTokens, } + t.tokens.Store(tokens) + return t } // updateBucket performs a select loop to update the token amount in the bucket. // Used in a goroutine by the rate limiter. -func (t *TokenTicker) updateBucket(ticksChan <-chan time.Time, startTime time.Time, syncChan chan struct{}) { +func (t *TokenTicker) updateBucket(startTime time.Time, ticksChan <-chan time.Time, stopChan <-chan struct{}, syncChan chan<- struct{}) { nsPerToken := time.Second.Nanoseconds() / t.maxTokens elapsedNs := int64(0) prevStamp := startTime for { select { - case <-t.stopChan: + case <-stopChan: if syncChan != nil { close(syncChan) } return - case stamp := <-ticksChan: + case stamp, ok := <-ticksChan: + if !ok { + // The ticker has been closed, stamp is a zero-value, we ignore that. We nil-out the + // ticksChan so we don't get stuck endlessly reading from this closed channel again. + ticksChan = nil + continue + } + // Compute the time in nanoseconds that passed between the previous timestamp and this one // This will be used to know how many tokens can be added into the bucket depending on the limiter rate elapsedNs += stamp.Sub(prevStamp).Nanoseconds() @@ -67,7 +74,7 @@ func (t *TokenTicker) updateBucket(ticksChan <-chan time.Time, startTime time.Ti if elapsedNs >= nsPerToken { // Atomic spin lock to make sure we don't race for `t.tokens` for { - tokens := atomic.LoadInt64(&t.tokens) + tokens := t.tokens.Load() if tokens == t.maxTokens { break // Bucket is already full, nothing to do } @@ -76,7 +83,7 @@ func (t *TokenTicker) updateBucket(ticksChan <-chan time.Time, startTime time.Ti if tokens+inc > t.maxTokens { inc -= (tokens + inc) % t.maxTokens } - if atomic.CompareAndSwapInt64(&t.tokens, tokens, tokens+inc) { + if t.tokens.CompareAndSwap(tokens, tokens+inc) { // Keep track of remaining elapsed ns that were not taken into account for this computation, // so that increment computation remains precise over time elapsedNs = elapsedNs % nsPerToken @@ -98,22 +105,17 @@ func (t *TokenTicker) updateBucket(ticksChan <-chan time.Time, startTime time.Ti func (t *TokenTicker) Start() { timeNow := time.Now() t.ticker = time.NewTicker(500 * time.Microsecond) - t.start(t.ticker.C, timeNow, false) + t.start(timeNow, t.ticker.C, nil) } // start is used for internal testing. Controlling the ticker means being able to test per-tick -// rather than per-duration, which is more reliable if the app is under a lot of stress. -// sync is used to decide whether the limiter should create a channel for synchronization with the testing app after a -// bucket update. The limiter is in charge of closing the channel in this case. -func (t *TokenTicker) start(ticksChan <-chan time.Time, startTime time.Time, sync bool) <-chan struct{} { +// rather than per-duration, which is more reliable if the app is under a lot of stress. The +// syncChan, if non-nil, will receive one message after each tick from the ticksChan has been +// processed, providing a strong synchronization primitive. The limiter will close the syncChan when +// it is stopped, signaling that no further ticks will be processed. +func (t *TokenTicker) start(startTime time.Time, ticksChan <-chan time.Time, syncChan chan<- struct{}) { t.stopChan = make(chan struct{}) - var syncChan chan struct{} - - if sync { - syncChan = make(chan struct{}) - } - go t.updateBucket(ticksChan, startTime, syncChan) - return syncChan + go t.updateBucket(startTime, ticksChan, t.stopChan, syncChan) } // Stop shuts down the rate limiter, taking care stopping the ticker and closing all channels @@ -121,11 +123,13 @@ func (t *TokenTicker) Stop() { // Stop the ticker only if it has been instantiated (not the case when testing by calling start() directly) if t.ticker != nil { t.ticker.Stop() + t.ticker = nil // Ensure stop can be called multiple times idempotently. } // Close the stop channel only if it has been created. This covers the case where Stop() is called without any prior // call to Start() if t.stopChan != nil { close(t.stopChan) + t.stopChan = nil // Ensure stop can be called multiple times idempotently. } } @@ -133,10 +137,10 @@ func (t *TokenTicker) Stop() { // Thread-safe. func (t *TokenTicker) Allow() bool { for { - tokens := atomic.LoadInt64(&t.tokens) + tokens := t.tokens.Load() if tokens == 0 { return false - } else if atomic.CompareAndSwapInt64(&t.tokens, tokens, tokens-1) { + } else if t.tokens.CompareAndSwap(tokens, tokens-1) { return true } } diff --git a/vendor/github.com/DataDog/appsec-internal-go/log/backend.go b/vendor/github.com/DataDog/appsec-internal-go/log/backend.go new file mode 100644 index 000000000..b9d94f5cd --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/log/backend.go @@ -0,0 +1,138 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package log + +import ( + "fmt" + "log" + "os" + "strings" +) + +var ( + backend = Backend{ + Trace: defaultWithLevel(logLevelTrace), + Debug: defaultWithLevel(logLevelDebug), + Info: defaultWithLevel(logLevelInfo), + Warn: defaultWithLevel(logLevelWarn), + Errorf: defaultErrorfWithLevel(logLevelError), + Criticalf: defaultErrorfWithLevel(logLevelCritical), + } + defaultBackendLogLevel = logLevelError +) + +type Backend struct { + Trace func(string, ...any) + Debug func(string, ...any) + Info func(string, ...any) + Warn func(string, ...any) + Errorf func(string, ...any) error + Criticalf func(string, ...any) error +} + +// SetBackend replaces the active log backend with the provided one. Any nil +// function in the new backend will silently ignore any message logged at that +// level. +func SetBackend(newBackend Backend) { + if newBackend.Trace == nil { + newBackend.Trace = noopLogger + } + if newBackend.Debug == nil { + newBackend.Debug = noopLogger + } + if newBackend.Info == nil { + newBackend.Info = noopLogger + } + if newBackend.Warn == nil { + newBackend.Warn = noopLogger + } + if newBackend.Errorf == nil { + newBackend.Errorf = fmt.Errorf + } + if newBackend.Criticalf == nil { + newBackend.Criticalf = fmt.Errorf + } + + backend = newBackend +} + +// defaultWithLevel returns the default log backend function for the provided +// logLevel. This returns a no-op function if the default backend logLevel does +// not enable logging at that level. +func defaultWithLevel(level logLevel) func(string, ...any) { + if defaultBackendLogLevel < level { + return noopLogger + } + return func(format string, args ...any) { + log.Printf(fmt.Sprintf("[%s] %s\n", level, format), args...) + } +} + +// defaultErrorfWithLevel returns the default log backend function for the +// provided error logLevel. +func defaultErrorfWithLevel(level logLevel) func(string, ...any) error { + if defaultBackendLogLevel < level { + return fmt.Errorf + } + return func(format string, args ...any) error { + err := fmt.Errorf(format, args...) + log.Printf("[%s] %v", level, err) + return err + } +} + +// noopLogger does nothing. +func noopLogger(string, ...any) { /* noop */ } + +type logLevel uint8 + +const ( + logLevelTrace logLevel = 1 << iota + logLevelDebug + logLevelInfo + logLevelWarn + logLevelError + logLevelCritical +) + +func (l logLevel) String() string { + switch l { + case logLevelTrace: + return "TRACE" + case logLevelDebug: + return "DEBUG" + case logLevelInfo: + return "INFO" + case logLevelWarn: + return "WARN" + case logLevelError: + return "ERROR" + case logLevelCritical: + return "CRITICAL" + default: + return "UNKNOWN" + } +} + +func init() { + ddLogLevel := os.Getenv("DD_LOG_LEVEL") + switch strings.ToUpper(ddLogLevel) { + case "TRACE": + defaultBackendLogLevel = logLevelTrace + case "DEBUG": + defaultBackendLogLevel = logLevelDebug + case "INFO": + defaultBackendLogLevel = logLevelInfo + case "WARN": + defaultBackendLogLevel = logLevelWarn + case "ERROR": + defaultBackendLogLevel = logLevelError + case "CRITICAL": + defaultBackendLogLevel = logLevelCritical + default: + // Ignore invalid/unexpected values + } +} diff --git a/vendor/github.com/DataDog/appsec-internal-go/log/log.go b/vendor/github.com/DataDog/appsec-internal-go/log/log.go new file mode 100644 index 000000000..a34f578d7 --- /dev/null +++ b/vendor/github.com/DataDog/appsec-internal-go/log/log.go @@ -0,0 +1,45 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +// Package log provides a logging facility that is used by this library, and +// which can be configured to piggyback on another logging facility where +// available. If not explicitly configured, this will log messages using the Go +// standar library log package, filtered according to the log level set in the +// `DD_LOG_LEVEL` environment variable (or `ERROR` if none is set). +// +// Custom logger intergrations are configured by calling the SetBackend function. +package log + +// Trace logs a message with format using the TRACE log level. +func Trace(format string, args ...any) { + backend.Trace(format, args...) +} + +// Debug logs a message with format using the DEBUG log level. +func Debug(format string, args ...any) { + backend.Debug(format, args...) +} + +// Info logs a message with format using the INFO log level. +func Info(format string, args ...any) { + backend.Info(format, args...) +} + +// Warn logs a message with format using the WARN log level. +func Warn(format string, args ...any) { + backend.Warn(format, args...) +} + +// Errorf logs a message with format using the ERROR log level and returns an +// error containing the formatted log message. +func Errorf(format string, args ...any) error { + return backend.Errorf(format, args...) +} + +// Errorf logs a message with format using the CRITICAL log level and returns an +// error containing the formatted log message. +func Criticalf(format string, args ...any) error { + return backend.Criticalf(format, args...) +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go index 3121a553e..3993390d2 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go @@ -1,10 +1,15 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + package obfuscate import ( "fmt" "time" - "github.com/dgraph-io/ristretto" + "github.com/outcaste-io/ristretto" ) // measuredCache is a wrapper on top of *ristretto.Cache which additionally diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go index 050188194..03adf1544 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go @@ -90,7 +90,6 @@ loop: // See: // • https://en.wikipedia.org/wiki/Luhn_algorithm // • https://dev.to/shiraazm/goluhn-a-simple-library-for-generating-calculating-and-verifying-luhn-numbers-588j -// func luhnValid(str []byte) bool { var ( sum int @@ -155,9 +154,9 @@ func validCardPrefix(n int) (maybe, yes bool) { // 34-39, 51-55, 62, 65 are valid IIN return false, true } - if n == 30 || n == 63 || n == 64 || n == 35 || n == 50 || n == 60 || + if n == 30 || n == 63 || n == 64 || n == 50 || n == 60 || (n >= 22 && n <= 27) || (n >= 56 && n <= 58) || (n >= 60 && n <= 69) { - // 30, 63, 64, 35, 50, 60, 22-27, 56-58, 60-69 may end up as valid IIN + // 30, 63, 64, 50, 60, 22-27, 56-58, 60-69 may end up as valid IIN return true, false } } diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go index 485d1fd17..d9a00084f 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go @@ -10,12 +10,21 @@ import ( "strings" ) -// ObfuscateURLString obfuscates the given URL. It must be a valid URL and at least one -// HTTP obfuscation option must be enabled at Obfuscator instantiation time. +// obfuscateUserInfo returns a URL string that obfuscates any userinfo by setting url.User to nil. +func obfuscateUserInfo(val string) string { + u, err := url.Parse(val) + if err != nil { + return val + } + u.User = nil + return u.String() +} + +// ObfuscateURLString obfuscates the given URL. It must be a valid URL. func (o *Obfuscator) ObfuscateURLString(val string) string { if !o.opts.HTTP.RemoveQueryString && !o.opts.HTTP.RemovePathDigits { // nothing to do - return val + return obfuscateUserInfo(val) } u, err := url.Parse(val) if err != nil { @@ -23,6 +32,7 @@ func (o *Obfuscator) ObfuscateURLString(val string) string { // rather than expose sensitive information when this option is on. return "?" } + u.User = nil if o.opts.HTTP.RemoveQueryString && u.RawQuery != "" { u.ForceQuery = true // add the '?' u.RawQuery = "" diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go index aef236f31..8252a9f0f 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go @@ -6,8 +6,10 @@ package obfuscate import ( + "bytes" "strconv" "strings" + "sync" ) // ObfuscateMongoDBString obfuscates the given MongoDB JSON query. @@ -35,18 +37,11 @@ func obfuscateJSONString(cmd string, obfuscator *jsonObfuscator) string { } type jsonObfuscator struct { + buffPool sync.Pool // pool for fixed-length buffers (50 showed to be the optimal running benchmarks with different length) + statePool sync.Pool // pool for jsonObfuscatorState values keepKeys map[string]bool // the values for these keys will not be obfuscated transformKeys map[string]bool // the values for these keys pass through the transformer transformer func(string) string - - scan *scanner // scanner - closures []bool // closure stack, true if object (e.g. {[{ => []bool{true, false, true}) - key bool // true if scanning a key - - wiped bool // true if obfuscation string (`"?"`) was already written for current value - keeping bool // true if not obfuscating - transformingValue bool // true if collecting the next literal for transformation - keepDepth int // the depth at which we've stopped obfuscating } func newJSONObfuscator(cfg *JSONConfig, o *Obfuscator) *jsonObfuscator { @@ -66,11 +61,21 @@ func newJSONObfuscator(cfg *JSONConfig, o *Obfuscator) *jsonObfuscator { } } return &jsonObfuscator{ - closures: []bool{}, keepKeys: keepValue, transformKeys: transformKeys, transformer: transformer, - scan: &scanner{}, + buffPool: sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, + }, + statePool: sync.Pool{ + New: func() any { + return &jsonObfuscatorState{ + closures: []bool{}, + } + }, + }, } } @@ -87,115 +92,138 @@ func sqlObfuscationTransformer(o *Obfuscator) func(string) string { } } +type jsonObfuscatorState struct { + scan scanner // scanner + closures []bool // closure stack, true if object (e.g. {[{ => []bool{true, false, true}) + keepDepth int // the depth at which we've stopped obfuscating + key bool // true if scanning a key + wiped bool // true if obfuscation string (`"?"`) was already written for current value + keeping bool // true if not obfuscating + transformingValue bool // true if collecting the next literal for transformation +} + +func (st *jsonObfuscatorState) reset() { + st.scan.reset() + st.closures = st.closures[0:0] + st.keepDepth = 0 + st.key = false + st.wiped = false + st.keeping = false + st.transformingValue = false +} + // setKey verifies if we are currently scanning a key based on the current state // and updates the state accordingly. It must be called only after a closure or a // value scan has ended. -func (p *jsonObfuscator) setKey() { - n := len(p.closures) - p.key = n == 0 || p.closures[n-1] // true if we are at top level or in an object - p.wiped = false +func (st *jsonObfuscatorState) setKey() { + n := len(st.closures) + st.key = n == 0 || st.closures[n-1] // true if we are at top level or in an object + st.wiped = false } func (p *jsonObfuscator) obfuscate(data []byte) (string, error) { - var out strings.Builder - - keyBuf := make([]byte, 0, 10) // recording key token - valBuf := make([]byte, 0, 10) // recording value + if len(data) == 0 { + return "", nil + } - p.scan.reset() + var out strings.Builder + st := p.statePool.Get().(*jsonObfuscatorState) + st.reset() + + buf := p.buffPool.Get().(*bytes.Buffer) // recording current token + buf.Reset() + defer func() { + p.statePool.Put(st) + p.buffPool.Put(buf) + }() + + out.Grow(len(data)) + buf.Grow(len(data) / 10) // Benchmarks show that the optimal point is a tenth of the data length. for _, c := range data { - p.scan.bytes++ - op := p.scan.step(p.scan, c) - depth := len(p.closures) + st.scan.bytes++ + op := st.scan.step(&st.scan, c) + depth := len(st.closures) switch op { case scanBeginObject: // object begins: { - p.closures = append(p.closures, true) - p.setKey() - p.transformingValue = false - + st.closures = append(st.closures, true) + st.setKey() + st.transformingValue = false case scanBeginArray: // array begins: [ - p.closures = append(p.closures, false) - p.setKey() - p.transformingValue = false - + st.closures = append(st.closures, false) + st.setKey() + st.transformingValue = false case scanEndArray, scanEndObject: // array or object closing - if n := len(p.closures) - 1; n > 0 { - p.closures = p.closures[:n] + if n := len(st.closures) - 1; n > 0 { + st.closures = st.closures[:n] } fallthrough - case scanObjectValue, scanArrayValue: // done scanning value - p.setKey() - if p.transformingValue && p.transformer != nil { - v, err := strconv.Unquote(string(valBuf)) + st.setKey() + if st.transformingValue && p.transformer != nil { + v, err := strconv.Unquote(buf.String()) if err != nil { - v = string(valBuf) + v = buf.String() } result := p.transformer(v) out.WriteByte('"') out.WriteString(result) out.WriteByte('"') - p.transformingValue = false - valBuf = valBuf[:0] - } else if p.keeping && depth < p.keepDepth { - p.keeping = false + st.transformingValue = false + buf.Reset() + } else if st.keeping && depth < st.keepDepth { + st.keeping = false } - case scanBeginLiteral, scanContinue: // starting or continuing a literal - if p.transformingValue { - valBuf = append(valBuf, c) + if st.transformingValue { + buf.WriteByte(c) continue - } else if p.key { + } else if st.key { // it's a key - keyBuf = append(keyBuf, c) - } else if !p.keeping { + buf.WriteByte(c) + } else if !st.keeping { // it's a value we're not keeping - if !p.wiped { - out.Write([]byte(`"?"`)) - p.wiped = true + if !st.wiped { + out.WriteString(`"?"`) + st.wiped = true } continue } - case scanObjectKey: // done scanning key - k := strings.Trim(string(keyBuf), `"`) - if !p.keeping && p.keepKeys[k] { + k := string(bytes.Trim(buf.Bytes(), `"`)) + if !st.keeping && p.keepKeys[k] { // we should not obfuscate values of this key - p.keeping = true - p.keepDepth = depth + 1 - } else if !p.transformingValue && p.transformer != nil && p.transformKeys[k] { + st.keeping = true + st.keepDepth = depth + 1 + } else if !st.transformingValue && p.transformer != nil && p.transformKeys[k] { // the string value immediately following this key will be passed through the value transformer // if anything other than a literal is found then sql obfuscation is stopped and json obfuscation // proceeds as usual - p.transformingValue = true + st.transformingValue = true } - - keyBuf = keyBuf[:0] - p.key = false - + buf.Reset() + st.key = false case scanSkipSpace: continue - case scanError: // we've encountered an error, mark that there might be more JSON // using the ellipsis and return whatever we've managed to obfuscate // thus far. - out.Write([]byte("...")) - return out.String(), p.scan.err + out.WriteString("...") + return out.String(), st.scan.err } out.WriteByte(c) } - if p.scan.eof() == scanError { + if st.scan.eof() == scanError { // if an error occurred it's fine, simply add the ellipsis to indicate // that the input has been truncated. out.Write([]byte("...")) - return out.String(), p.scan.err + return out.String(), st.scan.err } return out.String(), nil } diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go index 9398632d8..e642aa2c9 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go @@ -53,9 +53,6 @@ type scanner struct { // Error that happened, if any. err error - // 1-byte redo (see undo method) - redo bool - // total bytes consumed, updated by decoder.Decode bytes int64 } @@ -101,7 +98,6 @@ func (s *scanner) reset() { s.step = stateBeginValue s.parseState = s.parseState[0:0] s.err = nil - s.redo = false s.endTop = false } @@ -133,14 +129,13 @@ func (s *scanner) pushParseState(p int) { // and updates s.step accordingly. func (s *scanner) popParseState() { n := len(s.parseState) - 1 - s.parseState = s.parseState[0:n] - s.redo = false if n == 0 { s.step = stateEndTop s.endTop = true - } else { - s.step = stateEndValue + return } + s.parseState = s.parseState[0:n] + s.step = stateEndValue } func isSpace(c byte) bool { @@ -203,8 +198,8 @@ func stateBeginStringOrEmpty(s *scanner, c byte) int { if c <= ' ' && isSpace(c) { return scanSkipSpace } - if c == '}' { - n := len(s.parseState) + n := len(s.parseState) + if c == '}' && n > 0 { s.parseState[n-1] = parseObjectValue return stateEndValue(s, c) } diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go index b474c1a2e..62ad512bd 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go @@ -14,13 +14,11 @@ package obfuscate import ( "bytes" - "sync/atomic" - "github.com/DataDog/datadog-go/statsd" + "github.com/DataDog/datadog-go/v5/statsd" + "go.uber.org/atomic" ) -//go:generate easyjson -no_std_marshalers $GOFILE - // Obfuscator quantizes and obfuscates spans. The obfuscator is not safe for // concurrent use. type Obfuscator struct { @@ -30,10 +28,8 @@ type Obfuscator struct { sqlExecPlan *jsonObfuscator // nil if disabled sqlExecPlanNormalize *jsonObfuscator // nil if disabled // sqlLiteralEscapes reports whether we should treat escape characters literally or as escape characters. - // A non-zero value means 'yes'. Different SQL engines behave in different ways and the tokenizer needs - // to be generic. - // Not safe for concurrent use. - sqlLiteralEscapes int32 + // Different SQL engines behave in different ways and the tokenizer needs to be generic. + sqlLiteralEscapes *atomic.Bool // queryCache keeps a cache of already obfuscated queries. queryCache *measuredCache log Logger @@ -52,9 +48,9 @@ func (noopLogger) Debugf(_ string, _ ...interface{}) {} // setSQLLiteralEscapes sets whether or not escape characters should be treated literally by the SQL obfuscator. func (o *Obfuscator) setSQLLiteralEscapes(ok bool) { if ok { - atomic.StoreInt32(&o.sqlLiteralEscapes, 1) + o.sqlLiteralEscapes.Store(true) } else { - atomic.StoreInt32(&o.sqlLiteralEscapes, 0) + o.sqlLiteralEscapes.Store(false) } } @@ -62,7 +58,7 @@ func (o *Obfuscator) setSQLLiteralEscapes(ok bool) { // Some SQL engines require it and others don't. It will be detected as SQL queries are being obfuscated // through calls to ObfuscateSQLString and automatically set for future. func (o *Obfuscator) useSQLLiteralEscapes() bool { - return atomic.LoadInt32(&o.sqlLiteralEscapes) == 1 + return o.sqlLiteralEscapes.Load() } // Config holds the configuration for obfuscating sensitive data for various span types. @@ -86,6 +82,9 @@ type Config struct { // HTTP holds the obfuscation settings for HTTP URLs. HTTP HTTPConfig + // Redis holds the obfuscation settings for Redis commands. + Redis RedisConfig + // Statsd specifies the statsd client to use for reporting metrics. Statsd StatsClient @@ -101,29 +100,52 @@ type StatsClient interface { } // SQLConfig holds the config for obfuscating SQL. -// easyjson:json type SQLConfig struct { + // DBMS identifies the type of database management system (e.g. MySQL, Postgres, and SQL Server). + // Valid values for this can be found at https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#connection-level-attributes + DBMS string `json:"dbms"` + // TableNames specifies whether the obfuscator should also extract the table names that a query addresses, // in addition to obfuscating. - TableNames bool + TableNames bool `json:"table_names" yaml:"table_names"` + + // CollectCommands specifies whether the obfuscator should extract and return commands as SQL metadata when obfuscating. + CollectCommands bool `json:"collect_commands" yaml:"collect_commands"` + + // CollectComments specifies whether the obfuscator should extract and return comments as SQL metadata when obfuscating. + CollectComments bool `json:"collect_comments" yaml:"collect_comments"` // ReplaceDigits specifies whether digits in table names and identifiers should be obfuscated. - ReplaceDigits bool `json:"replace_digits"` + ReplaceDigits bool `json:"replace_digits" yaml:"replace_digits"` // KeepSQLAlias reports whether SQL aliases ("AS") should be truncated. - KeepSQLAlias bool + KeepSQLAlias bool `json:"keep_sql_alias"` // DollarQuotedFunc reports whether to treat "$func$" delimited dollar-quoted strings // differently and not obfuscate them as a string. To read more about dollar quoted // strings see: // // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING - DollarQuotedFunc bool + DollarQuotedFunc bool `json:"dollar_quoted_func"` // Cache reports whether the obfuscator should use a LRU look-up cache for SQL obfuscations. Cache bool } +// SQLMetadata holds metadata collected throughout the obfuscation of an SQL statement. It is only +// collected when enabled via SQLConfig. +type SQLMetadata struct { + // Size holds the byte size of the metadata collected. + Size int64 + // TablesCSV is a comma-separated list of tables that the query addresses. + TablesCSV string `json:"tables_csv"` + // Commands holds commands executed in an SQL statement. + // e.g. SELECT, UPDATE, INSERT, DELETE, etc. + Commands []string `json:"commands"` + // Comments holds comments in an SQL statement. + Comments []string `json:"comments"` +} + // HTTPConfig holds the configuration settings for HTTP obfuscation. type HTTPConfig struct { // RemoveQueryStrings determines query strings to be removed from HTTP URLs. @@ -133,6 +155,16 @@ type HTTPConfig struct { RemovePathDigits bool } +// RedisConfig holds the configuration settings for Redis obfuscation +type RedisConfig struct { + // Enabled specifies whether this feature should be enabled. + Enabled bool + + // RemoveAllArgs specifies whether all arguments to a given Redis + // command should be obfuscated. + RemoveAllArgs bool +} + // JSONConfig holds the obfuscation configuration for sensitive // data found in JSON objects. type JSONConfig struct { @@ -154,8 +186,10 @@ func NewObfuscator(cfg Config) *Obfuscator { cfg.Logger = noopLogger{} } o := Obfuscator{ - opts: &cfg, - queryCache: newMeasuredCache(cacheOptions{On: cfg.SQL.Cache, Statsd: cfg.Statsd}), + opts: &cfg, + queryCache: newMeasuredCache(cacheOptions{On: cfg.SQL.Cache, Statsd: cfg.Statsd}), + sqlLiteralEscapes: atomic.NewBool(false), + log: cfg.Logger, } if cfg.ES.Enabled { o.es = newJSONObfuscator(&cfg.ES, &o) diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go deleted file mode 100644 index 31dc9152d..000000000 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. - -package obfuscate - -import ( - json "encoding/json" - - easyjson "github.com/mailru/easyjson" - jlexer "github.com/mailru/easyjson/jlexer" - jwriter "github.com/mailru/easyjson/jwriter" -) - -// suppress unused package warning -var ( - _ *json.RawMessage - _ *jlexer.Lexer - _ *jwriter.Writer - _ easyjson.Marshaler -) - -func easyjson4ef41860DecodeGithubComDataDogDatadogAgentPkgObfuscate(in *jlexer.Lexer, out *SQLConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "TableNames": - out.TableNames = bool(in.Bool()) - case "replace_digits": - out.ReplaceDigits = bool(in.Bool()) - case "KeepSQLAlias": - out.KeepSQLAlias = bool(in.Bool()) - case "DollarQuotedFunc": - out.DollarQuotedFunc = bool(in.Bool()) - case "Cache": - out.Cache = bool(in.Bool()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson4ef41860EncodeGithubComDataDogDatadogAgentPkgObfuscate(out *jwriter.Writer, in SQLConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"TableNames\":" - out.RawString(prefix[1:]) - out.Bool(bool(in.TableNames)) - } - { - const prefix string = ",\"replace_digits\":" - out.RawString(prefix) - out.Bool(bool(in.ReplaceDigits)) - } - { - const prefix string = ",\"KeepSQLAlias\":" - out.RawString(prefix) - out.Bool(bool(in.KeepSQLAlias)) - } - { - const prefix string = ",\"DollarQuotedFunc\":" - out.RawString(prefix) - out.Bool(bool(in.DollarQuotedFunc)) - } - { - const prefix string = ",\"Cache\":" - out.RawString(prefix) - out.Bool(bool(in.Cache)) - } - out.RawByte('}') -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v SQLConfig) MarshalEasyJSON(w *jwriter.Writer) { - easyjson4ef41860EncodeGithubComDataDogDatadogAgentPkgObfuscate(w, v) -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *SQLConfig) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson4ef41860DecodeGithubComDataDogDatadogAgentPkgObfuscate(l, v) -} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go index 13591af68..f1cacac15 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go @@ -245,6 +245,45 @@ func obfuscateRedisCmd(out *strings.Builder, cmd string, args ...string) { out.WriteString(strings.Join(args, " ")) } +// removeAllRedisArgs will take in a command and obfuscate all arguments following +// the command, regardless of if the command is valid Redis or not +func (*Obfuscator) RemoveAllRedisArgs(rediscmd string) string { + fullCmd := strings.Fields(rediscmd) + if len(fullCmd) == 0 { + return "" + } + cmd, args := fullCmd[0], fullCmd[1:] + + var out strings.Builder + out.WriteString(cmd) + if len(args) == 0 { + return out.String() + } + + out.WriteByte(' ') + switch strings.ToUpper(cmd) { + case "BITFIELD": + out.WriteString("?") + for _, a := range args { + arg := strings.ToUpper(a) + if arg == "SET" || arg == "GET" || arg == "INCRBY" { + out.WriteString(strings.Join([]string{"", a, "?"}, " ")) + } + } + case "CONFIG": + arg := strings.ToUpper(args[0]) + if arg == "GET" || arg == "SET" || arg == "RESETSTAT" || arg == "REWRITE" { + out.WriteString(strings.Join([]string{args[0], "?"}, " ")) + } else { + out.WriteString("?") + } + default: + out.WriteString("?") + } + + return out.String() +} + func obfuscateRedisArgN(args []string, n int) { if len(args) > n { args[n] = "?" diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go index c8aba20bf..2a3bbdee7 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go @@ -16,9 +16,111 @@ import ( var questionMark = []byte("?") +// metadataFinderFilter is a filter which attempts to collect metadata from a query, such as comments and tables. +// It is meant to run before all the other filters. +type metadataFinderFilter struct { + collectTableNames bool + collectCommands bool + collectComments bool + replaceDigits bool + + // size holds the byte size of the metadata collected by the filter. + size int64 + // tablesSeen keeps track of unique table names encountered by the filter. + tablesSeen map[string]struct{} + // tablesCSV specifies a comma-separated list of tables. + tablesCSV strings.Builder + // commands keeps track of commands encountered by the filter. + commands []string + // comments keeps track of comments encountered by the filter. + comments []string +} + +func (f *metadataFinderFilter) Filter(token, lastToken TokenKind, buffer []byte) (TokenKind, []byte, error) { + if f.collectComments && token == Comment { + // A comment with line-breaks will be brought to a single line. + comment := strings.TrimSpace(strings.Replace(string(buffer), "\n", " ", -1)) + f.size += int64(len(comment)) + f.comments = append(f.comments, comment) + } + if f.collectCommands { + switch token { + case Select, Update, Insert, Delete, Join, Alter, Drop, Create, Grant, Revoke, Commit, Begin, Truncate: + command := strings.ToUpper(token.String()) + f.size += int64(len(command)) + f.commands = append(f.commands, command) + } + } + if f.collectTableNames { + switch lastToken { + case From, Join: + // SELECT ... FROM [tableName] + // DELETE FROM [tableName] + // ... JOIN [tableName] + if r, _ := utf8.DecodeRune(buffer); !unicode.IsLetter(r) { + // first character in buffer is not a letter; we might have a nested + // query like SELECT * FROM (SELECT ...) + break + } + fallthrough + case Update, Into: + // UPDATE [tableName] + // INSERT INTO [tableName] + tableName := string(buffer) + if f.replaceDigits { + tableNameCopy := make([]byte, len(buffer)) + copy(tableNameCopy, buffer) + tableName = string(replaceDigits(tableNameCopy)) + } + f.storeTableName(tableName) + return TableName, buffer, nil + } + } + return token, buffer, nil +} + +func (f *metadataFinderFilter) storeTableName(name string) { + if _, ok := f.tablesSeen[name]; ok { + return + } + if f.tablesSeen == nil { + f.tablesSeen = make(map[string]struct{}, 1) + } + f.tablesSeen[name] = struct{}{} + if f.tablesCSV.Len() > 0 { + f.size++ + f.tablesCSV.WriteByte(',') + } + f.size += int64(len(name)) + f.tablesCSV.WriteString(name) +} + +// Results returns metadata collected by the filter for an SQL statement. +func (f *metadataFinderFilter) Results() SQLMetadata { + return SQLMetadata{ + Size: f.size, + TablesCSV: f.tablesCSV.String(), + Commands: f.commands, + Comments: f.comments, + } +} + +// Reset implements tokenFilter. +func (f *metadataFinderFilter) Reset() { + for k := range f.tablesSeen { + delete(f.tablesSeen, k) + } + f.size = 0 + f.tablesCSV.Reset() + f.commands = f.commands[:0] + f.comments = f.comments[:0] +} + // discardFilter is a token filter which discards certain elements from a query, such as // comments and AS aliases by returning a nil buffer. -type discardFilter struct{ keepSQLAlias bool } +type discardFilter struct { + keepSQLAlias bool +} // Filter the given token so that a `nil` slice is returned if the token is in the token filtered list. func (f *discardFilter) Filter(token, lastToken TokenKind, buffer []byte) (TokenKind, []byte, error) { @@ -114,8 +216,8 @@ type groupingFilter struct { // Filter the given token so that it will be discarded if a grouping pattern // has been recognized. A grouping is composed by items like: -// * '( ?, ?, ? )' -// * '( ?, ? ), ( ?, ? )' +// - '( ?, ?, ? )' +// - '( ?, ? ), ( ?, ? )' func (f *groupingFilter) Filter(token, lastToken TokenKind, buffer []byte) (tokenType TokenKind, tokenBytes []byte, err error) { // increasing the number of groups means that we're filtering an entire group // because it can be represented with a single '( ? )' @@ -123,8 +225,11 @@ func (f *groupingFilter) Filter(token, lastToken TokenKind, buffer []byte) (toke f.groupMulti++ } + // Potential commands that could indicate the start of a subquery. + isStartOfSubquery := token == Select || token == Delete || token == Update || token == ID + switch { - case f.groupMulti > 0 && lastToken == FilteredGroupableParenthesis && token == ID: + case f.groupMulti > 0 && lastToken == FilteredGroupableParenthesis && isStartOfSubquery: // this is the start of a new group that seems to be a nested query; // cancel grouping. f.Reset() @@ -220,90 +325,35 @@ func (o *Obfuscator) obfuscateSQLString(in string, opts *SQLConfig) (*Obfuscated return out, err } -// tableFinderFilter is a filter which attempts to identify the table name as it goes through each -// token in a query. -type tableFinderFilter struct { - storeTableNames bool - // seen keeps track of unique table names encountered by the filter. - seen map[string]struct{} - // csv specifies a comma-separated list of tables - csv strings.Builder -} - -// Filter implements tokenFilter. -func (f *tableFinderFilter) Filter(token, lastToken TokenKind, buffer []byte) (TokenKind, []byte, error) { - switch lastToken { - case From, Join: - // SELECT ... FROM [tableName] - // DELETE FROM [tableName] - // ... JOIN [tableName] - if r, _ := utf8.DecodeRune(buffer); !unicode.IsLetter(r) { - // first character in buffer is not a letter; we might have a nested - // query like SELECT * FROM (SELECT ...) - break - } - fallthrough - case Update, Into: - // UPDATE [tableName] - // INSERT INTO [tableName] - if f.storeTableNames { - f.storeName(string(buffer)) - } - return TableName, buffer, nil - } - return token, buffer, nil -} - -// storeName marks the given table name as seen in the internal storage. -func (f *tableFinderFilter) storeName(name string) { - if _, ok := f.seen[name]; ok { - return - } - if f.seen == nil { - f.seen = make(map[string]struct{}, 1) - } - f.seen[name] = struct{}{} - if f.csv.Len() > 0 { - f.csv.WriteByte(',') - } - f.csv.WriteString(name) -} - -// CSV returns a comma-separated list of the tables seen by the filter. -func (f *tableFinderFilter) CSV() string { return f.csv.String() } - -// Reset implements tokenFilter. -func (f *tableFinderFilter) Reset() { - for k := range f.seen { - delete(f.seen, k) - } - f.csv.Reset() -} - // ObfuscatedQuery specifies information about an obfuscated SQL query. type ObfuscatedQuery struct { - Query string // the obfuscated SQL query - TablesCSV string // comma-separated list of tables that the query addresses + Query string `json:"query"` // the obfuscated SQL query + Metadata SQLMetadata `json:"metadata"` // metadata extracted from the SQL query } // Cost returns the number of bytes needed to store all the fields // of this ObfuscatedQuery. func (oq *ObfuscatedQuery) Cost() int64 { - return int64(len(oq.Query) + len(oq.TablesCSV)) + return int64(len(oq.Query)) + oq.Metadata.Size } // attemptObfuscation attempts to obfuscate the SQL query loaded into the tokenizer, using the given set of filters. func attemptObfuscation(tokenizer *SQLTokenizer) (*ObfuscatedQuery, error) { var ( - storeTableNames = tokenizer.cfg.TableNames - out = bytes.NewBuffer(make([]byte, 0, len(tokenizer.buf))) - err error - lastToken TokenKind - discard = discardFilter{tokenizer.cfg.KeepSQLAlias} - replace = replaceFilter{replaceDigits: tokenizer.cfg.ReplaceDigits} - grouping groupingFilter - tableFinder = tableFinderFilter{storeTableNames: storeTableNames} + out = bytes.NewBuffer(make([]byte, 0, len(tokenizer.buf))) + err error + lastToken TokenKind + metadata = metadataFinderFilter{ + collectTableNames: tokenizer.cfg.TableNames, + collectCommands: tokenizer.cfg.CollectCommands, + collectComments: tokenizer.cfg.CollectComments, + replaceDigits: tokenizer.cfg.ReplaceDigits, + } + discard = discardFilter{keepSQLAlias: tokenizer.cfg.KeepSQLAlias} + replace = replaceFilter{replaceDigits: tokenizer.cfg.ReplaceDigits} + grouping groupingFilter ) + defer metadata.Reset() // call Scan() function until tokens are available or if a LEX_ERROR is raised. After // retrieving a token, send it to the tokenFilter chains so that the token is discarded // or replaced. @@ -316,13 +366,11 @@ func attemptObfuscation(tokenizer *SQLTokenizer) (*ObfuscatedQuery, error) { return nil, fmt.Errorf("%v", tokenizer.Err()) } - if token, buff, err = discard.Filter(token, lastToken, buff); err != nil { + if token, buff, err = metadata.Filter(token, lastToken, buff); err != nil { return nil, err } - if storeTableNames { - if token, buff, err = tableFinder.Filter(token, lastToken, buff); err != nil { - return nil, err - } + if token, buff, err = discard.Filter(token, lastToken, buff); err != nil { + return nil, err } if token, buff, err = replace.Filter(token, lastToken, buff); err != nil { return nil, err @@ -353,8 +401,8 @@ func attemptObfuscation(tokenizer *SQLTokenizer) (*ObfuscatedQuery, error) { return nil, errors.New("result is empty") } return &ObfuscatedQuery{ - Query: out.String(), - TablesCSV: tableFinder.CSV(), + Query: out.String(), + Metadata: metadata.Results(), }, nil } diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go index 3fd34a5ea..f9c1e39bc 100644 --- a/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go +++ b/vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go @@ -8,6 +8,7 @@ package obfuscate import ( "bytes" "fmt" + "strings" "unicode" "unicode/utf8" ) @@ -56,14 +57,36 @@ const ( NE Not As + Alter + Drop + Create + Grant + Revoke + Commit + Begin + Truncate + Select From Update + Delete Insert Into Join TableName ColonCast + // PostgreSQL specific JSON operators + JSONSelect // -> + JSONSelectText // ->> + JSONSelectPath // #> + JSONSelectPathText // #>> + JSONContains // @> + JSONContainsLeft // <@ + JSONKeyExists // ? + JSONAnyKeysExist // ?| + JSONAllKeysExist // ?& + JSONDelete // #- + // FilteredGroupable specifies that the given token has been discarded by one of the // token filters and that it is groupable together with consecutive FilteredGroupable // tokens. @@ -109,8 +132,18 @@ var tokenKindStrings = map[TokenKind]string{ NE: "NE", Not: "NOT", As: "As", + Alter: "Alter", + Drop: "Drop", + Create: "Create", + Grant: "Grant", + Revoke: "Revoke", + Commit: "Commit", + Begin: "Begin", + Truncate: "Truncate", + Select: "Select", From: "From", Update: "Update", + Delete: "Delete", Insert: "Insert", Into: "Into", Join: "Join", @@ -120,6 +153,16 @@ var tokenKindStrings = map[TokenKind]string{ FilteredGroupableParenthesis: "FilteredGroupableParenthesis", Filtered: "Filtered", FilteredBracketedIdentifier: "FilteredBracketedIdentifier", + JSONSelect: "JSONSelect", + JSONSelectText: "JSONSelectText", + JSONSelectPath: "JSONSelectPath", + JSONSelectPathText: "JSONSelectPathText", + JSONContains: "JSONContains", + JSONContainsLeft: "JSONContainsLeft", + JSONKeyExists: "JSONKeyExists", + JSONAnyKeysExist: "JSONAnyKeysExist", + JSONAllKeysExist: "JSONAllKeysExist", + JSONDelete: "JSONDelete", } func (k TokenKind) String() string { @@ -130,6 +173,13 @@ func (k TokenKind) String() string { return str } +const ( + // DBMSSQLServer is a MS SQL Server + DBMSSQLServer = "mssql" + // DBMSPostgres is a PostgreSQL Server + DBMSPostgres = "postgresql" +) + const escapeCharacter = '\\' // SQLTokenizer is the struct used to generate SQL @@ -179,8 +229,18 @@ var keywords = map[string]TokenKind{ "SAVEPOINT": Savepoint, "LIMIT": Limit, "AS": As, + "ALTER": Alter, + "CREATE": Create, + "GRANT": Grant, + "REVOKE": Revoke, + "COMMIT": Commit, + "BEGIN": Begin, + "TRUNCATE": Truncate, + "DROP": Drop, + "SELECT": Select, "FROM": From, "UPDATE": Update, + "DELETE": Delete, "INSERT": Insert, "INTO": Into, "JOIN": Join, @@ -205,10 +265,14 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { if tkn.lastChar == 0 { tkn.advance() } - tkn.skipBlank() + tkn.SkipBlank() switch ch := tkn.lastChar; { - case isLeadingLetter(ch): + case isLeadingLetter(ch) && + !(tkn.cfg.DBMS == DBMSPostgres && ch == '@'): + // The '@' symbol should not be considered part of an identifier in + // postgres, so we skip this in the case where the DBMS is postgres + // and ch is '@'. return tkn.scanIdentifier() case isDigit(ch): return tkn.scanNumber(false) @@ -245,7 +309,26 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { default: return TokenKind(ch), tkn.bytes() } - case '=', ',', ';', '(', ')', '+', '*', '&', '|', '^', '[', ']', '?': + case '?': + if tkn.cfg.DBMS == DBMSPostgres { + switch tkn.lastChar { + case '|': + tkn.advance() + return JSONAnyKeysExist, []byte("?|") + case '&': + tkn.advance() + return JSONAllKeysExist, []byte("?&") + default: + return JSONKeyExists, tkn.bytes() + } + } + fallthrough + case '=', ',', ';', '(', ')', '+', '*', '&', '|', '^', ']': + return TokenKind(ch), tkn.bytes() + case '[': + if tkn.cfg.DBMS == DBMSSQLServer { + return tkn.scanString(']', DoubleQuotedString) + } return TokenKind(ch), tkn.bytes() case '.': if isDigit(tkn.lastChar) { @@ -268,16 +351,56 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { case tkn.lastChar == '-': tkn.advance() return tkn.scanCommentType1("--") + case tkn.lastChar == '>': + if tkn.cfg.DBMS == DBMSPostgres { + tkn.advance() + switch tkn.lastChar { + case '>': + tkn.advance() + return JSONSelectText, []byte("->>") + default: + return JSONSelect, []byte("->") + } + } + fallthrough case isDigit(tkn.lastChar): + return tkn.scanNumber(false) + case tkn.lastChar == '.': tkn.advance() - kind, tokenBytes := tkn.scanNumber(false) - return kind, append([]byte{'-'}, tokenBytes...) + if isDigit(tkn.lastChar) { + return tkn.scanNumber(true) + } + tkn.lastChar = '.' + tkn.pos-- + fallthrough default: return TokenKind(ch), tkn.bytes() } case '#': - tkn.advance() - return tkn.scanCommentType1("#") + switch tkn.cfg.DBMS { + case DBMSSQLServer: + return tkn.scanIdentifier() + case DBMSPostgres: + switch tkn.lastChar { + case '>': + tkn.advance() + switch tkn.lastChar { + case '>': + tkn.advance() + return JSONSelectPathText, []byte("#>>") + default: + return JSONSelectPath, []byte("#>") + } + case '-': + tkn.advance() + return JSONDelete, []byte("#-") + default: + return TokenKind(ch), tkn.bytes() + } + default: + tkn.advance() + return tkn.scanCommentType1("#") + } case '<': switch tkn.lastChar { case '>': @@ -292,6 +415,13 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { default: return LE, []byte("<=") } + case '@': + if tkn.cfg.DBMS == DBMSPostgres { + // check for JSONContainsLeft (<@) + tkn.advance() + return JSONContainsLeft, []byte("<@") + } + fallthrough default: return TokenKind(ch), tkn.bytes() } @@ -357,6 +487,21 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { tok = append(append([]byte("$func$"), []byte(out.Query)...), []byte("$func$")...) } return kind, tok + case '@': + if tkn.cfg.DBMS == DBMSPostgres { + // For postgres the @ symbol is reserved as an operator + // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-OPERATORS + // And is used as a json operator + // https://www.postgresql.org/docs/9.5/functions-json.html + switch tkn.lastChar { + case '>': + tkn.advance() + return JSONContains, []byte("@>") + default: + return TokenKind(ch), tkn.bytes() + } + } + fallthrough case '{': if tkn.pos == 1 || tkn.curlys > 0 { // Do not fully obfuscate top-level SQL escape sequences like {{[?=]call procedure-name[([parameter][,parameter]...)]}. @@ -382,7 +527,9 @@ func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) { } } -func (tkn *SQLTokenizer) skipBlank() { +// SkipBlank moves the tokenizer forward until hitting a non-whitespace character +// The whitespace definition used here is the same as unicode.IsSpace +func (tkn *SQLTokenizer) SkipBlank() { for unicode.IsSpace(tkn.lastChar) { tkn.advance() } @@ -431,7 +578,7 @@ func toUpper(src, dst []byte) []byte { func (tkn *SQLTokenizer) scanIdentifier() (TokenKind, []byte) { tkn.advance() - for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || tkn.lastChar == '.' || tkn.lastChar == '*' { + for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || strings.ContainsRune(".*$", tkn.lastChar) { tkn.advance() } @@ -551,8 +698,8 @@ func (tkn *SQLTokenizer) scanBindVar() (TokenKind, []byte) { token = ListArg tkn.advance() } - if !isLetter(tkn.lastChar) { - tkn.setErr(`bind variables should start with letters, got "%c" (%d)`, tkn.lastChar, tkn.lastChar) + if !isLetter(tkn.lastChar) && !isDigit(tkn.lastChar) { + tkn.setErr(`bind variables should start with letters or digits, got "%c" (%d)`, tkn.lastChar, tkn.lastChar) return LexError, tkn.bytes() } for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || tkn.lastChar == '.' { @@ -582,21 +729,13 @@ func (tkn *SQLTokenizer) scanNumber(seenDecimalPoint bool) (TokenKind, []byte) { tkn.scanMantissa(16) } else { // octal int or float - seenDecimalDigit := false tkn.scanMantissa(8) if tkn.lastChar == '8' || tkn.lastChar == '9' { - // illegal octal int or float - seenDecimalDigit = true tkn.scanMantissa(10) } if tkn.lastChar == '.' || tkn.lastChar == 'e' || tkn.lastChar == 'E' { goto fraction } - // octal int - if seenDecimalDigit { - // tkn.setErr called in caller - return LexError, tkn.bytes() - } } goto exit } @@ -622,6 +761,7 @@ exponent: exit: t := tkn.bytes() if len(t) == 0 { + tkn.setErr("Parse error: ended up with zero-length number.") return LexError, nil } return Number, t @@ -732,6 +872,11 @@ func (tkn *SQLTokenizer) bytes() []byte { return ret } +// Position exports the tokenizer's current position in the query +func (tkn *SQLTokenizer) Position() int { + return tkn.pos +} + func isLeadingLetter(ch rune) bool { return unicode.IsLetter(ch) || ch == '_' || ch == '@' } diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE new file mode 100644 index 000000000..b370545be --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE @@ -0,0 +1,200 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-present Datadog, Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/README.md b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/README.md new file mode 100644 index 000000000..a42ffb54e --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/README.md @@ -0,0 +1,5 @@ +# Remote Config Go client + +This package powers the Remote Config client shipped in the Go tracer and in all the agent processes (core-agent, trace-agent, system-probe, ...). + +To add a new product simply add it to `products.go` as a constant and in the `validProducts` set. diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/agent_config.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/agent_config.go new file mode 100644 index 000000000..f6df6a937 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/agent_config.go @@ -0,0 +1,159 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + +package state + +import ( + "encoding/json" + "fmt" + "regexp" + + "github.com/pkg/errors" +) + +const agentConfigOrderID = "configuration_order" + +var datadogConfigIDRegexp = regexp.MustCompile(`^datadog/\d+/AGENT_CONFIG/([^/]+)/[^/]+$`) + +// AgentConfig is a deserialized agent configuration file +// along with the associated metadata +type AgentConfig struct { + Config agentConfigData + Metadata Metadata +} + +// ConfigContent contains the configurations set by remote-config +type ConfigContent struct { + LogLevel string `json:"log_level"` +} + +type agentConfigData struct { + Name string `json:"name"` + Config ConfigContent `json:"config"` +} + +// AgentConfigOrder is a deserialized agent configuration file +// along with the associated metadata +type AgentConfigOrder struct { + Config agentConfigOrderData + Metadata Metadata +} + +type agentConfigOrderData struct { + Order []string `json:"order"` + InternalOrder []string `json:"internal_order"` +} + +// AgentConfigState contains the state of the config in case of fallback or override +type AgentConfigState struct { + FallbackLogLevel string + LatestLogLevel string +} + +// parseConfigAgentConfig parses an agent task config +func parseConfigAgentConfig(data []byte, metadata Metadata) (AgentConfig, error) { + var d agentConfigData + + err := json.Unmarshal(data, &d) + if err != nil { + return AgentConfig{}, fmt.Errorf("Unexpected AGENT_CONFIG received through remote-config: %s", err) + } + + return AgentConfig{ + Config: d, + Metadata: metadata, + }, nil +} + +// parseConfigAgentConfig parses an agent task config +func parseConfigAgentConfigOrder(data []byte, metadata Metadata) (AgentConfigOrder, error) { + var d agentConfigOrderData + + err := json.Unmarshal(data, &d) + if err != nil { + return AgentConfigOrder{}, fmt.Errorf("Unexpected AGENT_CONFIG received through remote-config: %s", err) + } + + return AgentConfigOrder{ + Config: d, + Metadata: metadata, + }, nil +} + +// MergeRCAgentConfig is the callback function called when there is an AGENT_CONFIG config update +// The RCClient can directly call back listeners, because there would be no way to send back +// RCTE2 configuration applied state to RC backend. +func MergeRCAgentConfig(applyStatus func(cfgPath string, status ApplyStatus), updates map[string]RawConfig) (ConfigContent, error) { + var orderFile AgentConfigOrder + var hasError bool + var fullErr error + parsedLayers := map[string]AgentConfig{} + + for configPath, c := range updates { + var err error + matched := datadogConfigIDRegexp.FindStringSubmatch(configPath) + if len(matched) != 2 { + err = fmt.Errorf("config file path '%s' has wrong format", configPath) + hasError = true + fullErr = errors.Wrap(fullErr, err.Error()) + applyStatus(configPath, ApplyStatus{ + State: ApplyStateError, + Error: err.Error(), + }) + // If a layer is wrong, fail later to parse the rest and check them all + continue + } + + parsedConfigID := matched[1] + + // Ignore the configuration order file + if parsedConfigID == agentConfigOrderID { + orderFile, err = parseConfigAgentConfigOrder(c.Config, c.Metadata) + if err != nil { + hasError = true + fullErr = errors.Wrap(fullErr, err.Error()) + applyStatus(configPath, ApplyStatus{ + State: ApplyStateError, + Error: err.Error(), + }) + // If a layer is wrong, fail later to parse the rest and check them all + continue + } + } else { + cfg, err := parseConfigAgentConfig(c.Config, c.Metadata) + if err != nil { + hasError = true + applyStatus(configPath, ApplyStatus{ + State: ApplyStateError, + Error: err.Error(), + }) + // If a layer is wrong, fail later to parse the rest and check them all + continue + } + parsedLayers[parsedConfigID] = cfg + } + } + + // If there was at least one error, don't apply any config + if hasError || (len(orderFile.Config.Order) == 0 && len(orderFile.Config.InternalOrder) == 0) { + return ConfigContent{}, fullErr + } + + // Go through all the layers that were sent, and apply them one by one to the merged structure + mergedConfig := ConfigContent{} + for i := len(orderFile.Config.Order) - 1; i >= 0; i-- { + if layer, found := parsedLayers[orderFile.Config.Order[i]]; found { + mergedConfig.LogLevel = layer.Config.Config.LogLevel + } + } + // Same for internal config + for i := len(orderFile.Config.InternalOrder) - 1; i >= 0; i-- { + if layer, found := parsedLayers[orderFile.Config.InternalOrder[i]]; found { + mergedConfig.LogLevel = layer.Config.Config.LogLevel + } + } + + return mergedConfig, nil +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go new file mode 100644 index 000000000..06fdb2730 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go @@ -0,0 +1,123 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/DataDog/go-tuf/data" +) + +// ErrNoConfigVersion occurs when a target file's custom meta is missing the config version +var ErrNoConfigVersion = errors.New("version missing in custom file meta") + +func parseConfig(product string, raw []byte, metadata Metadata) (interface{}, error) { + if _, validProduct := validProducts[product]; !validProduct { + return nil, fmt.Errorf("unknown product: %s", product) + } + + switch product { + // ASM products are parsed directly in this client + case ProductASMFeatures: + return parseASMFeaturesConfig(raw, metadata) + case ProductASMDD: + return parseConfigASMDD(raw, metadata) + case ProductASMData: + return parseConfigASMData(raw, metadata) + // case ProductAgentTask: + // return ParseConfigAgentTask(raw, metadata) + // Other products are parsed separately + default: + return RawConfig{ + Config: raw, + Metadata: metadata, + }, nil + } +} + +// RawConfig holds a config that will be parsed separately +type RawConfig struct { + Config []byte + Metadata Metadata +} + +// GetConfigs returns the current configs of a given product +func (r *Repository) GetConfigs(product string) map[string]RawConfig { + typedConfigs := make(map[string]RawConfig) + configs := r.getConfigs(product) + + for path, conf := range configs { + // We control this, so if this has gone wrong something has gone horribly wrong + typed, ok := conf.(RawConfig) + if !ok { + panic("unexpected config stored as RawConfig") + } + + typedConfigs[path] = typed + } + + return typedConfigs +} + +// Metadata stores remote config metadata for a given configuration +type Metadata struct { + Product string + ID string + Name string + Version uint64 + RawLength uint64 + Hashes map[string][]byte + ApplyStatus ApplyStatus +} + +func newConfigMetadata(parsedPath configPath, tfm data.TargetFileMeta) (Metadata, error) { + var m Metadata + m.ID = parsedPath.ConfigID + m.Product = parsedPath.Product + m.Name = parsedPath.Name + m.RawLength = uint64(tfm.Length) + m.Hashes = make(map[string][]byte) + for k, v := range tfm.Hashes { + m.Hashes[k] = []byte(v) + } + v, err := fileMetaVersion(tfm) + if err != nil { + return Metadata{}, err + } + m.Version = v + + return m, nil +} + +type fileMetaCustom struct { + Version *uint64 `json:"v"` +} + +func fileMetaVersion(fm data.TargetFileMeta) (uint64, error) { + if fm.Custom == nil { + return 0, ErrNoConfigVersion + } + fmc, err := parseFileMetaCustom(*fm.Custom) + if err != nil { + return 0, err + } + + return *fmc.Version, nil +} + +func parseFileMetaCustom(rawCustom []byte) (fileMetaCustom, error) { + var custom fileMetaCustom + err := json.Unmarshal(rawCustom, &custom) + if err != nil { + return fileMetaCustom{}, err + } + if custom.Version == nil { + return fileMetaCustom{}, ErrNoConfigVersion + } + return custom, nil +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_agent_task.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_agent_task.go new file mode 100644 index 000000000..618bf7335 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_agent_task.go @@ -0,0 +1,59 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + +package state + +import ( + "encoding/json" + "fmt" +) + +// AgentTaskConfig is a deserialized agent task configuration file +// along with the associated metadata +type AgentTaskConfig struct { + Config AgentTaskData + Metadata Metadata +} + +// AgentTaskData is the content of a agent task configuration file +type AgentTaskData struct { + TaskType string `json:"task_type"` + UUID string `json:"uuid"` + TaskArgs map[string]string `json:"args"` +} + +// ParseConfigAgentTask parses an agent task config +func ParseConfigAgentTask(data []byte, metadata Metadata) (AgentTaskConfig, error) { + var d AgentTaskData + + err := json.Unmarshal(data, &d) + if err != nil { + return AgentTaskConfig{}, fmt.Errorf("Unexpected AGENT_TASK received through remote-config: %s", err) + } + + return AgentTaskConfig{ + Config: d, + Metadata: metadata, + }, nil +} + +// AgentTaskConfigs returns the currently active AGENT_TASK configs +func (r *Repository) AgentTaskConfigs() map[string]AgentTaskConfig { + typedConfigs := make(map[string]AgentTaskConfig) + + configs := r.getConfigs(ProductAgentTask) + + for path, conf := range configs { + // We control this, so if this has gone wrong something has gone horribly wrong + typed, ok := conf.(AgentTaskConfig) + if !ok { + panic("unexpected config stored as AgentTaskConfigs") + } + + typedConfigs[path] = typed + } + + return typedConfigs +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_asm.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_asm.go new file mode 100644 index 000000000..ac7a918e8 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs_asm.go @@ -0,0 +1,159 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +import ( + "encoding/json" +) + +// ConfigASMDD is a deserialized ASM DD configuration file along with its +// associated remote config metadata +type ConfigASMDD struct { + Config []byte + Metadata Metadata +} + +func parseConfigASMDD(data []byte, metadata Metadata) (ConfigASMDD, error) { + return ConfigASMDD{ + Config: data, + Metadata: metadata, + }, nil +} + +// ASMDDConfigs returns the currently active ASMDD configs +func (r *Repository) ASMDDConfigs() map[string]ConfigASMDD { + typedConfigs := make(map[string]ConfigASMDD) + + configs := r.getConfigs(ProductASMDD) + + for path, conf := range configs { + // We control this, so if this has gone wrong something has gone horribly wrong + typed, ok := conf.(ConfigASMDD) + if !ok { + panic("unexpected config stored as ASMDD Config") + } + + typedConfigs[path] = typed + } + + return typedConfigs +} + +// ASMFeaturesConfig is a deserialized configuration file that indicates whether ASM should be enabled +// within a tracer, along with its associated remote config metadata. +type ASMFeaturesConfig struct { + Config ASMFeaturesData + Metadata Metadata +} + +// ASMFeaturesData describes the enabled state of ASM features +type ASMFeaturesData struct { + ASM struct { + Enabled bool `json:"enabled"` + } `json:"asm"` +} + +func parseASMFeaturesConfig(data []byte, metadata Metadata) (ASMFeaturesConfig, error) { + var f ASMFeaturesData + + err := json.Unmarshal(data, &f) + if err != nil { + return ASMFeaturesConfig{}, nil + } + + return ASMFeaturesConfig{ + Config: f, + Metadata: metadata, + }, nil +} + +// ASMFeaturesConfigs returns the currently active ASMFeatures configs +func (r *Repository) ASMFeaturesConfigs() map[string]ASMFeaturesConfig { + typedConfigs := make(map[string]ASMFeaturesConfig) + + configs := r.getConfigs(ProductASMFeatures) + + for path, conf := range configs { + // We control this, so if this has gone wrong something has gone horribly wrong + typed, ok := conf.(ASMFeaturesConfig) + if !ok { + panic("unexpected config stored as ASMFeaturesConfig") + } + + typedConfigs[path] = typed + } + + return typedConfigs +} + +// ApplyState represents the status of a configuration application by a remote configuration client +// Clients need to either ack the correct application of received configurations, or communicate that +// they haven't applied it yet, or communicate any error that may have happened while doing so +type ApplyState uint64 + +const ( + ApplyStateUnknown ApplyState = iota + ApplyStateUnacknowledged + ApplyStateAcknowledged + ApplyStateError +) + +// ApplyStatus is the processing status for a given configuration. +// It basically represents whether a config was successfully processed and apply, or if an error occurred +type ApplyStatus struct { + State ApplyState + Error string +} + +// ASMDataConfig is a deserialized configuration file that holds rules data that can be used +// by the ASM WAF for specific features (example: ip blocking). +type ASMDataConfig struct { + Config ASMDataRulesData + Metadata Metadata +} + +// ASMDataRulesData is a serializable array of rules data entries +type ASMDataRulesData struct { + RulesData []ASMDataRuleData `json:"rules_data"` +} + +// ASMDataRuleData is an entry in the rules data list held by an ASMData configuration +type ASMDataRuleData struct { + ID string `json:"id"` + Type string `json:"type"` + Data []ASMDataRuleDataEntry `json:"data"` +} + +// ASMDataRuleDataEntry represents a data entry in a rule data file +type ASMDataRuleDataEntry struct { + Expiration int64 `json:"expiration,omitempty"` + Value string `json:"value"` +} + +func parseConfigASMData(data []byte, metadata Metadata) (ASMDataConfig, error) { + cfg := ASMDataConfig{ + Metadata: metadata, + } + err := json.Unmarshal(data, &cfg.Config) + return cfg, err +} + +// ASMDataConfigs returns the currently active ASMData configs +func (r *Repository) ASMDataConfigs() map[string]ASMDataConfig { + typedConfigs := make(map[string]ASMDataConfig) + configs := r.getConfigs(ProductASMData) + + for path, cfg := range configs { + // We control this, so if this has gone wrong something has gone horribly wrong + typed, ok := cfg.(ASMDataConfig) + if !ok { + panic("unexpected config stored as ASMDataConfig") + } + typedConfigs[path] = typed + } + + return typedConfigs +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go new file mode 100644 index 000000000..d1a4d69e2 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go @@ -0,0 +1,100 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +var ( + // matches datadog//// for datadog//// + datadogPathRegexp = regexp.MustCompile(`^datadog/(\d+)/([^/]+)/([^/]+)/([^/]+)$`) + datadogPathRegexpGroups = 4 + + // matches employee/// for employee//// + employeePathRegexp = regexp.MustCompile(`^employee/([^/]+)/([^/]+)/([^/]+)$`) + employeePathRegexpGroups = 3 +) + +type source uint + +const ( + sourceUnknown source = iota + sourceDatadog + sourceEmployee +) + +type configPath struct { + Source source + OrgID int64 + Product string + ConfigID string + Name string +} + +func parseConfigPath(path string) (configPath, error) { + configType := parseConfigPathSource(path) + switch configType { + case sourceDatadog: + return parseDatadogConfigPath(path) + case sourceEmployee: + return parseEmployeeConfigPath(path) + } + return configPath{}, fmt.Errorf("config path '%s' has unknown source", path) +} + +func parseDatadogConfigPath(path string) (configPath, error) { + matchedGroups := datadogPathRegexp.FindStringSubmatch(path) + if len(matchedGroups) != datadogPathRegexpGroups+1 { + return configPath{}, fmt.Errorf("config file path '%s' has wrong format", path) + } + rawOrgID := matchedGroups[1] + orgID, err := strconv.ParseInt(rawOrgID, 10, 64) + if err != nil { + return configPath{}, fmt.Errorf("could not parse orgID '%s' in config file path: %v", rawOrgID, err) + } + rawProduct := matchedGroups[2] + if len(rawProduct) == 0 { + return configPath{}, fmt.Errorf("product is empty") + } + return configPath{ + Source: sourceDatadog, + OrgID: orgID, + Product: rawProduct, + ConfigID: matchedGroups[3], + Name: matchedGroups[4], + }, nil +} + +func parseEmployeeConfigPath(path string) (configPath, error) { + matchedGroups := employeePathRegexp.FindStringSubmatch(path) + if len(matchedGroups) != employeePathRegexpGroups+1 { + return configPath{}, fmt.Errorf("config file path '%s' has wrong format", path) + } + rawProduct := matchedGroups[1] + if len(rawProduct) == 0 { + return configPath{}, fmt.Errorf("product is empty") + } + return configPath{ + Source: sourceEmployee, + Product: rawProduct, + ConfigID: matchedGroups[2], + Name: matchedGroups[3], + }, nil +} + +func parseConfigPathSource(path string) source { + switch { + case strings.HasPrefix(path, "datadog/"): + return sourceDatadog + case strings.HasPrefix(path, "employee/"): + return sourceEmployee + } + return sourceUnknown +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/products.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/products.go new file mode 100644 index 000000000..96f635df6 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/products.go @@ -0,0 +1,45 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +var validProducts = map[string]struct{}{ + ProductAgentConfig: {}, + ProductAgentTask: {}, + ProductAPMSampling: {}, + ProductCWSDD: {}, + ProductCWSCustom: {}, + ProductCWSProfiles: {}, + ProductASM: {}, + ProductASMFeatures: {}, + ProductASMDD: {}, + ProductASMData: {}, + ProductAPMTracing: {}, +} + +const ( + // ProductAgentConfig is to receive agent configurations, like the log level + ProductAgentConfig = "AGENT_CONFIG" + // ProductAgentTask is to receive agent task instruction, like a flare + ProductAgentTask = "AGENT_TASK" + // ProductAPMSampling is the apm sampling product + ProductAPMSampling = "APM_SAMPLING" + // ProductCWSDD is the cloud workload security product managed by datadog employees + ProductCWSDD = "CWS_DD" + // ProductCWSCustom is the cloud workload security product managed by datadog customers + ProductCWSCustom = "CWS_CUSTOM" + // ProductCWSProfiles is the cloud workload security profile product + ProductCWSProfiles = "CWS_SECURITY_PROFILES" + // ProductASM is the ASM product used by customers to issue rules configurations + ProductASM = "ASM" + // ProductASMFeatures is the ASM product used form ASM activation through remote config + ProductASMFeatures = "ASM_FEATURES" + // ProductASMDD is the application security monitoring product managed by datadog employees + ProductASMDD = "ASM_DD" + // ProductASMData is the ASM product used to configure WAF rules data + ProductASMData = "ASM_DATA" + // ProductAPMTracing is the apm tracing product + ProductAPMTracing = "APM_TRACING" +) diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go new file mode 100644 index 000000000..ced2a3db2 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go @@ -0,0 +1,442 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "log" + "strings" + + "github.com/DataDog/go-tuf/data" +) + +var ( + // ErrMalformedEmbeddedRoot occurs when the TUF root provided is invalid + ErrMalformedEmbeddedRoot = errors.New("malformed embedded TUF root file provided") +) + +// RepositoryState contains all of the information about the current config files +// stored by the client to be able to make an update request to an Agent +type RepositoryState struct { + Configs []ConfigState + CachedFiles []CachedFile + TargetsVersion int64 + RootsVersion int64 + OpaqueBackendState []byte +} + +// ConfigState describes an applied config by the agent client. +type ConfigState struct { + Product string + ID string + Version uint64 + ApplyStatus ApplyStatus +} + +// CachedFile describes a cached file stored by the agent client +// +// Note: You may be wondering why this exists when `ConfigState` exists +// as well. The API for requesting updates does not mandate that a client +// cache config files. This implementation just happens to do so. +type CachedFile struct { + Path string + Length uint64 + Hashes map[string][]byte +} + +// An Update contains all the data needed to update a client's remote config repository state +type Update struct { + // TUFRoots contains, in order, updated roots that this repository needs to keep up with TUF validation + TUFRoots [][]byte + // TUFTargets is the latest TUF Targets file and is used to validate raw config files + TUFTargets []byte + // TargetFiles stores the raw config files by their full TUF path + TargetFiles map[string][]byte + // ClientcConfigs is a list of TUF path's corresponding to config files designated for this repository + ClientConfigs []string +} + +// isEmpty returns whether or not all the fields of `Update` are empty +func (u *Update) isEmpty() bool { + return len(u.TUFRoots) == 0 && len(u.TUFTargets) == 0 && (u.TargetFiles == nil || len(u.TargetFiles) == 0) && len(u.ClientConfigs) == 0 +} + +// Repository is a remote config client used in a downstream process to retrieve +// remote config updates from an Agent. +type Repository struct { + // TUF related data + latestTargets *data.Targets + tufRootsClient *tufRootsClient + opaqueBackendState []byte + + // Unverified mode + tufVerificationEnabled bool + latestRootVersion int64 + + // Config file storage + metadata map[string]Metadata + configs map[string]map[string]interface{} +} + +// NewRepository creates a new remote config repository that will track +// both TUF metadata and raw config files for a client. +func NewRepository(embeddedRoot []byte) (*Repository, error) { + if embeddedRoot == nil { + return nil, ErrMalformedEmbeddedRoot + } + + configs := make(map[string]map[string]interface{}) + for product := range validProducts { + configs[product] = make(map[string]interface{}) + } + + tufRootsClient, err := newTufRootsClient(embeddedRoot) + if err != nil { + return nil, err + } + + return &Repository{ + latestTargets: data.NewTargets(), + tufRootsClient: tufRootsClient, + metadata: make(map[string]Metadata), + configs: configs, + tufVerificationEnabled: true, + }, nil +} + +// NewUnverifiedRepository creates a new remote config repository that will +// track config files for a client WITHOUT verifying any TUF related metadata. +// +// When creating this we pretend we have a root version of 1, as the backend expects +// to not have to send the initial "embedded" root. +func NewUnverifiedRepository() (*Repository, error) { + configs := make(map[string]map[string]interface{}) + for product := range validProducts { + configs[product] = make(map[string]interface{}) + } + + return &Repository{ + latestTargets: data.NewTargets(), + metadata: make(map[string]Metadata), + configs: configs, + tufVerificationEnabled: false, + latestRootVersion: 1, // The backend expects us to start with a root version of 1. + }, nil +} + +// Update processes the ClientGetConfigsResponse from the Agent and updates the +// configuration state +func (r *Repository) Update(update Update) ([]string, error) { + var err error + var updatedTargets *data.Targets + var tmpRootClient *tufRootsClient + + // If there's literally nothing in the update, it's not an error. + if update.isEmpty() { + return []string{}, nil + } + + // TUF: Update the roots and verify the TUF Targets file (optional) + // + // We don't want to partially update the state, so we need a temporary client to hold the new root + // data until we know it's valid. Since verification is optional, if the repository was configured + // to not do TUF verification we only deserialize the TUF targets file. + if r.tufVerificationEnabled { + tmpRootClient, err = r.tufRootsClient.clone() + if err != nil { + return nil, err + } + err = tmpRootClient.updateRoots(update.TUFRoots) + if err != nil { + return nil, err + } + + updatedTargets, err = tmpRootClient.validateTargets(update.TUFTargets) + if err != nil { + return nil, err + } + } else { + updatedTargets, err = unsafeUnmarshalTargets(update.TUFTargets) + if err != nil { + return nil, err + } + } + + clientConfigsMap := make(map[string]struct{}) + for _, f := range update.ClientConfigs { + clientConfigsMap[f] = struct{}{} + } + + result := newUpdateResult() + + // 2: Check the config list and mark any missing configs as "to be removed" + for _, configs := range r.configs { + for path := range configs { + if _, ok := clientConfigsMap[path]; !ok { + result.removed = append(result.removed, path) + parsedPath, err := parseConfigPath(path) + if err != nil { + return nil, err + } + result.productsUpdated[parsedPath.Product] = true + } + } + } + + // 3: For all the files referenced in this update + for _, path := range update.ClientConfigs { + targetFileMetadata, ok := updatedTargets.Targets[path] + if !ok { + return nil, fmt.Errorf("missing config file in TUF targets - %s", path) + } + + // 3.a: Extract the product and ID from the path + parsedPath, err := parseConfigPath(path) + if err != nil { + return nil, err + } + + // 3.b and 3.c: Check if this configuration is either new or has been modified + storedMetadata, exists := r.metadata[path] + if exists && hashesEqual(targetFileMetadata.Hashes, storedMetadata.Hashes) { + continue + } + + // 3.d: Ensure that the raw configuration file is present in the + // update payload. + raw, ok := update.TargetFiles[path] + if !ok { + return nil, fmt.Errorf("missing update file - %s", path) + } + + // TUF: Validate the hash of the raw target file and ensure that it matches + // the TUF metadata + err = validateTargetFileHash(targetFileMetadata, raw) + if err != nil { + return nil, fmt.Errorf("error validating %s hash with TUF metadata - %v", path, err) + } + + // 3.e: Deserialize the configuration. + // 3.f: Store the update details for application later + // + // Note: We don't have to worry about extra fields as mentioned + // in the RFC because the encoding/json library handles that for us. + m, err := newConfigMetadata(parsedPath, targetFileMetadata) + if err != nil { + return nil, err + } + config, err := parseConfig(parsedPath.Product, raw, m) + if err != nil { + return nil, err + } + result.metadata[path] = m + result.changed[parsedPath.Product][path] = config + result.productsUpdated[parsedPath.Product] = true + } + + // 4.a: Store the new targets.signed.custom.opaque_client_state + // TUF: Store the updated roots now that everything has validated + if r.tufVerificationEnabled { + r.tufRootsClient = tmpRootClient + } else if update.TUFRoots != nil && len(update.TUFRoots) > 0 { + v, err := extractRootVersion(update.TUFRoots[len(update.TUFRoots)-1]) + if err != nil { + return nil, err + } + r.latestRootVersion = v + } + r.latestTargets = updatedTargets + if r.latestTargets.Custom != nil { + r.opaqueBackendState = extractOpaqueBackendState(*r.latestTargets.Custom) + } + + // Upstream may not want to take any actions if the update result doesn't + // change any configs. + if result.isEmpty() { + return nil, nil + } + + changedProducts := make([]string, 0) + for product, updated := range result.productsUpdated { + if updated { + changedProducts = append(changedProducts, product) + } + } + + // 4.b/4.rave the new state and apply cleanups + r.applyUpdateResult(update, result) + + return changedProducts, nil +} + +// UpdateApplyStatus updates the config's metadata to reflect its processing state +// Can be used after a call to Update() in order to tell the repository which config was acked, which +// wasn't and which errors occurred while processing. +// Note: it is the responsibility of the caller to ensure that no new Update() call was made between +// the first Update() call and the call to UpdateApplyStatus() so as to keep the repository state accurate. +func (r *Repository) UpdateApplyStatus(cfgPath string, status ApplyStatus) { + if m, ok := r.metadata[cfgPath]; ok { + m.ApplyStatus = status + r.metadata[cfgPath] = m + } +} + +func (r *Repository) getConfigs(product string) map[string]interface{} { + configs, ok := r.configs[product] + if !ok { + return nil + } + + return configs +} + +// applyUpdateResult changes the state of the client based on the given update. +// +// The update is guaranteed to succeed at this point, having been vetted and the details +// needed to apply the update stored in the `updateResult`. +func (r *Repository) applyUpdateResult(update Update, result updateResult) { + // 4.b Save all the updated and new config files + for product, configs := range result.changed { + for path, config := range configs { + m := r.configs[product] + m[path] = config + } + } + for path, metadata := range result.metadata { + r.metadata[path] = metadata + } + + // 5.b Clean up the cache of any removed configs + for _, path := range result.removed { + delete(r.metadata, path) + for _, configs := range r.configs { + delete(configs, path) + } + } +} + +// CurrentState returns all of the information needed to +// make an update for new configurations. +func (r *Repository) CurrentState() (RepositoryState, error) { + var configs []ConfigState + var cached []CachedFile + + for path, metadata := range r.metadata { + configs = append(configs, configStateFromMetadata(metadata)) + cached = append(cached, cachedFileFromMetadata(path, metadata)) + } + + var latestRootVersion int64 + if r.tufVerificationEnabled { + root, err := r.tufRootsClient.latestRoot() + if err != nil { + return RepositoryState{}, err + } + latestRootVersion = root.Version + } else { + latestRootVersion = r.latestRootVersion + } + + return RepositoryState{ + Configs: configs, + CachedFiles: cached, + TargetsVersion: r.latestTargets.Version, + RootsVersion: latestRootVersion, + OpaqueBackendState: r.opaqueBackendState, + }, nil +} + +// An updateResult allows the client to apply the update as a transaction +// after validating all required preconditions +type updateResult struct { + removed []string + metadata map[string]Metadata + changed map[string]map[string]interface{} + productsUpdated map[string]bool +} + +func newUpdateResult() updateResult { + changed := make(map[string]map[string]interface{}) + + for product := range validProducts { + changed[product] = make(map[string]interface{}) + } + + return updateResult{ + removed: make([]string, 0), + metadata: make(map[string]Metadata), + changed: changed, + productsUpdated: map[string]bool{}, + } +} + +func (ur updateResult) Log() { + log.Printf("Removed Configs: %v", ur.removed) + + var b strings.Builder + b.WriteString("Changed configs: [") + for path := range ur.metadata { + b.WriteString(path) + b.WriteString(" ") + } + b.WriteString("]") + + log.Println(b.String()) +} + +func (ur updateResult) isEmpty() bool { + return len(ur.removed) == 0 && len(ur.metadata) == 0 +} + +func configStateFromMetadata(m Metadata) ConfigState { + return ConfigState{ + Product: m.Product, + ID: m.ID, + Version: m.Version, + ApplyStatus: m.ApplyStatus, + } +} + +func cachedFileFromMetadata(path string, m Metadata) CachedFile { + return CachedFile{ + Path: path, + Length: m.RawLength, + Hashes: m.Hashes, + } +} + +// hashesEqual checks if the hash values in the TUF metadata file match the stored +// hash values for a given config +func hashesEqual(tufHashes data.Hashes, storedHashes map[string][]byte) bool { + for algorithm, value := range tufHashes { + v, ok := storedHashes[algorithm] + if !ok { + continue + } + + if !bytes.Equal(value, v) { + return false + } + } + + return true +} + +func extractOpaqueBackendState(targetsCustom []byte) []byte { + state := struct { + State []byte `json:"opaque_backend_state"` + }{nil} + + err := json.Unmarshal(targetsCustom, &state) + if err != nil { + return []byte{} + } + + return state.State +} diff --git a/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go new file mode 100644 index 000000000..f67ab9c19 --- /dev/null +++ b/vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go @@ -0,0 +1,233 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022-present Datadog, Inc. + +package state + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + + "github.com/DataDog/go-tuf/client" + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/util" + "github.com/DataDog/go-tuf/verify" +) + +type tufRootsClient struct { + rootClient *client.Client + rootLocalStore client.LocalStore + rootRemoteStore *rootClientRemoteStore +} + +func newTufRootsClient(root []byte) (*tufRootsClient, error) { + rootLocalStore := client.MemoryLocalStore() + rootRemoteStore := &rootClientRemoteStore{} + rootClient := client.NewClient(rootLocalStore, rootRemoteStore) + + err := rootClient.Init(root) + if err != nil { + return nil, err + } + + return &tufRootsClient{ + rootClient: rootClient, + rootLocalStore: rootLocalStore, + rootRemoteStore: rootRemoteStore, + }, nil +} + +func (trc *tufRootsClient) clone() (*tufRootsClient, error) { + root, err := trc.latestRootRaw() + if err != nil { + return nil, err + } + + return newTufRootsClient(root) +} + +func (trc *tufRootsClient) updateRoots(newRoots [][]byte) error { + if len(newRoots) == 0 { + return nil + } + + trc.rootRemoteStore.roots = append(trc.rootRemoteStore.roots, newRoots...) + + return trc.rootClient.UpdateRoots() +} + +func (trc *tufRootsClient) latestRoot() (*data.Root, error) { + raw, err := trc.latestRootRaw() + if err != nil { + return nil, err + } + + return unsafeUnmarshalRoot(raw) +} + +func (trc *tufRootsClient) latestRootRaw() ([]byte, error) { + metas, err := trc.rootLocalStore.GetMeta() + if err != nil { + return nil, err + } + rawRoot := metas["root.json"] + + return rawRoot, nil +} + +func (trc *tufRootsClient) validateTargets(rawTargets []byte) (*data.Targets, error) { + root, err := trc.latestRoot() + if err != nil { + return nil, err + } + + db := verify.NewDB() + for _, key := range root.Keys { + for _, id := range key.IDs() { + if err := db.AddKey(id, key); err != nil { + return nil, err + } + } + } + targetsRole, hasRoleTargets := root.Roles["targets"] + if !hasRoleTargets { + return nil, fmt.Errorf("root is missing a targets role") + } + role := &data.Role{Threshold: targetsRole.Threshold, KeyIDs: targetsRole.KeyIDs} + if err := db.AddRole("targets", role); err != nil { + return nil, fmt.Errorf("could not add targets role to db: %v", err) + } + var targets data.Targets + err = db.Unmarshal(rawTargets, &targets, "targets", 0) + if err != nil { + return nil, err + } + + return &targets, nil +} + +type rootClientRemoteStore struct { + roots [][]byte +} + +func (s *rootClientRemoteStore) GetMeta(name string) (stream io.ReadCloser, size int64, err error) { + metaPath, err := parseMetaPath(name) + if err != nil { + return nil, 0, err + } + if metaPath.role != roleRoot || !metaPath.versionSet { + return nil, 0, client.ErrNotFound{File: name} + } + for _, root := range s.roots { + parsedRoot, err := unsafeUnmarshalRoot(root) + if err != nil { + return nil, 0, err + } + if parsedRoot.Version == metaPath.version { + return io.NopCloser(bytes.NewReader(root)), int64(len(root)), nil + } + } + return nil, 0, client.ErrNotFound{File: name} +} + +func (s *rootClientRemoteStore) GetTarget(path string) (stream io.ReadCloser, size int64, err error) { + return nil, 0, client.ErrNotFound{File: path} +} + +type role string + +const ( + roleRoot role = "root" +) + +type metaPath struct { + role role + version int64 + versionSet bool +} + +func parseMetaPath(rawMetaPath string) (metaPath, error) { + splitRawMetaPath := strings.SplitN(rawMetaPath, ".", 3) + if len(splitRawMetaPath) != 2 && len(splitRawMetaPath) != 3 { + return metaPath{}, fmt.Errorf("invalid metadata path '%s'", rawMetaPath) + } + suffix := splitRawMetaPath[len(splitRawMetaPath)-1] + if suffix != "json" { + return metaPath{}, fmt.Errorf("invalid metadata path (suffix) '%s'", rawMetaPath) + } + rawRole := splitRawMetaPath[len(splitRawMetaPath)-2] + if rawRole == "" { + return metaPath{}, fmt.Errorf("invalid metadata path (role) '%s'", rawMetaPath) + } + if len(splitRawMetaPath) == 2 { + return metaPath{ + role: role(rawRole), + }, nil + } + rawVersion, err := strconv.ParseInt(splitRawMetaPath[0], 10, 64) + if err != nil { + return metaPath{}, fmt.Errorf("invalid metadata path (version) '%s': %w", rawMetaPath, err) + } + return metaPath{ + role: role(rawRole), + version: rawVersion, + versionSet: true, + }, nil +} + +func validateTargetFileHash(targetMeta data.TargetFileMeta, targetFile []byte) error { + if len(targetMeta.HashAlgorithms()) == 0 { + return fmt.Errorf("target file has no hash") + } + generatedMeta, err := util.GenerateFileMeta(bytes.NewBuffer(targetFile), targetMeta.HashAlgorithms()...) + if err != nil { + return err + } + err = util.FileMetaEqual(targetMeta.FileMeta, generatedMeta) + if err != nil { + return err + } + return nil +} + +func unsafeUnmarshalRoot(raw []byte) (*data.Root, error) { + var signedRoot data.Signed + err := json.Unmarshal(raw, &signedRoot) + if err != nil { + return nil, err + } + var root data.Root + err = json.Unmarshal(signedRoot.Signed, &root) + if err != nil { + return nil, err + } + return &root, err +} + +func unsafeUnmarshalTargets(raw []byte) (*data.Targets, error) { + var signedTargets data.Signed + err := json.Unmarshal(raw, &signedTargets) + if err != nil { + return nil, err + } + var targets data.Targets + err = json.Unmarshal(signedTargets.Signed, &targets) + if err != nil { + return nil, err + } + return &targets, err +} + +func extractRootVersion(raw []byte) (int64, error) { + root, err := unsafeUnmarshalRoot(raw) + if err != nil { + return 0, err + } + + return root.Version, nil +} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/README.md b/vendor/github.com/DataDog/datadog-go/statsd/README.md deleted file mode 100644 index 2fc899687..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## Overview - -Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags -and histograms. diff --git a/vendor/github.com/DataDog/datadog-go/statsd/aggregator.go b/vendor/github.com/DataDog/datadog-go/statsd/aggregator.go deleted file mode 100644 index c4446a23b..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/aggregator.go +++ /dev/null @@ -1,283 +0,0 @@ -package statsd - -import ( - "strings" - "sync" - "sync/atomic" - "time" -) - -type ( - countsMap map[string]*countMetric - gaugesMap map[string]*gaugeMetric - setsMap map[string]*setMetric - bufferedMetricMap map[string]*bufferedMetric -) - -type aggregator struct { - nbContextGauge int32 - nbContextCount int32 - nbContextSet int32 - - countsM sync.RWMutex - gaugesM sync.RWMutex - setsM sync.RWMutex - - gauges gaugesMap - counts countsMap - sets setsMap - histograms bufferedMetricContexts - distributions bufferedMetricContexts - timings bufferedMetricContexts - - closed chan struct{} - - client *Client - - // aggregator implements ChannelMode mechanism to receive histograms, - // distributions and timings. Since they need sampling they need to - // lock for random. When using both ChannelMode and ExtendedAggregation - // we don't want goroutine to fight over the lock. - inputMetrics chan metric - stopChannelMode chan struct{} - wg sync.WaitGroup -} - -type aggregatorMetrics struct { - nbContext int32 - nbContextGauge int32 - nbContextCount int32 - nbContextSet int32 - nbContextHistogram int32 - nbContextDistribution int32 - nbContextTiming int32 -} - -func newAggregator(c *Client) *aggregator { - return &aggregator{ - client: c, - counts: countsMap{}, - gauges: gaugesMap{}, - sets: setsMap{}, - histograms: newBufferedContexts(newHistogramMetric), - distributions: newBufferedContexts(newDistributionMetric), - timings: newBufferedContexts(newTimingMetric), - closed: make(chan struct{}), - stopChannelMode: make(chan struct{}), - } -} - -func (a *aggregator) start(flushInterval time.Duration) { - ticker := time.NewTicker(flushInterval) - - go func() { - for { - select { - case <-ticker.C: - a.flush() - case <-a.closed: - return - } - } - }() -} - -func (a *aggregator) startReceivingMetric(bufferSize int, nbWorkers int) { - a.inputMetrics = make(chan metric, bufferSize) - for i := 0; i < nbWorkers; i++ { - a.wg.Add(1) - go a.pullMetric() - } -} - -func (a *aggregator) stopReceivingMetric() { - close(a.stopChannelMode) - a.wg.Wait() -} - -func (a *aggregator) stop() { - a.closed <- struct{}{} -} - -func (a *aggregator) pullMetric() { - for { - select { - case m := <-a.inputMetrics: - switch m.metricType { - case histogram: - a.histogram(m.name, m.fvalue, m.tags, m.rate) - case distribution: - a.distribution(m.name, m.fvalue, m.tags, m.rate) - case timing: - a.timing(m.name, m.fvalue, m.tags, m.rate) - } - case <-a.stopChannelMode: - a.wg.Done() - return - } - } -} - -func (a *aggregator) flush() { - for _, m := range a.flushMetrics() { - a.client.sendBlocking(m) - } -} - -func (a *aggregator) flushTelemetryMetrics() *aggregatorMetrics { - if a == nil { - return nil - } - - am := &aggregatorMetrics{ - nbContextGauge: atomic.SwapInt32(&a.nbContextGauge, 0), - nbContextCount: atomic.SwapInt32(&a.nbContextCount, 0), - nbContextSet: atomic.SwapInt32(&a.nbContextSet, 0), - nbContextHistogram: a.histograms.resetAndGetNbContext(), - nbContextDistribution: a.distributions.resetAndGetNbContext(), - nbContextTiming: a.timings.resetAndGetNbContext(), - } - - am.nbContext = am.nbContextGauge + am.nbContextCount + am.nbContextSet + am.nbContextHistogram + am.nbContextDistribution + am.nbContextTiming - return am -} - -func (a *aggregator) flushMetrics() []metric { - metrics := []metric{} - - // We reset the values to avoid sending 'zero' values for metrics not - // sampled during this flush interval - - a.setsM.Lock() - sets := a.sets - a.sets = setsMap{} - a.setsM.Unlock() - - for _, s := range sets { - metrics = append(metrics, s.flushUnsafe()...) - } - - a.gaugesM.Lock() - gauges := a.gauges - a.gauges = gaugesMap{} - a.gaugesM.Unlock() - - for _, g := range gauges { - metrics = append(metrics, g.flushUnsafe()) - } - - a.countsM.Lock() - counts := a.counts - a.counts = countsMap{} - a.countsM.Unlock() - - for _, c := range counts { - metrics = append(metrics, c.flushUnsafe()) - } - - metrics = a.histograms.flush(metrics) - metrics = a.distributions.flush(metrics) - metrics = a.timings.flush(metrics) - - atomic.AddInt32(&a.nbContextCount, int32(len(counts))) - atomic.AddInt32(&a.nbContextGauge, int32(len(gauges))) - atomic.AddInt32(&a.nbContextSet, int32(len(sets))) - return metrics -} - -func getContext(name string, tags []string) string { - return name + ":" + strings.Join(tags, tagSeparatorSymbol) -} - -func getContextAndTags(name string, tags []string) (string, string) { - stringTags := strings.Join(tags, tagSeparatorSymbol) - return name + ":" + stringTags, stringTags -} - -func (a *aggregator) count(name string, value int64, tags []string) error { - context := getContext(name, tags) - a.countsM.RLock() - if count, found := a.counts[context]; found { - count.sample(value) - a.countsM.RUnlock() - return nil - } - a.countsM.RUnlock() - - a.countsM.Lock() - // Check if another goroutines hasn't created the value betwen the RUnlock and 'Lock' - if count, found := a.counts[context]; found { - count.sample(value) - a.countsM.Unlock() - return nil - } - - a.counts[context] = newCountMetric(name, value, tags) - a.countsM.Unlock() - return nil -} - -func (a *aggregator) gauge(name string, value float64, tags []string) error { - context := getContext(name, tags) - a.gaugesM.RLock() - if gauge, found := a.gauges[context]; found { - gauge.sample(value) - a.gaugesM.RUnlock() - return nil - } - a.gaugesM.RUnlock() - - gauge := newGaugeMetric(name, value, tags) - - a.gaugesM.Lock() - // Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock' - if gauge, found := a.gauges[context]; found { - gauge.sample(value) - a.gaugesM.Unlock() - return nil - } - a.gauges[context] = gauge - a.gaugesM.Unlock() - return nil -} - -func (a *aggregator) set(name string, value string, tags []string) error { - context := getContext(name, tags) - a.setsM.RLock() - if set, found := a.sets[context]; found { - set.sample(value) - a.setsM.RUnlock() - return nil - } - a.setsM.RUnlock() - - a.setsM.Lock() - // Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock' - if set, found := a.sets[context]; found { - set.sample(value) - a.setsM.Unlock() - return nil - } - a.sets[context] = newSetMetric(name, value, tags) - a.setsM.Unlock() - return nil -} - -// Only histograms, distributions and timings are sampled with a rate since we -// only pack them in on message instead of aggregating them. Discarding the -// sample rate will have impacts on the CPU and memory usage of the Agent. - -// type alias for Client.sendToAggregator -type bufferedMetricSampleFunc func(name string, value float64, tags []string, rate float64) error - -func (a *aggregator) histogram(name string, value float64, tags []string, rate float64) error { - return a.histograms.sample(name, value, tags, rate) -} - -func (a *aggregator) distribution(name string, value float64, tags []string, rate float64) error { - return a.distributions.sample(name, value, tags, rate) -} - -func (a *aggregator) timing(name string, value float64, tags []string, rate float64) error { - return a.timings.sample(name, value, tags, rate) -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/buffer.go b/vendor/github.com/DataDog/datadog-go/statsd/buffer.go deleted file mode 100644 index 34dc0d3fb..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/buffer.go +++ /dev/null @@ -1,190 +0,0 @@ -package statsd - -import ( - "strconv" -) - -type bufferFullError string - -func (e bufferFullError) Error() string { return string(e) } - -const errBufferFull = bufferFullError("statsd buffer is full") - -type partialWriteError string - -func (e partialWriteError) Error() string { return string(e) } - -const errPartialWrite = partialWriteError("value partially written") - -const metricOverhead = 512 - -// statsdBuffer is a buffer containing statsd messages -// this struct methods are NOT safe for concurent use -type statsdBuffer struct { - buffer []byte - maxSize int - maxElements int - elementCount int -} - -func newStatsdBuffer(maxSize, maxElements int) *statsdBuffer { - return &statsdBuffer{ - buffer: make([]byte, 0, maxSize+metricOverhead), // pre-allocate the needed size + metricOverhead to avoid having Go re-allocate on it's own if an element does not fit - maxSize: maxSize, - maxElements: maxElements, - } -} - -func (b *statsdBuffer) writeGauge(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendGauge(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeCount(namespace string, globalTags []string, name string, value int64, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendCount(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeHistogram(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendHistogram(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -// writeAggregated serialized as many values as possible in the current buffer and return the position in values where it stopped. -func (b *statsdBuffer) writeAggregated(metricSymbol []byte, namespace string, globalTags []string, name string, values []float64, tags string, tagSize int, precision int) (int, error) { - if b.elementCount >= b.maxElements { - return 0, errBufferFull - } - - originalBuffer := b.buffer - b.buffer = appendHeader(b.buffer, namespace, name) - - // buffer already full - if len(b.buffer)+tagSize > b.maxSize { - b.buffer = originalBuffer - return 0, errBufferFull - } - - // We add as many value as possible - var position int - for idx, v := range values { - previousBuffer := b.buffer - if idx != 0 { - b.buffer = append(b.buffer, ':') - } - - b.buffer = strconv.AppendFloat(b.buffer, v, 'f', precision, 64) - - // Should we stop serializing and switch to another buffer - if len(b.buffer)+tagSize > b.maxSize { - b.buffer = previousBuffer - break - } - position = idx + 1 - } - - // we could not add a single value - if position == 0 { - b.buffer = originalBuffer - return 0, errBufferFull - } - - b.buffer = append(b.buffer, '|') - b.buffer = append(b.buffer, metricSymbol...) - b.buffer = appendTagsAggregated(b.buffer, globalTags, tags) - b.writeSeparator() - b.elementCount++ - - if position != len(values) { - return position, errPartialWrite - } - return position, nil - -} - -func (b *statsdBuffer) writeDistribution(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendDistribution(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeSet(namespace string, globalTags []string, name string, value string, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendSet(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeTiming(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendTiming(b.buffer, namespace, globalTags, name, value, tags, rate) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeEvent(event Event, globalTags []string) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendEvent(b.buffer, event, globalTags) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) writeServiceCheck(serviceCheck ServiceCheck, globalTags []string) error { - if b.elementCount >= b.maxElements { - return errBufferFull - } - originalBuffer := b.buffer - b.buffer = appendServiceCheck(b.buffer, serviceCheck, globalTags) - b.writeSeparator() - return b.validateNewElement(originalBuffer) -} - -func (b *statsdBuffer) validateNewElement(originalBuffer []byte) error { - if len(b.buffer) > b.maxSize { - b.buffer = originalBuffer - return errBufferFull - } - b.elementCount++ - return nil -} - -func (b *statsdBuffer) writeSeparator() { - b.buffer = append(b.buffer, '\n') -} - -func (b *statsdBuffer) reset() { - b.buffer = b.buffer[:0] - b.elementCount = 0 -} - -func (b *statsdBuffer) bytes() []byte { - return b.buffer -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go b/vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go deleted file mode 100644 index 7a3e3c9d2..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go +++ /dev/null @@ -1,40 +0,0 @@ -package statsd - -type bufferPool struct { - pool chan *statsdBuffer - bufferMaxSize int - bufferMaxElements int -} - -func newBufferPool(poolSize, bufferMaxSize, bufferMaxElements int) *bufferPool { - p := &bufferPool{ - pool: make(chan *statsdBuffer, poolSize), - bufferMaxSize: bufferMaxSize, - bufferMaxElements: bufferMaxElements, - } - for i := 0; i < poolSize; i++ { - p.addNewBuffer() - } - return p -} - -func (p *bufferPool) addNewBuffer() { - p.pool <- newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements) -} - -func (p *bufferPool) borrowBuffer() *statsdBuffer { - select { - case b := <-p.pool: - return b - default: - return newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements) - } -} - -func (p *bufferPool) returnBuffer(buffer *statsdBuffer) { - buffer.reset() - select { - case p.pool <- buffer: - default: - } -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go b/vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go deleted file mode 100644 index 15bba2861..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go +++ /dev/null @@ -1,82 +0,0 @@ -package statsd - -import ( - "math/rand" - "sync" - "sync/atomic" - "time" -) - -// bufferedMetricContexts represent the contexts for Histograms, Distributions -// and Timing. Since those 3 metric types behave the same way and are sampled -// with the same type they're represented by the same class. -type bufferedMetricContexts struct { - nbContext int32 - mutex sync.RWMutex - values bufferedMetricMap - newMetric func(string, float64, string) *bufferedMetric - - // Each bufferedMetricContexts uses its own random source and random - // lock to prevent goroutines from contending for the lock on the - // "math/rand" package-global random source (e.g. calls like - // "rand.Float64()" must acquire a shared lock to get the next - // pseudorandom number). - random *rand.Rand - randomLock sync.Mutex -} - -func newBufferedContexts(newMetric func(string, float64, string) *bufferedMetric) bufferedMetricContexts { - return bufferedMetricContexts{ - values: bufferedMetricMap{}, - newMetric: newMetric, - // Note that calling "time.Now().UnixNano()" repeatedly quickly may return - // very similar values. That's fine for seeding the worker-specific random - // source because we just need an evenly distributed stream of float values. - // Do not use this random source for cryptographic randomness. - random: rand.New(rand.NewSource(time.Now().UnixNano())), - } -} - -func (bc *bufferedMetricContexts) flush(metrics []metric) []metric { - bc.mutex.Lock() - values := bc.values - bc.values = bufferedMetricMap{} - bc.mutex.Unlock() - - for _, d := range values { - metrics = append(metrics, d.flushUnsafe()) - } - atomic.AddInt32(&bc.nbContext, int32(len(values))) - return metrics -} - -func (bc *bufferedMetricContexts) sample(name string, value float64, tags []string, rate float64) error { - if !shouldSample(rate, bc.random, &bc.randomLock) { - return nil - } - - context, stringTags := getContextAndTags(name, tags) - - bc.mutex.RLock() - if v, found := bc.values[context]; found { - v.sample(value) - bc.mutex.RUnlock() - return nil - } - bc.mutex.RUnlock() - - bc.mutex.Lock() - // Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock' - if v, found := bc.values[context]; found { - v.sample(value) - bc.mutex.Unlock() - return nil - } - bc.values[context] = bc.newMetric(name, value, stringTags) - bc.mutex.Unlock() - return nil -} - -func (bc *bufferedMetricContexts) resetAndGetNbContext() int32 { - return atomic.SwapInt32(&bc.nbContext, 0) -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/event.go b/vendor/github.com/DataDog/datadog-go/statsd/event.go deleted file mode 100644 index cf966d1a8..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/event.go +++ /dev/null @@ -1,91 +0,0 @@ -package statsd - -import ( - "fmt" - "time" -) - -// Events support -// EventAlertType and EventAlertPriority became exported types after this issue was submitted: https://github.com/DataDog/datadog-go/issues/41 -// The reason why they got exported is so that client code can directly use the types. - -// EventAlertType is the alert type for events -type EventAlertType string - -const ( - // Info is the "info" AlertType for events - Info EventAlertType = "info" - // Error is the "error" AlertType for events - Error EventAlertType = "error" - // Warning is the "warning" AlertType for events - Warning EventAlertType = "warning" - // Success is the "success" AlertType for events - Success EventAlertType = "success" -) - -// EventPriority is the event priority for events -type EventPriority string - -const ( - // Normal is the "normal" Priority for events - Normal EventPriority = "normal" - // Low is the "low" Priority for events - Low EventPriority = "low" -) - -// An Event is an object that can be posted to your DataDog event stream. -type Event struct { - // Title of the event. Required. - Title string - // Text is the description of the event. Required. - Text string - // Timestamp is a timestamp for the event. If not provided, the dogstatsd - // server will set this to the current time. - Timestamp time.Time - // Hostname for the event. - Hostname string - // AggregationKey groups this event with others of the same key. - AggregationKey string - // Priority of the event. Can be statsd.Low or statsd.Normal. - Priority EventPriority - // SourceTypeName is a source type for the event. - SourceTypeName string - // AlertType can be statsd.Info, statsd.Error, statsd.Warning, or statsd.Success. - // If absent, the default value applied by the dogstatsd server is Info. - AlertType EventAlertType - // Tags for the event. - Tags []string -} - -// NewEvent creates a new event with the given title and text. Error checking -// against these values is done at send-time, or upon running e.Check. -func NewEvent(title, text string) *Event { - return &Event{ - Title: title, - Text: text, - } -} - -// Check verifies that an event is valid. -func (e Event) Check() error { - if len(e.Title) == 0 { - return fmt.Errorf("statsd.Event title is required") - } - if len(e.Text) == 0 { - return fmt.Errorf("statsd.Event text is required") - } - return nil -} - -// Encode returns the dogstatsd wire protocol representation for an event. -// Tags may be passed which will be added to the encoded output but not to -// the Event's list of tags, eg. for default tags. -func (e Event) Encode(tags ...string) (string, error) { - err := e.Check() - if err != nil { - return "", err - } - var buffer []byte - buffer = appendEvent(buffer, e, tags) - return string(buffer), nil -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go b/vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go deleted file mode 100644 index 03dc8a07c..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go +++ /dev/null @@ -1,39 +0,0 @@ -package statsd - -const ( - // FNV-1a - offset32 = uint32(2166136261) - prime32 = uint32(16777619) - - // init32 is what 32 bits hash values should be initialized with. - init32 = offset32 -) - -// HashString32 returns the hash of s. -func hashString32(s string) uint32 { - return addString32(init32, s) -} - -// AddString32 adds the hash of s to the precomputed hash value h. -func addString32(h uint32, s string) uint32 { - i := 0 - n := (len(s) / 8) * 8 - - for i != n { - h = (h ^ uint32(s[i])) * prime32 - h = (h ^ uint32(s[i+1])) * prime32 - h = (h ^ uint32(s[i+2])) * prime32 - h = (h ^ uint32(s[i+3])) * prime32 - h = (h ^ uint32(s[i+4])) * prime32 - h = (h ^ uint32(s[i+5])) * prime32 - h = (h ^ uint32(s[i+6])) * prime32 - h = (h ^ uint32(s[i+7])) * prime32 - i += 8 - } - - for _, c := range s[i:] { - h = (h ^ uint32(c)) * prime32 - } - - return h -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/format.go b/vendor/github.com/DataDog/datadog-go/statsd/format.go deleted file mode 100644 index 8d62aa7ba..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/format.go +++ /dev/null @@ -1,257 +0,0 @@ -package statsd - -import ( - "strconv" - "strings" -) - -var ( - gaugeSymbol = []byte("g") - countSymbol = []byte("c") - histogramSymbol = []byte("h") - distributionSymbol = []byte("d") - setSymbol = []byte("s") - timingSymbol = []byte("ms") - tagSeparatorSymbol = "," -) - -func appendHeader(buffer []byte, namespace string, name string) []byte { - if namespace != "" { - buffer = append(buffer, namespace...) - } - buffer = append(buffer, name...) - buffer = append(buffer, ':') - return buffer -} - -func appendRate(buffer []byte, rate float64) []byte { - if rate < 1 { - buffer = append(buffer, "|@"...) - buffer = strconv.AppendFloat(buffer, rate, 'f', -1, 64) - } - return buffer -} - -func appendWithoutNewlines(buffer []byte, s string) []byte { - // fastpath for strings without newlines - if strings.IndexByte(s, '\n') == -1 { - return append(buffer, s...) - } - - for _, b := range []byte(s) { - if b != '\n' { - buffer = append(buffer, b) - } - } - return buffer -} - -func appendTags(buffer []byte, globalTags []string, tags []string) []byte { - if len(globalTags) == 0 && len(tags) == 0 { - return buffer - } - buffer = append(buffer, "|#"...) - firstTag := true - - for _, tag := range globalTags { - if !firstTag { - buffer = append(buffer, tagSeparatorSymbol...) - } - buffer = appendWithoutNewlines(buffer, tag) - firstTag = false - } - for _, tag := range tags { - if !firstTag { - buffer = append(buffer, tagSeparatorSymbol...) - } - buffer = appendWithoutNewlines(buffer, tag) - firstTag = false - } - return buffer -} - -func appendTagsAggregated(buffer []byte, globalTags []string, tags string) []byte { - if len(globalTags) == 0 && tags == "" { - return buffer - } - - buffer = append(buffer, "|#"...) - firstTag := true - - for _, tag := range globalTags { - if !firstTag { - buffer = append(buffer, tagSeparatorSymbol...) - } - buffer = appendWithoutNewlines(buffer, tag) - firstTag = false - } - if tags != "" { - if !firstTag { - buffer = append(buffer, tagSeparatorSymbol...) - } - buffer = appendWithoutNewlines(buffer, tags) - } - return buffer -} - -func appendFloatMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64, precision int) []byte { - buffer = appendHeader(buffer, namespace, name) - buffer = strconv.AppendFloat(buffer, value, 'f', precision, 64) - buffer = append(buffer, '|') - buffer = append(buffer, typeSymbol...) - buffer = appendRate(buffer, rate) - buffer = appendTags(buffer, globalTags, tags) - return buffer -} - -func appendIntegerMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte { - buffer = appendHeader(buffer, namespace, name) - buffer = strconv.AppendInt(buffer, value, 10) - buffer = append(buffer, '|') - buffer = append(buffer, typeSymbol...) - buffer = appendRate(buffer, rate) - buffer = appendTags(buffer, globalTags, tags) - return buffer -} - -func appendStringMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte { - buffer = appendHeader(buffer, namespace, name) - buffer = append(buffer, value...) - buffer = append(buffer, '|') - buffer = append(buffer, typeSymbol...) - buffer = appendRate(buffer, rate) - buffer = appendTags(buffer, globalTags, tags) - return buffer -} - -func appendGauge(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { - return appendFloatMetric(buffer, gaugeSymbol, namespace, globalTags, name, value, tags, rate, -1) -} - -func appendCount(buffer []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte { - return appendIntegerMetric(buffer, countSymbol, namespace, globalTags, name, value, tags, rate) -} - -func appendHistogram(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { - return appendFloatMetric(buffer, histogramSymbol, namespace, globalTags, name, value, tags, rate, -1) -} - -func appendDistribution(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { - return appendFloatMetric(buffer, distributionSymbol, namespace, globalTags, name, value, tags, rate, -1) -} - -func appendSet(buffer []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte { - return appendStringMetric(buffer, setSymbol, namespace, globalTags, name, value, tags, rate) -} - -func appendTiming(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { - return appendFloatMetric(buffer, timingSymbol, namespace, globalTags, name, value, tags, rate, 6) -} - -func escapedEventTextLen(text string) int { - return len(text) + strings.Count(text, "\n") -} - -func appendEscapedEventText(buffer []byte, text string) []byte { - for _, b := range []byte(text) { - if b != '\n' { - buffer = append(buffer, b) - } else { - buffer = append(buffer, "\\n"...) - } - } - return buffer -} - -func appendEvent(buffer []byte, event Event, globalTags []string) []byte { - escapedTextLen := escapedEventTextLen(event.Text) - - buffer = append(buffer, "_e{"...) - buffer = strconv.AppendInt(buffer, int64(len(event.Title)), 10) - buffer = append(buffer, tagSeparatorSymbol...) - buffer = strconv.AppendInt(buffer, int64(escapedTextLen), 10) - buffer = append(buffer, "}:"...) - buffer = append(buffer, event.Title...) - buffer = append(buffer, '|') - if escapedTextLen != len(event.Text) { - buffer = appendEscapedEventText(buffer, event.Text) - } else { - buffer = append(buffer, event.Text...) - } - - if !event.Timestamp.IsZero() { - buffer = append(buffer, "|d:"...) - buffer = strconv.AppendInt(buffer, int64(event.Timestamp.Unix()), 10) - } - - if len(event.Hostname) != 0 { - buffer = append(buffer, "|h:"...) - buffer = append(buffer, event.Hostname...) - } - - if len(event.AggregationKey) != 0 { - buffer = append(buffer, "|k:"...) - buffer = append(buffer, event.AggregationKey...) - } - - if len(event.Priority) != 0 { - buffer = append(buffer, "|p:"...) - buffer = append(buffer, event.Priority...) - } - - if len(event.SourceTypeName) != 0 { - buffer = append(buffer, "|s:"...) - buffer = append(buffer, event.SourceTypeName...) - } - - if len(event.AlertType) != 0 { - buffer = append(buffer, "|t:"...) - buffer = append(buffer, string(event.AlertType)...) - } - - buffer = appendTags(buffer, globalTags, event.Tags) - return buffer -} - -func appendEscapedServiceCheckText(buffer []byte, text string) []byte { - for i := 0; i < len(text); i++ { - if text[i] == '\n' { - buffer = append(buffer, "\\n"...) - } else if text[i] == 'm' && i+1 < len(text) && text[i+1] == ':' { - buffer = append(buffer, "m\\:"...) - i++ - } else { - buffer = append(buffer, text[i]) - } - } - return buffer -} - -func appendServiceCheck(buffer []byte, serviceCheck ServiceCheck, globalTags []string) []byte { - buffer = append(buffer, "_sc|"...) - buffer = append(buffer, serviceCheck.Name...) - buffer = append(buffer, '|') - buffer = strconv.AppendInt(buffer, int64(serviceCheck.Status), 10) - - if !serviceCheck.Timestamp.IsZero() { - buffer = append(buffer, "|d:"...) - buffer = strconv.AppendInt(buffer, int64(serviceCheck.Timestamp.Unix()), 10) - } - - if len(serviceCheck.Hostname) != 0 { - buffer = append(buffer, "|h:"...) - buffer = append(buffer, serviceCheck.Hostname...) - } - - buffer = appendTags(buffer, globalTags, serviceCheck.Tags) - - if len(serviceCheck.Message) != 0 { - buffer = append(buffer, "|m:"...) - buffer = appendEscapedServiceCheckText(buffer, serviceCheck.Message) - } - return buffer -} - -func appendSeparator(buffer []byte) []byte { - return append(buffer, '\n') -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/metrics.go b/vendor/github.com/DataDog/datadog-go/statsd/metrics.go deleted file mode 100644 index 99ed4da53..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/metrics.go +++ /dev/null @@ -1,181 +0,0 @@ -package statsd - -import ( - "math" - "sync" - "sync/atomic" -) - -/* -Those are metrics type that can be aggregated on the client side: - - Gauge - - Count - - Set -*/ - -type countMetric struct { - value int64 - name string - tags []string -} - -func newCountMetric(name string, value int64, tags []string) *countMetric { - return &countMetric{ - value: value, - name: name, - tags: tags, - } -} - -func (c *countMetric) sample(v int64) { - atomic.AddInt64(&c.value, v) -} - -func (c *countMetric) flushUnsafe() metric { - return metric{ - metricType: count, - name: c.name, - tags: c.tags, - rate: 1, - ivalue: c.value, - } -} - -// Gauge - -type gaugeMetric struct { - value uint64 - name string - tags []string -} - -func newGaugeMetric(name string, value float64, tags []string) *gaugeMetric { - return &gaugeMetric{ - value: math.Float64bits(value), - name: name, - tags: tags, - } -} - -func (g *gaugeMetric) sample(v float64) { - atomic.StoreUint64(&g.value, math.Float64bits(v)) -} - -func (g *gaugeMetric) flushUnsafe() metric { - return metric{ - metricType: gauge, - name: g.name, - tags: g.tags, - rate: 1, - fvalue: math.Float64frombits(g.value), - } -} - -// Set - -type setMetric struct { - data map[string]struct{} - name string - tags []string - sync.Mutex -} - -func newSetMetric(name string, value string, tags []string) *setMetric { - set := &setMetric{ - data: map[string]struct{}{}, - name: name, - tags: tags, - } - set.data[value] = struct{}{} - return set -} - -func (s *setMetric) sample(v string) { - s.Lock() - defer s.Unlock() - s.data[v] = struct{}{} -} - -// Sets are aggregated on the agent side too. We flush the keys so a set from -// multiple application can be correctly aggregated on the agent side. -func (s *setMetric) flushUnsafe() []metric { - if len(s.data) == 0 { - return nil - } - - metrics := make([]metric, len(s.data)) - i := 0 - for value := range s.data { - metrics[i] = metric{ - metricType: set, - name: s.name, - tags: s.tags, - rate: 1, - svalue: value, - } - i++ - } - return metrics -} - -// Histograms, Distributions and Timings - -type bufferedMetric struct { - sync.Mutex - - data []float64 - name string - // Histograms and Distributions store tags as one string since we need - // to compute its size multiple time when serializing. - tags string - mtype metricType -} - -func (s *bufferedMetric) sample(v float64) { - s.Lock() - defer s.Unlock() - s.data = append(s.data, v) -} - -func (s *bufferedMetric) flushUnsafe() metric { - return metric{ - metricType: s.mtype, - name: s.name, - stags: s.tags, - rate: 1, - fvalues: s.data, - } -} - -type histogramMetric = bufferedMetric - -func newHistogramMetric(name string, value float64, stringTags string) *histogramMetric { - return &histogramMetric{ - data: []float64{value}, - name: name, - tags: stringTags, - mtype: histogramAggregated, - } -} - -type distributionMetric = bufferedMetric - -func newDistributionMetric(name string, value float64, stringTags string) *distributionMetric { - return &distributionMetric{ - data: []float64{value}, - name: name, - tags: stringTags, - mtype: distributionAggregated, - } -} - -type timingMetric = bufferedMetric - -func newTimingMetric(name string, value float64, stringTags string) *timingMetric { - return &timingMetric{ - data: []float64{value}, - name: name, - tags: stringTags, - mtype: timingAggregated, - } -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/noop.go b/vendor/github.com/DataDog/datadog-go/statsd/noop.go deleted file mode 100644 index 010783333..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/noop.go +++ /dev/null @@ -1,91 +0,0 @@ -package statsd - -import "time" - -// NoOpClient is a statsd client that does nothing. Can be useful in testing -// situations for library users. -type NoOpClient struct{} - -// Gauge does nothing and returns nil -func (n *NoOpClient) Gauge(name string, value float64, tags []string, rate float64) error { - return nil -} - -// Count does nothing and returns nil -func (n *NoOpClient) Count(name string, value int64, tags []string, rate float64) error { - return nil -} - -// Histogram does nothing and returns nil -func (n *NoOpClient) Histogram(name string, value float64, tags []string, rate float64) error { - return nil -} - -// Distribution does nothing and returns nil -func (n *NoOpClient) Distribution(name string, value float64, tags []string, rate float64) error { - return nil -} - -// Decr does nothing and returns nil -func (n *NoOpClient) Decr(name string, tags []string, rate float64) error { - return nil -} - -// Incr does nothing and returns nil -func (n *NoOpClient) Incr(name string, tags []string, rate float64) error { - return nil -} - -// Set does nothing and returns nil -func (n *NoOpClient) Set(name string, value string, tags []string, rate float64) error { - return nil -} - -// Timing does nothing and returns nil -func (n *NoOpClient) Timing(name string, value time.Duration, tags []string, rate float64) error { - return nil -} - -// TimeInMilliseconds does nothing and returns nil -func (n *NoOpClient) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error { - return nil -} - -// Event does nothing and returns nil -func (n *NoOpClient) Event(e *Event) error { - return nil -} - -// SimpleEvent does nothing and returns nil -func (n *NoOpClient) SimpleEvent(title, text string) error { - return nil -} - -// ServiceCheck does nothing and returns nil -func (n *NoOpClient) ServiceCheck(sc *ServiceCheck) error { - return nil -} - -// SimpleServiceCheck does nothing and returns nil -func (n *NoOpClient) SimpleServiceCheck(name string, status ServiceCheckStatus) error { - return nil -} - -// Close does nothing and returns nil -func (n *NoOpClient) Close() error { - return nil -} - -// Flush does nothing and returns nil -func (n *NoOpClient) Flush() error { - return nil -} - -// SetWriteTimeout does nothing and returns nil -func (n *NoOpClient) SetWriteTimeout(d time.Duration) error { - return nil -} - -// Verify that NoOpClient implements the ClientInterface. -// https://golang.org/doc/faq#guarantee_satisfies_interface -var _ ClientInterface = &NoOpClient{} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/options.go b/vendor/github.com/DataDog/datadog-go/statsd/options.go deleted file mode 100644 index 9db27039c..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/options.go +++ /dev/null @@ -1,323 +0,0 @@ -package statsd - -import ( - "fmt" - "math" - "strings" - "time" -) - -var ( - // DefaultNamespace is the default value for the Namespace option - DefaultNamespace = "" - // DefaultTags is the default value for the Tags option - DefaultTags = []string{} - // DefaultMaxBytesPerPayload is the default value for the MaxBytesPerPayload option - DefaultMaxBytesPerPayload = 0 - // DefaultMaxMessagesPerPayload is the default value for the MaxMessagesPerPayload option - DefaultMaxMessagesPerPayload = math.MaxInt32 - // DefaultBufferPoolSize is the default value for the DefaultBufferPoolSize option - DefaultBufferPoolSize = 0 - // DefaultBufferFlushInterval is the default value for the BufferFlushInterval option - DefaultBufferFlushInterval = 100 * time.Millisecond - // DefaultBufferShardCount is the default value for the BufferShardCount option - DefaultBufferShardCount = 32 - // DefaultSenderQueueSize is the default value for the DefaultSenderQueueSize option - DefaultSenderQueueSize = 0 - // DefaultWriteTimeoutUDS is the default value for the WriteTimeoutUDS option - DefaultWriteTimeoutUDS = 100 * time.Millisecond - // DefaultTelemetry is the default value for the Telemetry option - DefaultTelemetry = true - // DefaultReceivingMode is the default behavior when sending metrics - DefaultReceivingMode = MutexMode - // DefaultChannelModeBufferSize is the default size of the channel holding incoming metrics - DefaultChannelModeBufferSize = 4096 - // DefaultAggregationFlushInterval is the default interval for the aggregator to flush metrics. - // This should divide the Agent reporting period (default=10s) evenly to reduce "aliasing" that - // can cause values to appear irregular. - DefaultAggregationFlushInterval = 2 * time.Second - // DefaultAggregation - DefaultAggregation = false - // DefaultExtendedAggregation - DefaultExtendedAggregation = false - // DefaultDevMode - DefaultDevMode = false -) - -// Options contains the configuration options for a client. -type Options struct { - // Namespace to prepend to all metrics, events and service checks name. - Namespace string - // Tags are global tags to be applied to every metrics, events and service checks. - Tags []string - // MaxBytesPerPayload is the maximum number of bytes a single payload will contain. - // The magic value 0 will set the option to the optimal size for the transport - // protocol used when creating the client: 1432 for UDP and 8192 for UDS. - MaxBytesPerPayload int - // MaxMessagesPerPayload is the maximum number of metrics, events and/or service checks a single payload will contain. - // This option can be set to `1` to create an unbuffered client. - MaxMessagesPerPayload int - // BufferPoolSize is the size of the pool of buffers in number of buffers. - // The magic value 0 will set the option to the optimal size for the transport - // protocol used when creating the client: 2048 for UDP and 512 for UDS. - BufferPoolSize int - // BufferFlushInterval is the interval after which the current buffer will get flushed. - BufferFlushInterval time.Duration - // BufferShardCount is the number of buffer "shards" that will be used. - // Those shards allows the use of multiple buffers at the same time to reduce - // lock contention. - BufferShardCount int - // SenderQueueSize is the size of the sender queue in number of buffers. - // The magic value 0 will set the option to the optimal size for the transport - // protocol used when creating the client: 2048 for UDP and 512 for UDS. - SenderQueueSize int - // WriteTimeoutUDS is the timeout after which a UDS packet is dropped. - WriteTimeoutUDS time.Duration - // Telemetry is a set of metrics automatically injected by the client in the - // dogstatsd stream to be able to monitor the client itself. - Telemetry bool - // ReceiveMode determins the behavior of the client when receiving to many - // metrics. The client will either drop the metrics if its buffers are - // full (ChannelMode mode) or block the caller until the metric can be - // handled (MutexMode mode). By default the client will MutexMode. This - // option should be set to ChannelMode only when use under very high - // load. - // - // MutexMode uses a mutex internally which is much faster than - // channel but causes some lock contention when used with a high number - // of threads. Mutex are sharded based on the metrics name which - // limit mutex contention when goroutines send different metrics. - // - // ChannelMode: uses channel (of ChannelModeBufferSize size) to send - // metrics and drop metrics if the channel is full. Sending metrics in - // this mode is slower that MutexMode (because of the channel), but - // will not block the application. This mode is made for application - // using many goroutines, sending the same metrics at a very high - // volume. The goal is to not slow down the application at the cost of - // dropping metrics and having a lower max throughput. - ReceiveMode ReceivingMode - // ChannelModeBufferSize is the size of the channel holding incoming metrics - ChannelModeBufferSize int - // AggregationFlushInterval is the interval for the aggregator to flush metrics - AggregationFlushInterval time.Duration - // [beta] Aggregation enables/disables client side aggregation for - // Gauges, Counts and Sets (compatible with every Agent's version). - Aggregation bool - // [beta] Extended aggregation enables/disables client side aggregation - // for all types. This feature is only compatible with Agent's versions - // >=7.25.0 or Agent's version >=6.25.0 && < 7.0.0. - ExtendedAggregation bool - // TelemetryAddr specify a different endpoint for telemetry metrics. - TelemetryAddr string - // DevMode enables the "dev" mode where the client sends much more - // telemetry metrics to help troubleshooting the client behavior. - DevMode bool -} - -func resolveOptions(options []Option) (*Options, error) { - o := &Options{ - Namespace: DefaultNamespace, - Tags: DefaultTags, - MaxBytesPerPayload: DefaultMaxBytesPerPayload, - MaxMessagesPerPayload: DefaultMaxMessagesPerPayload, - BufferPoolSize: DefaultBufferPoolSize, - BufferFlushInterval: DefaultBufferFlushInterval, - BufferShardCount: DefaultBufferShardCount, - SenderQueueSize: DefaultSenderQueueSize, - WriteTimeoutUDS: DefaultWriteTimeoutUDS, - Telemetry: DefaultTelemetry, - ReceiveMode: DefaultReceivingMode, - ChannelModeBufferSize: DefaultChannelModeBufferSize, - AggregationFlushInterval: DefaultAggregationFlushInterval, - Aggregation: DefaultAggregation, - ExtendedAggregation: DefaultExtendedAggregation, - DevMode: DefaultDevMode, - } - - for _, option := range options { - err := option(o) - if err != nil { - return nil, err - } - } - - return o, nil -} - -// Option is a client option. Can return an error if validation fails. -type Option func(*Options) error - -// WithNamespace sets the Namespace option. -func WithNamespace(namespace string) Option { - return func(o *Options) error { - if strings.HasSuffix(namespace, ".") { - o.Namespace = namespace - } else { - o.Namespace = namespace + "." - } - return nil - } -} - -// WithTags sets the Tags option. -func WithTags(tags []string) Option { - return func(o *Options) error { - o.Tags = tags - return nil - } -} - -// WithMaxMessagesPerPayload sets the MaxMessagesPerPayload option. -func WithMaxMessagesPerPayload(maxMessagesPerPayload int) Option { - return func(o *Options) error { - o.MaxMessagesPerPayload = maxMessagesPerPayload - return nil - } -} - -// WithMaxBytesPerPayload sets the MaxBytesPerPayload option. -func WithMaxBytesPerPayload(MaxBytesPerPayload int) Option { - return func(o *Options) error { - o.MaxBytesPerPayload = MaxBytesPerPayload - return nil - } -} - -// WithBufferPoolSize sets the BufferPoolSize option. -func WithBufferPoolSize(bufferPoolSize int) Option { - return func(o *Options) error { - o.BufferPoolSize = bufferPoolSize - return nil - } -} - -// WithBufferFlushInterval sets the BufferFlushInterval option. -func WithBufferFlushInterval(bufferFlushInterval time.Duration) Option { - return func(o *Options) error { - o.BufferFlushInterval = bufferFlushInterval - return nil - } -} - -// WithBufferShardCount sets the BufferShardCount option. -func WithBufferShardCount(bufferShardCount int) Option { - return func(o *Options) error { - if bufferShardCount < 1 { - return fmt.Errorf("BufferShardCount must be a positive integer") - } - o.BufferShardCount = bufferShardCount - return nil - } -} - -// WithSenderQueueSize sets the SenderQueueSize option. -func WithSenderQueueSize(senderQueueSize int) Option { - return func(o *Options) error { - o.SenderQueueSize = senderQueueSize - return nil - } -} - -// WithWriteTimeoutUDS sets the WriteTimeoutUDS option. -func WithWriteTimeoutUDS(writeTimeoutUDS time.Duration) Option { - return func(o *Options) error { - o.WriteTimeoutUDS = writeTimeoutUDS - return nil - } -} - -// WithoutTelemetry disables the telemetry -func WithoutTelemetry() Option { - return func(o *Options) error { - o.Telemetry = false - return nil - } -} - -// WithChannelMode will use channel to receive metrics -func WithChannelMode() Option { - return func(o *Options) error { - o.ReceiveMode = ChannelMode - return nil - } -} - -// WithMutexMode will use mutex to receive metrics -func WithMutexMode() Option { - return func(o *Options) error { - o.ReceiveMode = MutexMode - return nil - } -} - -// WithChannelModeBufferSize the channel buffer size when using "drop mode" -func WithChannelModeBufferSize(bufferSize int) Option { - return func(o *Options) error { - o.ChannelModeBufferSize = bufferSize - return nil - } -} - -// WithAggregationInterval set the aggregation interval -func WithAggregationInterval(interval time.Duration) Option { - return func(o *Options) error { - o.AggregationFlushInterval = interval - return nil - } -} - -// WithClientSideAggregation enables client side aggregation for Gauges, Counts -// and Sets. Client side aggregation is a beta feature. -func WithClientSideAggregation() Option { - return func(o *Options) error { - o.Aggregation = true - return nil - } -} - -// WithoutClientSideAggregation disables client side aggregation. -func WithoutClientSideAggregation() Option { - return func(o *Options) error { - o.Aggregation = false - o.ExtendedAggregation = false - return nil - } -} - -// WithExtendedClientSideAggregation enables client side aggregation for all -// types. This feature is only compatible with Agent's version >=6.25.0 && -// <7.0.0 or Agent's versions >=7.25.0. Client side aggregation is a beta -// feature. -func WithExtendedClientSideAggregation() Option { - return func(o *Options) error { - o.Aggregation = true - o.ExtendedAggregation = true - return nil - } -} - -// WithTelemetryAddr specify a different address for telemetry metrics. -func WithTelemetryAddr(addr string) Option { - return func(o *Options) error { - o.TelemetryAddr = addr - return nil - } -} - -// WithDevMode enables client "dev" mode, sending more Telemetry metrics to -// help troubleshoot client behavior. -func WithDevMode() Option { - return func(o *Options) error { - o.DevMode = true - return nil - } -} - -// WithoutDevMode disables client "dev" mode, sending more Telemetry metrics to -// help troubleshoot client behavior. -func WithoutDevMode() Option { - return func(o *Options) error { - o.DevMode = false - return nil - } -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/pipe.go b/vendor/github.com/DataDog/datadog-go/statsd/pipe.go deleted file mode 100644 index 0d098a182..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/pipe.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !windows - -package statsd - -import "errors" - -func newWindowsPipeWriter(pipepath string) (statsdWriter, error) { - return nil, errors.New("Windows Named Pipes are only supported on Windows") -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go b/vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go deleted file mode 100644 index f533b0248..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go +++ /dev/null @@ -1,84 +0,0 @@ -// +build windows - -package statsd - -import ( - "net" - "sync" - "time" - - "github.com/Microsoft/go-winio" -) - -const defaultPipeTimeout = 1 * time.Millisecond - -type pipeWriter struct { - mu sync.RWMutex - conn net.Conn - timeout time.Duration - pipepath string -} - -func (p *pipeWriter) SetWriteTimeout(d time.Duration) error { - p.mu.Lock() - p.timeout = d - p.mu.Unlock() - return nil -} - -func (p *pipeWriter) Write(data []byte) (n int, err error) { - conn, err := p.ensureConnection() - if err != nil { - return 0, err - } - - p.mu.RLock() - conn.SetWriteDeadline(time.Now().Add(p.timeout)) - p.mu.RUnlock() - - n, err = conn.Write(data) - if err != nil { - if e, ok := err.(net.Error); !ok || !e.Temporary() { - // disconnected; retry again on next attempt - p.mu.Lock() - p.conn = nil - p.mu.Unlock() - } - } - return n, err -} - -func (p *pipeWriter) ensureConnection() (net.Conn, error) { - p.mu.RLock() - conn := p.conn - p.mu.RUnlock() - if conn != nil { - return conn, nil - } - - // looks like we might need to connect - try again with write locking. - p.mu.Lock() - defer p.mu.Unlock() - if p.conn != nil { - return p.conn, nil - } - newconn, err := winio.DialPipe(p.pipepath, nil) - if err != nil { - return nil, err - } - p.conn = newconn - return newconn, nil -} - -func (p *pipeWriter) Close() error { - return p.conn.Close() -} - -func newWindowsPipeWriter(pipepath string) (*pipeWriter, error) { - // Defer connection establishment to first write - return &pipeWriter{ - conn: nil, - timeout: defaultPipeTimeout, - pipepath: pipepath, - }, nil -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/sender.go b/vendor/github.com/DataDog/datadog-go/statsd/sender.go deleted file mode 100644 index 4c8eb2696..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/sender.go +++ /dev/null @@ -1,130 +0,0 @@ -package statsd - -import ( - "sync/atomic" - "time" -) - -// A statsdWriter offers a standard interface regardless of the underlying -// protocol. For now UDS and UPD writers are available. -// Attention: the underlying buffer of `data` is reused after a `statsdWriter.Write` call. -// `statsdWriter.Write` must be synchronous. -type statsdWriter interface { - Write(data []byte) (n int, err error) - SetWriteTimeout(time.Duration) error - Close() error -} - -// SenderMetrics contains metrics about the health of the sender -type SenderMetrics struct { - TotalSentBytes uint64 - TotalSentPayloads uint64 - TotalDroppedPayloads uint64 - TotalDroppedBytes uint64 - TotalDroppedPayloadsQueueFull uint64 - TotalDroppedBytesQueueFull uint64 - TotalDroppedPayloadsWriter uint64 - TotalDroppedBytesWriter uint64 -} - -type sender struct { - transport statsdWriter - pool *bufferPool - queue chan *statsdBuffer - metrics *SenderMetrics - stop chan struct{} - flushSignal chan struct{} -} - -func newSender(transport statsdWriter, queueSize int, pool *bufferPool) *sender { - sender := &sender{ - transport: transport, - pool: pool, - queue: make(chan *statsdBuffer, queueSize), - metrics: &SenderMetrics{}, - stop: make(chan struct{}), - flushSignal: make(chan struct{}), - } - - go sender.sendLoop() - return sender -} - -func (s *sender) send(buffer *statsdBuffer) { - select { - case s.queue <- buffer: - default: - atomic.AddUint64(&s.metrics.TotalDroppedPayloads, 1) - atomic.AddUint64(&s.metrics.TotalDroppedBytes, uint64(len(buffer.bytes()))) - atomic.AddUint64(&s.metrics.TotalDroppedPayloadsQueueFull, 1) - atomic.AddUint64(&s.metrics.TotalDroppedBytesQueueFull, uint64(len(buffer.bytes()))) - s.pool.returnBuffer(buffer) - } -} - -func (s *sender) write(buffer *statsdBuffer) { - _, err := s.transport.Write(buffer.bytes()) - if err != nil { - atomic.AddUint64(&s.metrics.TotalDroppedPayloads, 1) - atomic.AddUint64(&s.metrics.TotalDroppedBytes, uint64(len(buffer.bytes()))) - atomic.AddUint64(&s.metrics.TotalDroppedPayloadsWriter, 1) - atomic.AddUint64(&s.metrics.TotalDroppedBytesWriter, uint64(len(buffer.bytes()))) - } else { - atomic.AddUint64(&s.metrics.TotalSentPayloads, 1) - atomic.AddUint64(&s.metrics.TotalSentBytes, uint64(len(buffer.bytes()))) - } - s.pool.returnBuffer(buffer) -} - -func (s *sender) flushTelemetryMetrics() SenderMetrics { - return SenderMetrics{ - TotalSentBytes: atomic.SwapUint64(&s.metrics.TotalSentBytes, 0), - TotalSentPayloads: atomic.SwapUint64(&s.metrics.TotalSentPayloads, 0), - TotalDroppedPayloads: atomic.SwapUint64(&s.metrics.TotalDroppedPayloads, 0), - TotalDroppedBytes: atomic.SwapUint64(&s.metrics.TotalDroppedBytes, 0), - TotalDroppedPayloadsQueueFull: atomic.SwapUint64(&s.metrics.TotalDroppedPayloadsQueueFull, 0), - TotalDroppedBytesQueueFull: atomic.SwapUint64(&s.metrics.TotalDroppedBytesQueueFull, 0), - TotalDroppedPayloadsWriter: atomic.SwapUint64(&s.metrics.TotalDroppedPayloadsWriter, 0), - TotalDroppedBytesWriter: atomic.SwapUint64(&s.metrics.TotalDroppedBytesWriter, 0), - } -} - -func (s *sender) sendLoop() { - defer close(s.stop) - for { - select { - case buffer := <-s.queue: - s.write(buffer) - case <-s.stop: - return - case <-s.flushSignal: - // At that point we know that the workers are paused (the statsd client - // will pause them before calling sender.flush()). - // So we can fully flush the input queue - s.flushInputQueue() - s.flushSignal <- struct{}{} - } - } -} - -func (s *sender) flushInputQueue() { - for { - select { - case buffer := <-s.queue: - s.write(buffer) - default: - return - } - } -} -func (s *sender) flush() { - s.flushSignal <- struct{}{} - <-s.flushSignal -} - -func (s *sender) close() error { - s.stop <- struct{}{} - <-s.stop - s.flushInputQueue() - return s.transport.Close() -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/service_check.go b/vendor/github.com/DataDog/datadog-go/statsd/service_check.go deleted file mode 100644 index fce86755f..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/service_check.go +++ /dev/null @@ -1,70 +0,0 @@ -package statsd - -import ( - "fmt" - "time" -) - -// ServiceCheckStatus support -type ServiceCheckStatus byte - -const ( - // Ok is the "ok" ServiceCheck status - Ok ServiceCheckStatus = 0 - // Warn is the "warning" ServiceCheck status - Warn ServiceCheckStatus = 1 - // Critical is the "critical" ServiceCheck status - Critical ServiceCheckStatus = 2 - // Unknown is the "unknown" ServiceCheck status - Unknown ServiceCheckStatus = 3 -) - -// A ServiceCheck is an object that contains status of DataDog service check. -type ServiceCheck struct { - // Name of the service check. Required. - Name string - // Status of service check. Required. - Status ServiceCheckStatus - // Timestamp is a timestamp for the serviceCheck. If not provided, the dogstatsd - // server will set this to the current time. - Timestamp time.Time - // Hostname for the serviceCheck. - Hostname string - // A message describing the current state of the serviceCheck. - Message string - // Tags for the serviceCheck. - Tags []string -} - -// NewServiceCheck creates a new serviceCheck with the given name and status. Error checking -// against these values is done at send-time, or upon running sc.Check. -func NewServiceCheck(name string, status ServiceCheckStatus) *ServiceCheck { - return &ServiceCheck{ - Name: name, - Status: status, - } -} - -// Check verifies that a service check is valid. -func (sc ServiceCheck) Check() error { - if len(sc.Name) == 0 { - return fmt.Errorf("statsd.ServiceCheck name is required") - } - if byte(sc.Status) < 0 || byte(sc.Status) > 3 { - return fmt.Errorf("statsd.ServiceCheck status has invalid value") - } - return nil -} - -// Encode returns the dogstatsd wire protocol representation for a service check. -// Tags may be passed which will be added to the encoded output but not to -// the Service Check's list of tags, eg. for default tags. -func (sc ServiceCheck) Encode(tags ...string) (string, error) { - err := sc.Check() - if err != nil { - return "", err - } - var buffer []byte - buffer = appendServiceCheck(buffer, sc, tags) - return string(buffer), nil -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/statsd.go b/vendor/github.com/DataDog/datadog-go/statsd/statsd.go deleted file mode 100644 index 19b5515a5..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/statsd.go +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright 2013 Ooyala, Inc. - -/* -Package statsd provides a Go dogstatsd client. Dogstatsd extends the popular statsd, -adding tags and histograms and pushing upstream to Datadog. - -Refer to http://docs.datadoghq.com/guides/dogstatsd/ for information about DogStatsD. - -statsd is based on go-statsd-client. -*/ -package statsd - -import ( - "errors" - "fmt" - "os" - "strings" - "sync" - "sync/atomic" - "time" -) - -/* -OptimalUDPPayloadSize defines the optimal payload size for a UDP datagram, 1432 bytes -is optimal for regular networks with an MTU of 1500 so datagrams don't get -fragmented. It's generally recommended not to fragment UDP datagrams as losing -a single fragment will cause the entire datagram to be lost. -*/ -const OptimalUDPPayloadSize = 1432 - -/* -MaxUDPPayloadSize defines the maximum payload size for a UDP datagram. -Its value comes from the calculation: 65535 bytes Max UDP datagram size - -8byte UDP header - 60byte max IP headers -any number greater than that will see frames being cut out. -*/ -const MaxUDPPayloadSize = 65467 - -// DefaultUDPBufferPoolSize is the default size of the buffer pool for UDP clients. -const DefaultUDPBufferPoolSize = 2048 - -// DefaultUDSBufferPoolSize is the default size of the buffer pool for UDS clients. -const DefaultUDSBufferPoolSize = 512 - -/* -DefaultMaxAgentPayloadSize is the default maximum payload size the agent -can receive. This can be adjusted by changing dogstatsd_buffer_size in the -agent configuration file datadog.yaml. This is also used as the optimal payload size -for UDS datagrams. -*/ -const DefaultMaxAgentPayloadSize = 8192 - -/* -UnixAddressPrefix holds the prefix to use to enable Unix Domain Socket -traffic instead of UDP. -*/ -const UnixAddressPrefix = "unix://" - -/* -WindowsPipeAddressPrefix holds the prefix to use to enable Windows Named Pipes -traffic instead of UDP. -*/ -const WindowsPipeAddressPrefix = `\\.\pipe\` - -const ( - agentHostEnvVarName = "DD_AGENT_HOST" - agentPortEnvVarName = "DD_DOGSTATSD_PORT" - defaultUDPPort = "8125" -) - -/* -ddEnvTagsMapping is a mapping of each "DD_" prefixed environment variable -to a specific tag name. We use a slice to keep the order and simplify tests. -*/ -var ddEnvTagsMapping = []struct{ envName, tagName string }{ - {"DD_ENTITY_ID", "dd.internal.entity_id"}, // Client-side entity ID injection for container tagging. - {"DD_ENV", "env"}, // The name of the env in which the service runs. - {"DD_SERVICE", "service"}, // The name of the running service. - {"DD_VERSION", "version"}, // The current version of the running service. -} - -type metricType int - -const ( - gauge metricType = iota - count - histogram - histogramAggregated - distribution - distributionAggregated - set - timing - timingAggregated - event - serviceCheck -) - -type ReceivingMode int - -const ( - MutexMode ReceivingMode = iota - ChannelMode -) - -const ( - WriterNameUDP string = "udp" - WriterNameUDS string = "uds" - WriterWindowsPipe string = "pipe" -) - -type metric struct { - metricType metricType - namespace string - globalTags []string - name string - fvalue float64 - fvalues []float64 - ivalue int64 - svalue string - evalue *Event - scvalue *ServiceCheck - tags []string - stags string - rate float64 -} - -type noClientErr string - -// ErrNoClient is returned if statsd reporting methods are invoked on -// a nil client. -const ErrNoClient = noClientErr("statsd client is nil") - -func (e noClientErr) Error() string { - return string(e) -} - -// ClientInterface is an interface that exposes the common client functions for the -// purpose of being able to provide a no-op client or even mocking. This can aid -// downstream users' with their testing. -type ClientInterface interface { - // Gauge measures the value of a metric at a particular time. - Gauge(name string, value float64, tags []string, rate float64) error - - // Count tracks how many times something happened per second. - Count(name string, value int64, tags []string, rate float64) error - - // Histogram tracks the statistical distribution of a set of values on each host. - Histogram(name string, value float64, tags []string, rate float64) error - - // Distribution tracks the statistical distribution of a set of values across your infrastructure. - Distribution(name string, value float64, tags []string, rate float64) error - - // Decr is just Count of -1 - Decr(name string, tags []string, rate float64) error - - // Incr is just Count of 1 - Incr(name string, tags []string, rate float64) error - - // Set counts the number of unique elements in a group. - Set(name string, value string, tags []string, rate float64) error - - // Timing sends timing information, it is an alias for TimeInMilliseconds - Timing(name string, value time.Duration, tags []string, rate float64) error - - // TimeInMilliseconds sends timing information in milliseconds. - // It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing) - TimeInMilliseconds(name string, value float64, tags []string, rate float64) error - - // Event sends the provided Event. - Event(e *Event) error - - // SimpleEvent sends an event with the provided title and text. - SimpleEvent(title, text string) error - - // ServiceCheck sends the provided ServiceCheck. - ServiceCheck(sc *ServiceCheck) error - - // SimpleServiceCheck sends an serviceCheck with the provided name and status. - SimpleServiceCheck(name string, status ServiceCheckStatus) error - - // Close the client connection. - Close() error - - // Flush forces a flush of all the queued dogstatsd payloads. - Flush() error - - // SetWriteTimeout allows the user to set a custom write timeout. - SetWriteTimeout(d time.Duration) error -} - -// A Client is a handle for sending messages to dogstatsd. It is safe to -// use one Client from multiple goroutines simultaneously. -type Client struct { - // Sender handles the underlying networking protocol - sender *sender - // Namespace to prepend to all statsd calls - Namespace string - // Tags are global tags to be added to every statsd call - Tags []string - // skipErrors turns off error passing and allows UDS to emulate UDP behaviour - SkipErrors bool - flushTime time.Duration - metrics *ClientMetrics - telemetry *telemetryClient - stop chan struct{} - wg sync.WaitGroup - workers []*worker - closerLock sync.Mutex - workersMode ReceivingMode - aggregatorMode ReceivingMode - agg *aggregator - aggExtended *aggregator - options []Option - addrOption string -} - -// ClientMetrics contains metrics about the client -type ClientMetrics struct { - TotalMetrics uint64 - TotalMetricsGauge uint64 - TotalMetricsCount uint64 - TotalMetricsHistogram uint64 - TotalMetricsDistribution uint64 - TotalMetricsSet uint64 - TotalMetricsTiming uint64 - TotalEvents uint64 - TotalServiceChecks uint64 - TotalDroppedOnReceive uint64 -} - -// Verify that Client implements the ClientInterface. -// https://golang.org/doc/faq#guarantee_satisfies_interface -var _ ClientInterface = &Client{} - -func resolveAddr(addr string) string { - envPort := "" - if addr == "" { - addr = os.Getenv(agentHostEnvVarName) - envPort = os.Getenv(agentPortEnvVarName) - } - - if addr == "" { - return "" - } - - if !strings.HasPrefix(addr, WindowsPipeAddressPrefix) && !strings.HasPrefix(addr, UnixAddressPrefix) { - if !strings.Contains(addr, ":") { - if envPort != "" { - addr = fmt.Sprintf("%s:%s", addr, envPort) - } else { - addr = fmt.Sprintf("%s:%s", addr, defaultUDPPort) - } - } - } - return addr -} - -func createWriter(addr string) (statsdWriter, string, error) { - addr = resolveAddr(addr) - if addr == "" { - return nil, "", errors.New("No address passed and autodetection from environment failed") - } - - switch { - case strings.HasPrefix(addr, WindowsPipeAddressPrefix): - w, err := newWindowsPipeWriter(addr) - return w, WriterWindowsPipe, err - case strings.HasPrefix(addr, UnixAddressPrefix): - w, err := newUDSWriter(addr[len(UnixAddressPrefix):]) - return w, WriterNameUDS, err - default: - w, err := newUDPWriter(addr) - return w, WriterNameUDP, err - } -} - -// New returns a pointer to a new Client given an addr in the format "hostname:port" for UDP, -// "unix:///path/to/socket" for UDS or "\\.\pipe\path\to\pipe" for Windows Named Pipes. -func New(addr string, options ...Option) (*Client, error) { - o, err := resolveOptions(options) - if err != nil { - return nil, err - } - - w, writerType, err := createWriter(addr) - if err != nil { - return nil, err - } - - client, err := newWithWriter(w, o, writerType) - if err == nil { - client.options = append(client.options, options...) - client.addrOption = addr - } - return client, err -} - -// NewWithWriter creates a new Client with given writer. Writer is a -// io.WriteCloser + SetWriteTimeout(time.Duration) error -func NewWithWriter(w statsdWriter, options ...Option) (*Client, error) { - o, err := resolveOptions(options) - if err != nil { - return nil, err - } - return newWithWriter(w, o, "custom") -} - -// CloneWithExtraOptions create a new Client with extra options -func CloneWithExtraOptions(c *Client, options ...Option) (*Client, error) { - if c == nil { - return nil, ErrNoClient - } - - if c.addrOption == "" { - return nil, fmt.Errorf("can't clone client with no addrOption") - } - opt := append(c.options, options...) - return New(c.addrOption, opt...) -} - -func newWithWriter(w statsdWriter, o *Options, writerName string) (*Client, error) { - - w.SetWriteTimeout(o.WriteTimeoutUDS) - - c := Client{ - Namespace: o.Namespace, - Tags: o.Tags, - metrics: &ClientMetrics{}, - } - // Inject values of DD_* environment variables as global tags. - for _, mapping := range ddEnvTagsMapping { - if value := os.Getenv(mapping.envName); value != "" { - c.Tags = append(c.Tags, fmt.Sprintf("%s:%s", mapping.tagName, value)) - } - } - - if o.MaxBytesPerPayload == 0 { - if writerName == WriterNameUDS { - o.MaxBytesPerPayload = DefaultMaxAgentPayloadSize - } else { - o.MaxBytesPerPayload = OptimalUDPPayloadSize - } - } - if o.BufferPoolSize == 0 { - if writerName == WriterNameUDS { - o.BufferPoolSize = DefaultUDSBufferPoolSize - } else { - o.BufferPoolSize = DefaultUDPBufferPoolSize - } - } - if o.SenderQueueSize == 0 { - if writerName == WriterNameUDS { - o.SenderQueueSize = DefaultUDSBufferPoolSize - } else { - o.SenderQueueSize = DefaultUDPBufferPoolSize - } - } - - bufferPool := newBufferPool(o.BufferPoolSize, o.MaxBytesPerPayload, o.MaxMessagesPerPayload) - c.sender = newSender(w, o.SenderQueueSize, bufferPool) - c.aggregatorMode = o.ReceiveMode - - c.workersMode = o.ReceiveMode - // ChannelMode mode at the worker level is not enabled when - // ExtendedAggregation is since the user app will not directly - // use the worker (the aggregator sit between the app and the - // workers). - if o.ExtendedAggregation { - c.workersMode = MutexMode - } - - if o.Aggregation || o.ExtendedAggregation { - c.agg = newAggregator(&c) - c.agg.start(o.AggregationFlushInterval) - - if o.ExtendedAggregation { - c.aggExtended = c.agg - - if c.aggregatorMode == ChannelMode { - c.agg.startReceivingMetric(o.ChannelModeBufferSize, o.BufferShardCount) - } - } - } - - for i := 0; i < o.BufferShardCount; i++ { - w := newWorker(bufferPool, c.sender) - c.workers = append(c.workers, w) - - if c.workersMode == ChannelMode { - w.startReceivingMetric(o.ChannelModeBufferSize) - } - } - - c.flushTime = o.BufferFlushInterval - c.stop = make(chan struct{}, 1) - - c.wg.Add(1) - go func() { - defer c.wg.Done() - c.watch() - }() - - if o.Telemetry { - if o.TelemetryAddr == "" { - c.telemetry = newTelemetryClient(&c, writerName, o.DevMode) - } else { - var err error - c.telemetry, err = newTelemetryClientWithCustomAddr(&c, writerName, o.DevMode, o.TelemetryAddr, bufferPool) - if err != nil { - return nil, err - } - } - c.telemetry.run(&c.wg, c.stop) - } - - return &c, nil -} - -// NewBuffered returns a Client that buffers its output and sends it in chunks. -// Buflen is the length of the buffer in number of commands. -// -// When addr is empty, the client will default to a UDP client and use the DD_AGENT_HOST -// and (optionally) the DD_DOGSTATSD_PORT environment variables to build the target address. -func NewBuffered(addr string, buflen int) (*Client, error) { - return New(addr, WithMaxMessagesPerPayload(buflen)) -} - -// SetWriteTimeout allows the user to set a custom UDS write timeout. Not supported for UDP -// or Windows Pipes. -func (c *Client) SetWriteTimeout(d time.Duration) error { - if c == nil { - return ErrNoClient - } - return c.sender.transport.SetWriteTimeout(d) -} - -func (c *Client) watch() { - ticker := time.NewTicker(c.flushTime) - - for { - select { - case <-ticker.C: - for _, w := range c.workers { - w.flush() - } - case <-c.stop: - ticker.Stop() - return - } - } -} - -// Flush forces a flush of all the queued dogstatsd payloads This method is -// blocking and will not return until everything is sent through the network. -// In MutexMode, this will also block sampling new data to the client while the -// workers and sender are flushed. -func (c *Client) Flush() error { - if c == nil { - return ErrNoClient - } - if c.agg != nil { - c.agg.flush() - } - for _, w := range c.workers { - w.pause() - defer w.unpause() - w.flushUnsafe() - } - // Now that the worker are pause the sender can flush the queue between - // worker and senders - c.sender.flush() - return nil -} - -func (c *Client) FlushTelemetryMetrics() ClientMetrics { - cm := ClientMetrics{ - TotalMetricsGauge: atomic.SwapUint64(&c.metrics.TotalMetricsGauge, 0), - TotalMetricsCount: atomic.SwapUint64(&c.metrics.TotalMetricsCount, 0), - TotalMetricsSet: atomic.SwapUint64(&c.metrics.TotalMetricsSet, 0), - TotalMetricsHistogram: atomic.SwapUint64(&c.metrics.TotalMetricsHistogram, 0), - TotalMetricsDistribution: atomic.SwapUint64(&c.metrics.TotalMetricsDistribution, 0), - TotalMetricsTiming: atomic.SwapUint64(&c.metrics.TotalMetricsTiming, 0), - TotalEvents: atomic.SwapUint64(&c.metrics.TotalEvents, 0), - TotalServiceChecks: atomic.SwapUint64(&c.metrics.TotalServiceChecks, 0), - TotalDroppedOnReceive: atomic.SwapUint64(&c.metrics.TotalDroppedOnReceive, 0), - } - - cm.TotalMetrics = cm.TotalMetricsGauge + cm.TotalMetricsCount + - cm.TotalMetricsSet + cm.TotalMetricsHistogram + - cm.TotalMetricsDistribution + cm.TotalMetricsTiming - - return cm -} - -func (c *Client) send(m metric) error { - h := hashString32(m.name) - worker := c.workers[h%uint32(len(c.workers))] - - if c.workersMode == ChannelMode { - select { - case worker.inputMetrics <- m: - default: - atomic.AddUint64(&c.metrics.TotalDroppedOnReceive, 1) - } - return nil - } - return worker.processMetric(m) -} - -// sendBlocking is used by the aggregator to inject aggregated metrics. -func (c *Client) sendBlocking(m metric) error { - m.globalTags = c.Tags - m.namespace = c.Namespace - - h := hashString32(m.name) - worker := c.workers[h%uint32(len(c.workers))] - return worker.processMetric(m) -} - -func (c *Client) sendToAggregator(mType metricType, name string, value float64, tags []string, rate float64, f bufferedMetricSampleFunc) error { - if c.aggregatorMode == ChannelMode { - select { - case c.aggExtended.inputMetrics <- metric{metricType: mType, name: name, fvalue: value, tags: tags, rate: rate}: - default: - atomic.AddUint64(&c.metrics.TotalDroppedOnReceive, 1) - } - return nil - } - return f(name, value, tags, rate) -} - -// Gauge measures the value of a metric at a particular time. -func (c *Client) Gauge(name string, value float64, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsGauge, 1) - if c.agg != nil { - return c.agg.gauge(name, value, tags) - } - return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Count tracks how many times something happened per second. -func (c *Client) Count(name string, value int64, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsCount, 1) - if c.agg != nil { - return c.agg.count(name, value, tags) - } - return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Histogram tracks the statistical distribution of a set of values on each host. -func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsHistogram, 1) - if c.aggExtended != nil { - return c.sendToAggregator(histogram, name, value, tags, rate, c.aggExtended.histogram) - } - return c.send(metric{metricType: histogram, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Distribution tracks the statistical distribution of a set of values across your infrastructure. -func (c *Client) Distribution(name string, value float64, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsDistribution, 1) - if c.aggExtended != nil { - return c.sendToAggregator(distribution, name, value, tags, rate, c.aggExtended.distribution) - } - return c.send(metric{metricType: distribution, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Decr is just Count of -1 -func (c *Client) Decr(name string, tags []string, rate float64) error { - return c.Count(name, -1, tags, rate) -} - -// Incr is just Count of 1 -func (c *Client) Incr(name string, tags []string, rate float64) error { - return c.Count(name, 1, tags, rate) -} - -// Set counts the number of unique elements in a group. -func (c *Client) Set(name string, value string, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsSet, 1) - if c.agg != nil { - return c.agg.set(name, value, tags) - } - return c.send(metric{metricType: set, name: name, svalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Timing sends timing information, it is an alias for TimeInMilliseconds -func (c *Client) Timing(name string, value time.Duration, tags []string, rate float64) error { - return c.TimeInMilliseconds(name, value.Seconds()*1000, tags, rate) -} - -// TimeInMilliseconds sends timing information in milliseconds. -// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing) -func (c *Client) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalMetricsTiming, 1) - if c.aggExtended != nil { - return c.sendToAggregator(timing, name, value, tags, rate, c.aggExtended.timing) - } - return c.send(metric{metricType: timing, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace}) -} - -// Event sends the provided Event. -func (c *Client) Event(e *Event) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalEvents, 1) - return c.send(metric{metricType: event, evalue: e, rate: 1, globalTags: c.Tags, namespace: c.Namespace}) -} - -// SimpleEvent sends an event with the provided title and text. -func (c *Client) SimpleEvent(title, text string) error { - e := NewEvent(title, text) - return c.Event(e) -} - -// ServiceCheck sends the provided ServiceCheck. -func (c *Client) ServiceCheck(sc *ServiceCheck) error { - if c == nil { - return ErrNoClient - } - atomic.AddUint64(&c.metrics.TotalServiceChecks, 1) - return c.send(metric{metricType: serviceCheck, scvalue: sc, rate: 1, globalTags: c.Tags, namespace: c.Namespace}) -} - -// SimpleServiceCheck sends an serviceCheck with the provided name and status. -func (c *Client) SimpleServiceCheck(name string, status ServiceCheckStatus) error { - sc := NewServiceCheck(name, status) - return c.ServiceCheck(sc) -} - -// Close the client connection. -func (c *Client) Close() error { - if c == nil { - return ErrNoClient - } - - // Acquire closer lock to ensure only one thread can close the stop channel - c.closerLock.Lock() - defer c.closerLock.Unlock() - - // Notify all other threads that they should stop - select { - case <-c.stop: - return nil - default: - } - close(c.stop) - - if c.workersMode == ChannelMode { - for _, w := range c.workers { - w.stopReceivingMetric() - } - } - - // flush the aggregator first - if c.agg != nil { - if c.aggExtended != nil && c.aggregatorMode == ChannelMode { - c.agg.stopReceivingMetric() - } - c.agg.stop() - } - - // Wait for the threads to stop - c.wg.Wait() - - c.Flush() - return c.sender.close() -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/telemetry.go b/vendor/github.com/DataDog/datadog-go/statsd/telemetry.go deleted file mode 100644 index a6715af0d..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/telemetry.go +++ /dev/null @@ -1,151 +0,0 @@ -package statsd - -import ( - "fmt" - "sync" - "time" -) - -/* -TelemetryInterval is the interval at which telemetry will be sent by the client. -*/ -const TelemetryInterval = 10 * time.Second - -/* -clientTelemetryTag is a tag identifying this specific client. -*/ -var clientTelemetryTag = "client:go" - -/* -clientVersionTelemetryTag is a tag identifying this specific client version. -*/ -var clientVersionTelemetryTag = "client_version:4.8.2" - -type telemetryClient struct { - c *Client - tags []string - tagsByType map[metricType][]string - sender *sender - worker *worker - devMode bool -} - -func newTelemetryClient(c *Client, transport string, devMode bool) *telemetryClient { - t := &telemetryClient{ - c: c, - tags: append(c.Tags, clientTelemetryTag, clientVersionTelemetryTag, "client_transport:"+transport), - tagsByType: map[metricType][]string{}, - devMode: devMode, - } - - if devMode { - t.tagsByType[gauge] = append(append([]string{}, t.tags...), "metrics_type:gauge") - t.tagsByType[count] = append(append([]string{}, t.tags...), "metrics_type:count") - t.tagsByType[set] = append(append([]string{}, t.tags...), "metrics_type:set") - t.tagsByType[timing] = append(append([]string{}, t.tags...), "metrics_type:timing") - t.tagsByType[histogram] = append(append([]string{}, t.tags...), "metrics_type:histogram") - t.tagsByType[distribution] = append(append([]string{}, t.tags...), "metrics_type:distribution") - t.tagsByType[timing] = append(append([]string{}, t.tags...), "metrics_type:timing") - } - return t -} - -func newTelemetryClientWithCustomAddr(c *Client, transport string, devMode bool, telemetryAddr string, pool *bufferPool) (*telemetryClient, error) { - telemetryWriter, _, err := createWriter(telemetryAddr) - if err != nil { - return nil, fmt.Errorf("Could not resolve telemetry address: %v", err) - } - - t := newTelemetryClient(c, transport, devMode) - - // Creating a custom sender/worker with 1 worker in mutex mode for the - // telemetry that share the same bufferPool. - // FIXME due to performance pitfall, we're always using UDP defaults - // even for UDS. - t.sender = newSender(telemetryWriter, DefaultUDPBufferPoolSize, pool) - t.worker = newWorker(pool, t.sender) - return t, nil -} - -func (t *telemetryClient) run(wg *sync.WaitGroup, stop chan struct{}) { - wg.Add(1) - go func() { - defer wg.Done() - ticker := time.NewTicker(TelemetryInterval) - for { - select { - case <-ticker.C: - t.sendTelemetry() - case <-stop: - ticker.Stop() - if t.sender != nil { - t.sender.close() - } - return - } - } - }() -} - -func (t *telemetryClient) sendTelemetry() { - for _, m := range t.flush() { - if t.worker != nil { - t.worker.processMetric(m) - } else { - t.c.send(m) - } - } - - if t.worker != nil { - t.worker.flush() - } -} - -// flushTelemetry returns Telemetry metrics to be flushed. It's its own function to ease testing. -func (t *telemetryClient) flush() []metric { - m := []metric{} - - // same as Count but without global namespace - telemetryCount := func(name string, value int64, tags []string) { - m = append(m, metric{metricType: count, name: name, ivalue: value, tags: tags, rate: 1}) - } - - clientMetrics := t.c.FlushTelemetryMetrics() - telemetryCount("datadog.dogstatsd.client.metrics", int64(clientMetrics.TotalMetrics), t.tags) - if t.devMode { - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsGauge), t.tagsByType[gauge]) - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsCount), t.tagsByType[count]) - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsHistogram), t.tagsByType[histogram]) - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsDistribution), t.tagsByType[distribution]) - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsSet), t.tagsByType[set]) - telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsTiming), t.tagsByType[timing]) - } - - telemetryCount("datadog.dogstatsd.client.events", int64(clientMetrics.TotalEvents), t.tags) - telemetryCount("datadog.dogstatsd.client.service_checks", int64(clientMetrics.TotalServiceChecks), t.tags) - telemetryCount("datadog.dogstatsd.client.metric_dropped_on_receive", int64(clientMetrics.TotalDroppedOnReceive), t.tags) - - senderMetrics := t.c.sender.flushTelemetryMetrics() - telemetryCount("datadog.dogstatsd.client.packets_sent", int64(senderMetrics.TotalSentPayloads), t.tags) - telemetryCount("datadog.dogstatsd.client.bytes_sent", int64(senderMetrics.TotalSentBytes), t.tags) - telemetryCount("datadog.dogstatsd.client.packets_dropped", int64(senderMetrics.TotalDroppedPayloads), t.tags) - telemetryCount("datadog.dogstatsd.client.bytes_dropped", int64(senderMetrics.TotalDroppedBytes), t.tags) - telemetryCount("datadog.dogstatsd.client.packets_dropped_queue", int64(senderMetrics.TotalDroppedPayloadsQueueFull), t.tags) - telemetryCount("datadog.dogstatsd.client.bytes_dropped_queue", int64(senderMetrics.TotalDroppedBytesQueueFull), t.tags) - telemetryCount("datadog.dogstatsd.client.packets_dropped_writer", int64(senderMetrics.TotalDroppedPayloadsWriter), t.tags) - telemetryCount("datadog.dogstatsd.client.bytes_dropped_writer", int64(senderMetrics.TotalDroppedBytesWriter), t.tags) - - if aggMetrics := t.c.agg.flushTelemetryMetrics(); aggMetrics != nil { - telemetryCount("datadog.dogstatsd.client.aggregated_context", int64(aggMetrics.nbContext), t.tags) - if t.devMode { - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextGauge), t.tagsByType[gauge]) - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextSet), t.tagsByType[set]) - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextCount), t.tagsByType[count]) - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextHistogram), t.tagsByType[histogram]) - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextDistribution), t.tagsByType[distribution]) - telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextTiming), t.tagsByType[timing]) - } - } - - return m -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/udp.go b/vendor/github.com/DataDog/datadog-go/statsd/udp.go deleted file mode 100644 index 8af522c5b..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/udp.go +++ /dev/null @@ -1,40 +0,0 @@ -package statsd - -import ( - "errors" - "net" - "time" -) - -// udpWriter is an internal class wrapping around management of UDP connection -type udpWriter struct { - conn net.Conn -} - -// New returns a pointer to a new udpWriter given an addr in the format "hostname:port". -func newUDPWriter(addr string) (*udpWriter, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } - conn, err := net.DialUDP("udp", nil, udpAddr) - if err != nil { - return nil, err - } - writer := &udpWriter{conn: conn} - return writer, nil -} - -// SetWriteTimeout is not needed for UDP, returns error -func (w *udpWriter) SetWriteTimeout(d time.Duration) error { - return errors.New("SetWriteTimeout: not supported for UDP connections") -} - -// Write data to the UDP connection with no error handling -func (w *udpWriter) Write(data []byte) (int, error) { - return w.conn.Write(data) -} - -func (w *udpWriter) Close() error { - return w.conn.Close() -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/uds.go b/vendor/github.com/DataDog/datadog-go/statsd/uds.go deleted file mode 100644 index 6c52261bd..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/uds.go +++ /dev/null @@ -1,100 +0,0 @@ -// +build !windows - -package statsd - -import ( - "net" - "sync" - "time" -) - -/* -UDSTimeout holds the default timeout for UDS socket writes, as they can get -blocking when the receiving buffer is full. -*/ -const defaultUDSTimeout = 100 * time.Millisecond - -// udsWriter is an internal class wrapping around management of UDS connection -type udsWriter struct { - // Address to send metrics to, needed to allow reconnection on error - addr net.Addr - // Established connection object, or nil if not connected yet - conn net.Conn - // write timeout - writeTimeout time.Duration - sync.RWMutex // used to lock conn / writer can replace it -} - -// newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr. -func newUDSWriter(addr string) (*udsWriter, error) { - udsAddr, err := net.ResolveUnixAddr("unixgram", addr) - if err != nil { - return nil, err - } - // Defer connection to first Write - writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: defaultUDSTimeout} - return writer, nil -} - -// SetWriteTimeout allows the user to set a custom write timeout -func (w *udsWriter) SetWriteTimeout(d time.Duration) error { - w.writeTimeout = d - return nil -} - -// Write data to the UDS connection with write timeout and minimal error handling: -// create the connection if nil, and destroy it if the statsd server has disconnected -func (w *udsWriter) Write(data []byte) (int, error) { - conn, err := w.ensureConnection() - if err != nil { - return 0, err - } - - conn.SetWriteDeadline(time.Now().Add(w.writeTimeout)) - n, e := conn.Write(data) - - if err, isNetworkErr := e.(net.Error); err != nil && (!isNetworkErr || !err.Temporary()) { - // Statsd server disconnected, retry connecting at next packet - w.unsetConnection() - return 0, e - } - return n, e -} - -func (w *udsWriter) Close() error { - if w.conn != nil { - return w.conn.Close() - } - return nil -} - -func (w *udsWriter) ensureConnection() (net.Conn, error) { - // Check if we've already got a socket we can use - w.RLock() - currentConn := w.conn - w.RUnlock() - - if currentConn != nil { - return currentConn, nil - } - - // Looks like we might need to connect - try again with write locking. - w.Lock() - defer w.Unlock() - if w.conn != nil { - return w.conn, nil - } - - newConn, err := net.Dial(w.addr.Network(), w.addr.String()) - if err != nil { - return nil, err - } - w.conn = newConn - return newConn, nil -} - -func (w *udsWriter) unsetConnection() { - w.Lock() - defer w.Unlock() - w.conn = nil -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go b/vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go deleted file mode 100644 index 9c97dfd4e..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build windows - -package statsd - -import "fmt" - -// newUDSWriter is disable on windows as unix sockets are not available -func newUDSWriter(addr string) (statsdWriter, error) { - return nil, fmt.Errorf("unix socket is not available on windows") -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/utils.go b/vendor/github.com/DataDog/datadog-go/statsd/utils.go deleted file mode 100644 index a2829d94f..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/utils.go +++ /dev/null @@ -1,23 +0,0 @@ -package statsd - -import ( - "math/rand" - "sync" -) - -func shouldSample(rate float64, r *rand.Rand, lock *sync.Mutex) bool { - if rate >= 1 { - return true - } - // sources created by rand.NewSource() (ie. w.random) are not thread safe. - // TODO: use defer once the lowest Go version we support is 1.14 (defer - // has an overhead before that). - lock.Lock() - if r.Float64() > rate { - lock.Unlock() - return false - } - lock.Unlock() - return true - -} diff --git a/vendor/github.com/DataDog/datadog-go/statsd/worker.go b/vendor/github.com/DataDog/datadog-go/statsd/worker.go deleted file mode 100644 index e5a3bac56..000000000 --- a/vendor/github.com/DataDog/datadog-go/statsd/worker.go +++ /dev/null @@ -1,150 +0,0 @@ -package statsd - -import ( - "math/rand" - "sync" - "time" -) - -type worker struct { - pool *bufferPool - buffer *statsdBuffer - sender *sender - random *rand.Rand - randomLock sync.Mutex - sync.Mutex - - inputMetrics chan metric - stop chan struct{} -} - -func newWorker(pool *bufferPool, sender *sender) *worker { - // Each worker uses its own random source and random lock to prevent - // workers in separate goroutines from contending for the lock on the - // "math/rand" package-global random source (e.g. calls like - // "rand.Float64()" must acquire a shared lock to get the next - // pseudorandom number). - // Note that calling "time.Now().UnixNano()" repeatedly quickly may return - // very similar values. That's fine for seeding the worker-specific random - // source because we just need an evenly distributed stream of float values. - // Do not use this random source for cryptographic randomness. - random := rand.New(rand.NewSource(time.Now().UnixNano())) - return &worker{ - pool: pool, - sender: sender, - buffer: pool.borrowBuffer(), - random: random, - stop: make(chan struct{}), - } -} - -func (w *worker) startReceivingMetric(bufferSize int) { - w.inputMetrics = make(chan metric, bufferSize) - go w.pullMetric() -} - -func (w *worker) stopReceivingMetric() { - w.stop <- struct{}{} -} - -func (w *worker) pullMetric() { - for { - select { - case m := <-w.inputMetrics: - w.processMetric(m) - case <-w.stop: - return - } - } -} - -func (w *worker) processMetric(m metric) error { - if !shouldSample(m.rate, w.random, &w.randomLock) { - return nil - } - w.Lock() - var err error - if err = w.writeMetricUnsafe(m); err == errBufferFull { - w.flushUnsafe() - err = w.writeMetricUnsafe(m) - } - w.Unlock() - return err -} - -func (w *worker) writeAggregatedMetricUnsafe(m metric, metricSymbol []byte, precision int) error { - globalPos := 0 - - // first check how much data we can write to the buffer: - // +3 + len(metricSymbol) because the message will include '||#' before the tags - // +1 for the potential line break at the start of the metric - tagsSize := len(m.stags) + 4 + len(metricSymbol) - for _, t := range m.globalTags { - tagsSize += len(t) + 1 - } - - for { - pos, err := w.buffer.writeAggregated(metricSymbol, m.namespace, m.globalTags, m.name, m.fvalues[globalPos:], m.stags, tagsSize, precision) - if err == errPartialWrite { - // We successfully wrote part of the histogram metrics. - // We flush the current buffer and finish the histogram - // in a new one. - w.flushUnsafe() - globalPos += pos - } else { - return err - } - } -} - -func (w *worker) writeMetricUnsafe(m metric) error { - switch m.metricType { - case gauge: - return w.buffer.writeGauge(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) - case count: - return w.buffer.writeCount(m.namespace, m.globalTags, m.name, m.ivalue, m.tags, m.rate) - case histogram: - return w.buffer.writeHistogram(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) - case distribution: - return w.buffer.writeDistribution(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) - case set: - return w.buffer.writeSet(m.namespace, m.globalTags, m.name, m.svalue, m.tags, m.rate) - case timing: - return w.buffer.writeTiming(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) - case event: - return w.buffer.writeEvent(*m.evalue, m.globalTags) - case serviceCheck: - return w.buffer.writeServiceCheck(*m.scvalue, m.globalTags) - case histogramAggregated: - return w.writeAggregatedMetricUnsafe(m, histogramSymbol, -1) - case distributionAggregated: - return w.writeAggregatedMetricUnsafe(m, distributionSymbol, -1) - case timingAggregated: - return w.writeAggregatedMetricUnsafe(m, timingSymbol, 6) - default: - return nil - } -} - -func (w *worker) flush() { - w.Lock() - w.flushUnsafe() - w.Unlock() -} - -func (w *worker) pause() { - w.Lock() -} - -func (w *worker) unpause() { - w.Unlock() -} - -// flush the current buffer. Lock must be held by caller. -// flushed buffer written to the network asynchronously. -func (w *worker) flushUnsafe() { - if len(w.buffer.bytes()) > 0 { - w.sender.send(w.buffer) - w.buffer = w.pool.borrowBuffer() - } -} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go index 6180e1f1d..ae4723c42 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go @@ -66,6 +66,7 @@ func (a *aggregator) start(flushInterval time.Duration) { case <-ticker.C: a.flush() case <-a.closed: + ticker.Stop() return } } @@ -172,12 +173,32 @@ func (a *aggregator) flushMetrics() []metric { } func getContext(name string, tags []string) string { - return name + ":" + strings.Join(tags, tagSeparatorSymbol) + c, _ := getContextAndTags(name, tags) + return c } func getContextAndTags(name string, tags []string) (string, string) { - stringTags := strings.Join(tags, tagSeparatorSymbol) - return name + ":" + stringTags, stringTags + if len(tags) == 0 { + return name + nameSeparatorSymbol, "" + } + n := len(name) + len(nameSeparatorSymbol) + len(tagSeparatorSymbol)*(len(tags)-1) + for _, s := range tags { + n += len(s) + } + + var sb strings.Builder + sb.Grow(n) + sb.WriteString(name) + sb.WriteString(nameSeparatorSymbol) + sb.WriteString(tags[0]) + for _, s := range tags[1:] { + sb.WriteString(tagSeparatorSymbol) + sb.WriteString(s) + } + + s := sb.String() + + return s, s[len(name)+len(nameSeparatorSymbol):] } func (a *aggregator) count(name string, value int64, tags []string) error { diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go index f41430194..f7bb8b0aa 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go @@ -4,11 +4,15 @@ import ( "strconv" ) -type bufferFullError string +// MessageTooLongError is an error returned when a sample, event or service check is too large once serialized. See +// WithMaxBytesPerPayload option for more details. +type MessageTooLongError struct{} -func (e bufferFullError) Error() string { return string(e) } +func (e MessageTooLongError) Error() string { + return "message too long. See 'WithMaxBytesPerPayload' documentation." +} -const errBufferFull = bufferFullError("statsd buffer is full") +var errBufferFull = MessageTooLongError{} type partialWriteError string @@ -35,22 +39,24 @@ func newStatsdBuffer(maxSize, maxElements int) *statsdBuffer { } } -func (b *statsdBuffer) writeGauge(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error { +func (b *statsdBuffer) writeGauge(namespace string, globalTags []string, name string, value float64, tags []string, rate float64, timestamp int64) error { if b.elementCount >= b.maxElements { return errBufferFull } originalBuffer := b.buffer b.buffer = appendGauge(b.buffer, namespace, globalTags, name, value, tags, rate) + b.buffer = appendTimestamp(b.buffer, timestamp) b.writeSeparator() return b.validateNewElement(originalBuffer) } -func (b *statsdBuffer) writeCount(namespace string, globalTags []string, name string, value int64, tags []string, rate float64) error { +func (b *statsdBuffer) writeCount(namespace string, globalTags []string, name string, value int64, tags []string, rate float64, timestamp int64) error { if b.elementCount >= b.maxElements { return errBufferFull } originalBuffer := b.buffer b.buffer = appendCount(b.buffer, namespace, globalTags, name, value, tags, rate) + b.buffer = appendTimestamp(b.buffer, timestamp) b.writeSeparator() return b.validateNewElement(originalBuffer) } @@ -107,6 +113,7 @@ func (b *statsdBuffer) writeAggregated(metricSymbol []byte, namespace string, gl b.buffer = append(b.buffer, '|') b.buffer = append(b.buffer, metricSymbol...) b.buffer = appendTagsAggregated(b.buffer, globalTags, tags) + b.buffer = appendContainerID(b.buffer) b.writeSeparator() b.elementCount++ diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/container.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/container.go similarity index 68% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/container.go rename to vendor/github.com/DataDog/datadog-go/v5/statsd/container.go index feda30a2f..b2331e829 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/container.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/container.go @@ -1,9 +1,4 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -package internal +package statsd import ( "bufio" @@ -11,6 +6,7 @@ import ( "io" "os" "regexp" + "sync" ) const ( @@ -31,14 +27,10 @@ var ( // expContainerID matches contained IDs and sources. Source: https://github.com/Qard/container-info/blob/master/index.js expContainerID = regexp.MustCompile(fmt.Sprintf(`(%s|%s|%s)(?:.scope)?$`, uuidSource, containerSource, taskSource)) - // containerID is the containerID read at init from /proc/self/cgroup - containerID string + // containerID holds the container ID. + containerID = "" ) -func init() { - containerID = readContainerID(cgroupPath) -} - // parseContainerID finds the first container ID reading from r and returns it. func parseContainerID(r io.Reader) string { scn := bufio.NewScanner(r) @@ -65,7 +57,26 @@ func readContainerID(fpath string) string { return parseContainerID(f) } -// ContainerID attempts to return the container ID from /proc/self/cgroup or empty on failure. -func ContainerID() string { +// getContainerID returns the container ID configured at the client creation +// It can either be auto-discovered with origin detection or provided by the user. +// User-defined container ID is prioritized. +func getContainerID() string { return containerID } + +var initOnce sync.Once + +// initContainerID initializes the container ID. +// It can either be provided by the user or read from cgroups. +func initContainerID(userProvidedID string, cgroupFallback bool) { + initOnce.Do(func() { + if userProvidedID != "" { + containerID = userProvidedID + return + } + + if cgroupFallback { + containerID = readContainerID(cgroupPath) + } + }) +} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/event.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/event.go index 5e4b09428..a2ca4faf7 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/event.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/event.go @@ -37,7 +37,7 @@ const ( type Event struct { // Title of the event. Required. Title string - // Text is the description of the event. Required. + // Text is the description of the event. Text string // Timestamp is a timestamp for the event. If not provided, the dogstatsd // server will set this to the current time. diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/format.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/format.go index e83169f40..f3ab9231f 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/format.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/format.go @@ -6,13 +6,14 @@ import ( ) var ( - gaugeSymbol = []byte("g") - countSymbol = []byte("c") - histogramSymbol = []byte("h") - distributionSymbol = []byte("d") - setSymbol = []byte("s") - timingSymbol = []byte("ms") - tagSeparatorSymbol = "," + gaugeSymbol = []byte("g") + countSymbol = []byte("c") + histogramSymbol = []byte("h") + distributionSymbol = []byte("d") + setSymbol = []byte("s") + timingSymbol = []byte("ms") + tagSeparatorSymbol = "," + nameSeparatorSymbol = ":" ) func appendHeader(buffer []byte, namespace string, name string) []byte { @@ -101,6 +102,7 @@ func appendFloatMetric(buffer []byte, typeSymbol []byte, namespace string, globa buffer = append(buffer, typeSymbol...) buffer = appendRate(buffer, rate) buffer = appendTags(buffer, globalTags, tags) + buffer = appendContainerID(buffer) return buffer } @@ -111,6 +113,7 @@ func appendIntegerMetric(buffer []byte, typeSymbol []byte, namespace string, glo buffer = append(buffer, typeSymbol...) buffer = appendRate(buffer, rate) buffer = appendTags(buffer, globalTags, tags) + buffer = appendContainerID(buffer) return buffer } @@ -121,6 +124,7 @@ func appendStringMetric(buffer []byte, typeSymbol []byte, namespace string, glob buffer = append(buffer, typeSymbol...) buffer = appendRate(buffer, rate) buffer = appendTags(buffer, globalTags, tags) + buffer = appendContainerID(buffer) return buffer } @@ -210,6 +214,7 @@ func appendEvent(buffer []byte, event *Event, globalTags []string) []byte { } buffer = appendTags(buffer, globalTags, event.Tags) + buffer = appendContainerID(buffer) return buffer } @@ -249,9 +254,27 @@ func appendServiceCheck(buffer []byte, serviceCheck *ServiceCheck, globalTags [] buffer = append(buffer, "|m:"...) buffer = appendEscapedServiceCheckText(buffer, serviceCheck.Message) } + + buffer = appendContainerID(buffer) return buffer } func appendSeparator(buffer []byte) []byte { return append(buffer, '\n') } + +func appendContainerID(buffer []byte) []byte { + if containerID := getContainerID(); len(containerID) > 0 { + buffer = append(buffer, "|c:"...) + buffer = append(buffer, containerID...) + } + return buffer +} + +func appendTimestamp(buffer []byte, timestamp int64) []byte { + if timestamp > noTimestamp { + buffer = append(buffer, "|T"...) + buffer = strconv.AppendInt(buffer, timestamp, 10) + } + return buffer +} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go index 99ed4da53..82f11ac18 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go @@ -23,7 +23,7 @@ func newCountMetric(name string, value int64, tags []string) *countMetric { return &countMetric{ value: value, name: name, - tags: tags, + tags: copySlice(tags), } } @@ -53,7 +53,7 @@ func newGaugeMetric(name string, value float64, tags []string) *gaugeMetric { return &gaugeMetric{ value: math.Float64bits(value), name: name, - tags: tags, + tags: copySlice(tags), } } @@ -84,7 +84,7 @@ func newSetMetric(name string, value string, tags []string) *setMetric { set := &setMetric{ data: map[string]struct{}{}, name: name, - tags: tags, + tags: copySlice(tags), } set.data[value] = struct{}{} return set diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/noop.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/noop.go index 206f6fc19..e92744f40 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/noop.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/noop.go @@ -11,11 +11,21 @@ func (n *NoOpClient) Gauge(name string, value float64, tags []string, rate float return nil } +// GaugeWithTimestamp does nothing and returns nil +func (n *NoOpClient) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error { + return nil +} + // Count does nothing and returns nil func (n *NoOpClient) Count(name string, value int64, tags []string, rate float64) error { return nil } +// CountWithTimestamp does nothing and returns nil +func (n *NoOpClient) CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error { + return nil +} + // Histogram does nothing and returns nil func (n *NoOpClient) Histogram(name string, value float64, tags []string, rate float64) error { return nil @@ -81,6 +91,16 @@ func (n *NoOpClient) Flush() error { return nil } +// IsClosed does nothing and return false +func (n *NoOpClient) IsClosed() bool { + return false +} + +// GetTelemetry does nothing and returns an empty Telemetry +func (n *NoOpClient) GetTelemetry() Telemetry { + return Telemetry{} +} + // Verify that NoOpClient implements the ClientInterface. // https://golang.org/doc/faq#guarantee_satisfies_interface var _ ClientInterface = &NoOpClient{} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/options.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/options.go index 8a2197fb6..0728a976b 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/options.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/options.go @@ -23,6 +23,7 @@ var ( defaultAggregationFlushInterval = 2 * time.Second defaultAggregation = true defaultExtendedAggregation = false + defaultOriginDetection = true ) // Options contains the configuration options for a client. @@ -43,6 +44,8 @@ type Options struct { aggregation bool extendedAggregation bool telemetryAddr string + originDetection bool + containerID string } func resolveOptions(options []Option) (*Options, error) { @@ -62,6 +65,7 @@ func resolveOptions(options []Option) (*Options, error) { aggregationFlushInterval: defaultAggregationFlushInterval, aggregation: defaultAggregation, extendedAggregation: defaultExtendedAggregation, + originDetection: defaultOriginDetection, } for _, option := range options { @@ -113,10 +117,13 @@ func WithMaxMessagesPerPayload(maxMessagesPerPayload int) Option { } } -// WithMaxBytesPerPayload sets the maximum number of bytes a single payload can contain. +// WithMaxBytesPerPayload sets the maximum number of bytes a single payload can contain. Each sample, even and service +// check must be lower than this value once serialized or an `MessageTooLongError` is returned. // -// The deault value 0 which will set the option to the optimal size for the transport protocol used: 1432 for UDP and -// named pipe and 8192 for UDS. +// The default value 0 which will set the option to the optimal size for the transport protocol used: 1432 for UDP and +// named pipe and 8192 for UDS. Those values offer the best performances. +// Be careful when changing this option, see +// https://docs.datadoghq.com/developers/dogstatsd/high_throughput/#ensure-proper-packet-sizes. func WithMaxBytesPerPayload(MaxBytesPerPayload int) Option { return func(o *Options) error { o.maxBytesPerPayload = MaxBytesPerPayload @@ -297,3 +304,45 @@ func WithTelemetryAddr(addr string) Option { return nil } } + +// WithoutOriginDetection disables the client origin detection. +// When enabled, the client tries to discover its container ID and sends it to the Agent +// to enrich the metrics with container tags. +// Origin detection can also be disabled by configuring the environment variabe DD_ORIGIN_DETECTION_ENABLED=false +// The client tries to read the container ID by parsing the file /proc/self/cgroup, this is not supported on Windows. +// The client prioritizes the value passed via DD_ENTITY_ID (if set) over the container ID. +// +// More on this here: https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#origin-detection-over-udp +func WithoutOriginDetection() Option { + return func(o *Options) error { + o.originDetection = false + return nil + } +} + +// WithOriginDetection enables the client origin detection. +// This feature requires Datadog Agent version >=6.35.0 && <7.0.0 or Agent versions >=7.35.0. +// When enabled, the client tries to discover its container ID and sends it to the Agent +// to enrich the metrics with container tags. +// Origin detection can be disabled by configuring the environment variabe DD_ORIGIN_DETECTION_ENABLED=false +// The client tries to read the container ID by parsing the file /proc/self/cgroup, this is not supported on Windows. +// The client prioritizes the value passed via DD_ENTITY_ID (if set) over the container ID. +// +// More on this here: https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#origin-detection-over-udp +func WithOriginDetection() Option { + return func(o *Options) error { + o.originDetection = true + return nil + } +} + +// WithContainerID allows passing the container ID, this will be used by the Agent to enrich metrics with container tags. +// This feature requires Datadog Agent version >=6.35.0 && <7.0.0 or Agent versions >=7.35.0. +// When configured, the provided container ID is prioritized over the container ID discovered via Origin Detection. +// The client prioritizes the value passed via DD_ENTITY_ID (if set) over the container ID. +func WithContainerID(id string) Option { + return func(o *Options) error { + o.containerID = id + return nil + } +} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/statsd.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/statsd.go index 5a29618a2..378581b9b 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/statsd.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/statsd.go @@ -10,11 +10,15 @@ statsd is based on go-statsd-client. */ package statsd +//go:generate mockgen -source=statsd.go -destination=mocks/statsd.go + import ( "errors" "fmt" "io" + "net/url" "os" + "strconv" "strings" "sync" "sync/atomic" @@ -66,18 +70,33 @@ const WindowsPipeAddressPrefix = `\\.\pipe\` const ( agentHostEnvVarName = "DD_AGENT_HOST" agentPortEnvVarName = "DD_DOGSTATSD_PORT" + agentURLEnvVarName = "DD_DOGSTATSD_URL" defaultUDPPort = "8125" ) +const ( + // ddEntityID specifies client-side user-specified entity ID injection. + // This env var can be set to the Pod UID on Kubernetes via the downward API. + // Docs: https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#origin-detection-over-udp + ddEntityID = "DD_ENTITY_ID" + + // ddEntityIDTag specifies the tag name for the client-side entity ID injection + // The Agent expects this tag to contain a non-prefixed Kubernetes Pod UID. + ddEntityIDTag = "dd.internal.entity_id" + + // originDetectionEnabled specifies the env var to enable/disable sending the container ID field. + originDetectionEnabled = "DD_ORIGIN_DETECTION_ENABLED" +) + /* ddEnvTagsMapping is a mapping of each "DD_" prefixed environment variable to a specific tag name. We use a slice to keep the order and simplify tests. */ var ddEnvTagsMapping = []struct{ envName, tagName string }{ - {"DD_ENTITY_ID", "dd.internal.entity_id"}, // Client-side entity ID injection for container tagging. - {"DD_ENV", "env"}, // The name of the env in which the service runs. - {"DD_SERVICE", "service"}, // The name of the running service. - {"DD_VERSION", "version"}, // The current version of the running service. + {ddEntityID, ddEntityIDTag}, // Client-side entity ID injection for container tagging. + {"DD_ENV", "env"}, // The name of the env in which the service runs. + {"DD_SERVICE", "service"}, // The name of the running service. + {"DD_VERSION", "version"}, // The current version of the running service. } type metricType int @@ -109,6 +128,9 @@ const ( writerWindowsPipe string = "pipe" ) +// noTimestamp is used as a value for metric without a given timestamp. +const noTimestamp = int64(0) + type metric struct { metricType metricType namespace string @@ -123,6 +145,7 @@ type metric struct { tags []string stags string rate float64 + timestamp int64 } type noClientErr string @@ -135,6 +158,15 @@ func (e noClientErr) Error() string { return string(e) } +type invalidTimestampErr string + +// InvalidTimestamp is returned if a provided timestamp is invalid. +const InvalidTimestamp = invalidTimestampErr("invalid timestamp") + +func (e invalidTimestampErr) Error() string { + return string(e) +} + // ClientInterface is an interface that exposes the common client functions for the // purpose of being able to provide a no-op client or even mocking. This can aid // downstream users' with their testing. @@ -142,9 +174,25 @@ type ClientInterface interface { // Gauge measures the value of a metric at a particular time. Gauge(name string, value float64, tags []string, rate float64) error + // GaugeWithTimestamp measures the value of a metric at a given time. + // BETA - Please contact our support team for more information to use this feature: https://www.datadoghq.com/support/ + // The value will bypass any aggregation on the client side and agent side, this is + // useful when sending points in the past. + // + // Minimum Datadog Agent version: 7.40.0 + GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error + // Count tracks how many times something happened per second. Count(name string, value int64, tags []string, rate float64) error + // CountWithTimestamp tracks how many times something happened at the given second. + // BETA - Please contact our support team for more information to use this feature: https://www.datadoghq.com/support/ + // The value will bypass any aggregation on the client side and agent side, this is + // useful when sending points in the past. + // + // Minimum Datadog Agent version: 7.40.0 + CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error + // Histogram tracks the statistical distribution of a set of values on each host. Histogram(name string, value float64, tags []string, rate float64) error @@ -184,6 +232,12 @@ type ClientInterface interface { // Flush forces a flush of all the queued dogstatsd payloads. Flush() error + + // IsClosed returns if the client has been closed. + IsClosed() bool + + // GetTelemetry return the telemetry metrics for the client since it started. + GetTelemetry() Telemetry } // A Client is a handle for sending messages to dogstatsd. It is safe to @@ -208,6 +262,7 @@ type Client struct { aggExtended *aggregator options []Option addrOption string + isClosed bool } // statsdTelemetry contains telemetry metrics about the client @@ -229,9 +284,17 @@ var _ ClientInterface = &Client{} func resolveAddr(addr string) string { envPort := "" + if addr == "" { addr = os.Getenv(agentHostEnvVarName) envPort = os.Getenv(agentPortEnvVarName) + agentURL, _ := os.LookupEnv(agentURLEnvVarName) + agentURL = parseAgentURL(agentURL) + + // agentURLEnvVarName has priority over agentHostEnvVarName + if agentURL != "" { + return agentURL + } } if addr == "" { @@ -250,6 +313,31 @@ func resolveAddr(addr string) string { return addr } +func parseAgentURL(agentURL string) string { + if agentURL != "" { + if strings.HasPrefix(agentURL, WindowsPipeAddressPrefix) { + return agentURL + } + + parsedURL, err := url.Parse(agentURL) + if err != nil { + return "" + } + + if parsedURL.Scheme == "udp" { + if strings.Contains(parsedURL.Host, ":") { + return parsedURL.Host + } + return fmt.Sprintf("%s:%s", parsedURL.Host, defaultUDPPort) + } + + if parsedURL.Scheme == "unix" { + return agentURL + } + } + return "" +} + func createWriter(addr string, writeTimeout time.Duration) (io.WriteCloser, string, error) { addr = resolveAddr(addr) if addr == "" { @@ -319,13 +407,22 @@ func newWithWriter(w io.WriteCloser, o *Options, writerName string) (*Client, er tags: o.tags, telemetry: &statsdTelemetry{}, } + + hasEntityID := false // Inject values of DD_* environment variables as global tags. for _, mapping := range ddEnvTagsMapping { if value := os.Getenv(mapping.envName); value != "" { + if mapping.envName == ddEntityID { + hasEntityID = true + } c.tags = append(c.tags, fmt.Sprintf("%s:%s", mapping.tagName, value)) } } + if !hasEntityID { + initContainerID(o.containerID, isOriginDetectionEnabled(o, hasEntityID)) + } + if o.maxBytesPerPayload == 0 { if writerName == writerNameUDS { o.maxBytesPerPayload = DefaultMaxAgentPayloadSize @@ -446,6 +543,13 @@ func (c *Client) Flush() error { return nil } +// IsClosed returns if the client has been closed. +func (c *Client) IsClosed() bool { + c.closerLock.Lock() + defer c.closerLock.Unlock() + return c.isClosed +} + func (c *Client) flushTelemetryMetrics(t *Telemetry) { t.TotalMetricsGauge = atomic.LoadUint64(&c.telemetry.totalMetricsGauge) t.TotalMetricsCount = atomic.LoadUint64(&c.telemetry.totalMetricsCount) @@ -512,6 +616,25 @@ func (c *Client) Gauge(name string, value float64, tags []string, rate float64) return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace}) } +// GaugeWithTimestamp measures the value of a metric at a given time. +// BETA - Please contact our support team for more information to use this feature: https://www.datadoghq.com/support/ +// The value will bypass any aggregation on the client side and agent side, this is +// useful when sending points in the past. +// +// Minimum Datadog Agent version: 7.40.0 +func (c *Client) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error { + if c == nil { + return ErrNoClient + } + + if timestamp.IsZero() || timestamp.Unix() <= noTimestamp { + return InvalidTimestamp + } + + atomic.AddUint64(&c.telemetry.totalMetricsGauge, 1) + return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace, timestamp: timestamp.Unix()}) +} + // Count tracks how many times something happened per second. func (c *Client) Count(name string, value int64, tags []string, rate float64) error { if c == nil { @@ -524,6 +647,25 @@ func (c *Client) Count(name string, value int64, tags []string, rate float64) er return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace}) } +// CountWithTimestamp tracks how many times something happened at the given second. +// BETA - Please contact our support team for more information to use this feature: https://www.datadoghq.com/support/ +// The value will bypass any aggregation on the client side and agent side, this is +// useful when sending points in the past. +// +// Minimum Datadog Agent version: 7.40.0 +func (c *Client) CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error { + if c == nil { + return ErrNoClient + } + + if timestamp.IsZero() || timestamp.Unix() <= noTimestamp { + return InvalidTimestamp + } + + atomic.AddUint64(&c.telemetry.totalMetricsCount, 1) + return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace, timestamp: timestamp.Unix()}) +} + // Histogram tracks the statistical distribution of a set of values on each host. func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error { if c == nil { @@ -628,6 +770,10 @@ func (c *Client) Close() error { c.closerLock.Lock() defer c.closerLock.Unlock() + if c.isClosed { + return nil + } + // Notify all other threads that they should stop select { case <-c.stop: @@ -654,5 +800,39 @@ func (c *Client) Close() error { c.wg.Wait() c.Flush() + + c.isClosed = true return c.sender.close() } + +// isOriginDetectionEnabled returns whether the clients should fill the container field. +// +// If DD_ENTITY_ID is set, we don't send the container ID +// If a user-defined container ID is provided, we don't ignore origin detection +// as dd.internal.entity_id is prioritized over the container field for backward compatibility. +// If DD_ENTITY_ID is not set, we try to fill the container field automatically unless +// DD_ORIGIN_DETECTION_ENABLED is explicitly set to false. +func isOriginDetectionEnabled(o *Options, hasEntityID bool) bool { + if !o.originDetection || hasEntityID || o.containerID != "" { + // originDetection is explicitly disabled + // or DD_ENTITY_ID was found + // or a user-defined container ID was provided + return false + } + + envVarValue := os.Getenv(originDetectionEnabled) + if envVarValue == "" { + // DD_ORIGIN_DETECTION_ENABLED is not set + // default to true + return true + } + + enabled, err := strconv.ParseBool(envVarValue) + if err != nil { + // Error due to an unsupported DD_ORIGIN_DETECTION_ENABLED value + // default to true + return true + } + + return enabled +} diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/telemetry.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/telemetry.go index 0b9d073f7..1e2bc0a3f 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/telemetry.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/telemetry.go @@ -19,7 +19,7 @@ var clientTelemetryTag = "client:go" /* clientVersionTelemetryTag is a tag identifying this specific client version. */ -var clientVersionTelemetryTag = "client_version:5.0.2" +var clientVersionTelemetryTag = "client_version:5.3.0" // Telemetry represents internal metrics about the client behavior since it started. type Telemetry struct { diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/utils.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/utils.go index a2829d94f..8c3ac8426 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/utils.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/utils.go @@ -19,5 +19,14 @@ func shouldSample(rate float64, r *rand.Rand, lock *sync.Mutex) bool { } lock.Unlock() return true +} + +func copySlice(src []string) []string { + if src == nil { + return nil + } + c := make([]string, len(src)) + copy(c, src) + return c } diff --git a/vendor/github.com/DataDog/datadog-go/v5/statsd/worker.go b/vendor/github.com/DataDog/datadog-go/v5/statsd/worker.go index 5446d506a..952a9fe36 100644 --- a/vendor/github.com/DataDog/datadog-go/v5/statsd/worker.go +++ b/vendor/github.com/DataDog/datadog-go/v5/statsd/worker.go @@ -100,9 +100,9 @@ func (w *worker) writeAggregatedMetricUnsafe(m metric, metricSymbol []byte, prec func (w *worker) writeMetricUnsafe(m metric) error { switch m.metricType { case gauge: - return w.buffer.writeGauge(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) + return w.buffer.writeGauge(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate, m.timestamp) case count: - return w.buffer.writeCount(m.namespace, m.globalTags, m.name, m.ivalue, m.tags, m.rate) + return w.buffer.writeCount(m.namespace, m.globalTags, m.name, m.ivalue, m.tags, m.rate, m.timestamp) case histogram: return w.buffer.writeHistogram(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate) case distribution: diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/.gitattributes b/vendor/github.com/DataDog/go-libddwaf/v2/.gitattributes new file mode 100644 index 000000000..003a80079 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/.gitattributes @@ -0,0 +1,3 @@ +*.dylib -diff +*.so -diff +*.a -diff diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/.gitignore b/vendor/github.com/DataDog/go-libddwaf/v2/.gitignore new file mode 100644 index 000000000..ed586868d --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +.vscode/ +.idea/ + +# Swap files +*.swp diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/LICENSE b/vendor/github.com/DataDog/go-libddwaf/v2/LICENSE new file mode 100644 index 000000000..9301dd7ab --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/LICENSE @@ -0,0 +1,200 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-present Datadog, Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/README.md b/vendor/github.com/DataDog/go-libddwaf/v2/README.md new file mode 100644 index 000000000..a3a94922b --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/README.md @@ -0,0 +1,148 @@ +# go-libddwaf + +This project's goal is to produce a higher level API for the go bindings to [libddwaf](https://github.com/DataDog/libddwaf): DataDog in-app WAF. +It consists of 2 separate entities: the bindings for the calls to libddwaf, and the encoder which job is to convert _any_ go value to its libddwaf object representation. + +An example usage would be: + +```go +import waf "github.com/DataDog/go-libddwaf" + +//go:embed +var ruleset []byte + +func main() { + var parsedRuleset any + + if err := json.Unmarshal(ruleset, &parsedRuleset); err != nil { + return 1 + } + + wafHandle, err := waf.NewHandle(parsedRuleset, "", "") + if err != nil { + return 1 + } + + defer wafHandle.Close() + + wafCtx := wafHandle.NewContext() + defer wafCtx.Close() + + matches, actions := wafCtx.Run(map[string]any{ + "server.request.path_params": "/rfiinc.txt", + }, time.Minute) +} +``` + +The API documentation details can be found on [pkg.go.dev](https://pkg.go.dev/github.com/DataDog/go-libddwaf). + +Originally this project was only here to provide CGO Wrappers to the calls to libddwaf. +But with the appearance of `ddwaf_object` tree like structure, +but also with the intention to build CGO-less bindings, this project size has grown to be a fully integrated brick in the DataDog tracer structure. +Which in turn made it necessary to document the project, to maintain it in an orderly fashion. + +## Supported platforms + +This library currently support the following platform doublets: + +| OS | Arch | +| ----- | ------- | +| Linux | amd64 | +| Linux | aarch64 | +| OSX | amd64 | +| OSX | arm64 | + +This means that when the platform is not supported, top-level functions will return a `WafDisabledError` error including the purpose of it. + +Note that: +* Linux support include for glibc and musl variants +* OSX under 10.9 is not supported +* A build tag named `datadog.no_waf` can be manually added to force the WAF to be disabled. + +## Design + +The WAF bindings have multiple moving parts that are necessary to understand: + +- Handle: a object wrapper over the pointer to the C WAF Handle +- Context: a object wrapper over a pointer to the C WAF Context +- Encoder: its goal is to construct a tree of Waf Objects to send to the WAF +- CGORefPool: Does all allocation operations for the construction of Waf Objects and keeps track of the equivalent go pointers +- Decoder: Transforms Waf Objects returned from the WAF to usual go objects (e.g. maps, arrays, ...) +- Library: The low-level go bindings to the C library, providing improved typing + +```mermaid +flowchart LR + + START:::hidden -->|NewHandle| Handle -->|NewContext| Context + + Context -->|Encode Inputs| Encoder + + Handle -->|Encode Ruleset| Encoder + Handle -->|Init WAF| Library + Context -->|Decode Result| Decoder + + Handle -->|Decode Init Errors| Decoder + + Context -->|Run| Library + Context -->|Store Go References| CGORefPool + + Encoder -->|Allocate Waf Objects| TempCGORefPool + + TempCGORefPool -->|Copy after each encoding| CGORefPool + + Library -->|Call C code| libddwaf + + classDef hidden display: none; +``` + +### CGO Reference Pool + +The cgoRefPool type is a pure Go pointer pool of `ddwaf_object` C values on the Go memory heap. +the `cgoRefPool` go type is a way to make sure we can safely send Go allocated data to the C side of the WAF +The main issue is the following: the `wafObject` uses a C union to store the tree structure of the full object, +union equivalent in go are interfaces and they are not compatible with C unions. The only way to be 100% sure +that the Go `wafObject` struct has the same layout as the C one is to only use primitive types. So the only way to +store a raw pointer is to use the `uintptr` type. But since `uintptr` do not have pointer semantics (and are just +basically integers), we need another method to store the value as Go pointer because the GC will delete our data if it +is not referenced by Go pointers. + +That's where the `cgoRefPool` object comes into play: all new `wafObject` elements are created via this API which is especially +built to make sure there is no gap for the Garbage Collector to exploit. From there, since underlying values of the +`wafObject` are either arrays of wafObjects (for maps, structs and arrays) or string (for all ints, booleans and strings), +we can store 2 slices of arrays and use `runtime.KeepAlive` in each code path to protect them from the GC. + +All these objects stored in the reference pool need to live throughout the use of the associated Waf Context. + +### Typical call to Run() + +Here is an example of the flow of operations on a simple call to Run(): + +- Encode input data into WAF Objects and store references in the temporary pool +- Lock the context mutex until the end of the call +- Store references from the temporary pool into the context level pool +- Call `ddwaf_run` +- Decode the matches and actions + +### CGO-less C Bindings + +This library uses [purego](https://github.com/ebitengine/purego) to implement C bindings without requiring use of CGO at compilation time. The high-level workflow +is to embed the C shared library using `go:embed`, dump it into a file, open the library using `dlopen`, load the +symbols using `dlsym`, and finally call them. + +> :warning: Keep in mind that **purego only works on linux/darwin for amd64/arm64 and so does go-libddwaf.** + +Another requirement of `libddwaf` is to have a FHS filesystem on your machine and, for linux, to provide `libc.so.6`, +`libpthread.so.0`, `libm.so.6` and `libdl.so.2` as dynamic libraries. + +## Contributing pitfalls + +- Cannot dlopen twice in the app lifetime on OSX. It messes with Thread Local Storage and usually finishes with a `std::bad_alloc()` +- `keepAlive()` calls are here to prevent the GC from destroying objects too early +- Since there is a stack switch between the Go code and the C code, usually the only C stacktrace you will ever get is from GDB +- If a segfault happens during a call to the C code, the goroutine stacktrace which has done the call is the one annotated with `[syscall]` +- [GoLand](https://www.jetbrains.com/go/) does not support `CGO_ENABLED=0` (as of June 2023) +- Keep in mind that we fully escape the type system. If you send the wrong data it will segfault in the best cases but not always! +- The structs in `ctypes.go` are here to reproduce the memory layout of the structs in `include/ddwaf.h` because pointers to these structs will be passed directly +- Do not use `uintptr` as function arguments or results types, coming from `unsafe.Pointer` casts of Go values, because they escape the pointer analysis which can create wrongly optimized code and crash. Pointer arithmetic is of course necessary in such a library but must be kept in the same function scope. +- GDB is available on arm64 but is not officially supported so it usually crashes pretty fast (as of June 2023) +- No pointer to variables on the stack shall be sent to the C code because Go stacks can be moved during the C call. More on this [here](https://medium.com/@trinad536/escape-analysis-in-golang-fc81b78f3550) diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/cgo_ref_pool.go b/vendor/github.com/DataDog/go-libddwaf/v2/cgo_ref_pool.go new file mode 100644 index 000000000..47cb6990b --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/cgo_ref_pool.go @@ -0,0 +1,97 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "strconv" +) + +// cgoRefPool is a way to make sure we can safely send go allocated data on the C side of the WAF +// The main issue is the following: the wafObject uses a C union to store the tree structure of the full object, +// union equivalent in go are interfaces and they are not compatible with C unions. The only way to be 100% sure +// that the Go wafObject struct have the same layout as the C one is to only use primitive types. So the only way to +// store a raw pointer is to use the uintptr type. But since uintptr do not have pointer semantics (and are just +// basically integers), we need another structure to store the value as Go pointer because the GC is lurking. That's +// where the cgoRefPool object comes into play: All new wafObject elements are created via this API whose especially +// built to make sure there is no gap for the Garbage Collector to exploit. From there, since underlying values of the +// wafObject are either arrays (for maps, structs and arrays) or string (for all ints, booleans and strings), +// we can store 2 slices of arrays and use runtime.KeepAlive in each code path to protect them from the GC. +type cgoRefPool struct { + stringRefs []string + arrayRefs [][]wafObject +} + +func (refPool *cgoRefPool) append(newRefs cgoRefPool) { + refPool.stringRefs = append(refPool.stringRefs, newRefs.stringRefs...) + refPool.arrayRefs = append(refPool.arrayRefs, newRefs.arrayRefs...) +} + +// AllocCString is used in the rare cases where we need the WAF to receive standard null-terminated strings. +// All cases where strings a wrapped in wafObject are handled by AllocWafString +func (refPool *cgoRefPool) AllocCString(str string) uintptr { + if len(str) > 0 && str[len(str)-1] != '\x00' { + str = str + "\x00" + } + + refPool.stringRefs = append(refPool.stringRefs, str) + return nativeStringUnwrap(str).Data +} + +// AllocWafString fills the obj parameter wafObject with all parameters needed for the WAF interpret it as a string. +// We take full advantage of the fact that the WAF can receive non-null-terminated strings by directly retrieving the +// underlying array in the string value using the nativeStringUnwrap function. Hence, removing any copy in the process +func (refPool *cgoRefPool) AllocWafString(obj *wafObject, str string) { + obj._type = wafStringType + + if len(str) == 0 { + obj.nbEntries = 0 + obj.value = 0 + return + } + + refPool.stringRefs = append(refPool.stringRefs, str) + stringHeader := nativeStringUnwrap(str) + obj.value = stringHeader.Data + obj.nbEntries = uint64(stringHeader.Len) +} + +// AllocWafArray is used to create a tree-like structure since we allocate a wafObject array inside another wafOject. +// wafObject can also represent a map, in that case we use the AllocWafMapKey function to make the wafObject key-value-pair +// like objects. +func (refPool *cgoRefPool) AllocWafArray(obj *wafObject, typ wafObjectType, size uint64) []wafObject { + if typ != wafMapType && typ != wafArrayType { + panic("Cannot allocate this waf object data type as an array: " + strconv.Itoa(int(typ))) + } + + obj._type = typ + obj.nbEntries = size + + // If the array size is zero no need to allocate anything + if size == 0 { + obj.value = 0 + return nil + } + + goArray := make([]wafObject, size) + refPool.arrayRefs = append(refPool.arrayRefs, goArray) + + obj.value = sliceToUintptr(goArray) + return goArray +} + +// AllocWafMapKey is used to store a string map key in a wafObject. +// We take full advantage of the fact that the WAF can receive non-null-terminated strings by directly retrieving the +// underlying array in the string value using the nativeStringUnwrap function. Hence, removing any copy in the process +func (refPool *cgoRefPool) AllocWafMapKey(obj *wafObject, str string) { + if len(str) == 0 { + return + } + + refPool.stringRefs = append(refPool.stringRefs, str) + stringHeader := nativeStringUnwrap(str) + obj.parameterName = stringHeader.Data + obj.parameterNameLength = uint64(stringHeader.Len) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/context.go b/vendor/github.com/DataDog/go-libddwaf/v2/context.go new file mode 100644 index 000000000..627f4239a --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/context.go @@ -0,0 +1,195 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "sync" + "time" + + "go.uber.org/atomic" +) + +// Context is a WAF execution context. It allows running the WAF incrementally +// when calling it multiple times to run its rules every time new addresses +// become available. Each request must have its own Context. +type Context struct { + handle *Handle // Instance of the WAF + + cgoRefs cgoRefPool // Used to retain go data referenced by WAF Objects the context holds + cContext wafContext // The C ddwaf_context pointer + + // Stats + totalRuntimeNs atomic.Uint64 // Cumulative internal WAF run time - in nanoseconds - for this context. + totalOverallRuntimeNs atomic.Uint64 // Cumulative overall run time - in nanoseconds - for this context. + timeoutCount atomic.Uint64 // Cumulative timeout count for this context. + + // Mutex protecting the use of cContext which is not thread-safe and cgoRefs. + mutex sync.Mutex +} + +// NewContext returns a new WAF context of to the given WAF handle. +// A nil value is returned when the WAF handle was released or when the +// WAF context couldn't be created. +// handle. A nil value is returned when the WAF handle can no longer be used +// or the WAF context couldn't be created. +func NewContext(handle *Handle) *Context { + // Handle has been released + if !handle.retain() { + return nil + } + + cContext := wafLib.wafContextInit(handle.cHandle) + if cContext == 0 { + handle.release() // We couldn't get a context, so we no longer have an implicit reference to the Handle in it... + return nil + } + + return &Context{handle: handle, cContext: cContext} +} + +// RunAddressData provides address data to the Context.Run method. If a given key is present in both +// RunAddressData.Persistent and RunAddressData.Ephemeral, the value from RunAddressData.Persistent will take precedence. +type RunAddressData struct { + // Persistent address data is scoped to the lifetime of a given Context, and subsquent calls to Context.Run with the + // same address name will be silently ignored. + Persistent map[string]any + // Ephemeral address data is scoped to a given Context.Run call and is not persisted across calls. This is used for + // protocols such as gRPC client/server streaming or GraphQL, where a single request can incur multiple subrequests. + Ephemeral map[string]any +} + +func (d RunAddressData) isEmpty() bool { + return len(d.Persistent) == 0 && len(d.Ephemeral) == 0 +} + +// Run encodes the given addressData values and runs them against the WAF rules within the given timeout value. If a +// given address is present both as persistent and ephemeral, the persistent value takes precedence. It returns the +// matches as a JSON string (usually opaquely used) along with the corresponding actions in any. In case of an error, +// matches and actions can still be returned, for instance in the case of a timeout error. Errors can be tested against +// the RunError type. +// Struct fields having the tag `ddwaf:"ignore"` will not be encoded and sent to the WAF +func (context *Context) Run(addressData RunAddressData, timeout time.Duration) (res Result, err error) { + if addressData.isEmpty() { + return + } + + now := time.Now() + defer func() { + dt := time.Since(now) + context.totalOverallRuntimeNs.Add(uint64(dt.Nanoseconds())) + }() + + // At this point, the only error we can get is an error in case the top level object is a nil map, but this + // behaviour is expected since either persistent or ephemeral addresses are allowed to be null one at a time. + // In this case, EncodeAddresses will return nil contrary to Encode which will return an nil wafObject, + // which is what we need to send to ddwaf_run to signal that the address data is empty. + var persistentData *wafObject = nil + var ephemeralData *wafObject = nil + persistentEncoder := newLimitedEncoder() + ephemeralEncoder := newLimitedEncoder() + if addressData.Persistent != nil { + persistentData, _ = persistentEncoder.EncodeAddresses(addressData.Persistent) + } + + if addressData.Ephemeral != nil { + ephemeralData, _ = ephemeralEncoder.EncodeAddresses(addressData.Ephemeral) + + } + // The WAF releases ephemeral address data at the end of each run call, so we need not keep the Go values live beyond + // that in the same way we need for persistent data. We hence use a separate encoder. + + // ddwaf_run cannot run concurrently and the next append write on the context state so we need a mutex + context.mutex.Lock() + defer context.mutex.Unlock() + + // Save the Go pointer references to addressesToData that were referenced by the encoder + // into C ddwaf_objects. libddwaf's API requires to keep this data for the lifetime of the ddwaf_context. + defer context.cgoRefs.append(persistentEncoder.cgoRefs) + + res, err = context.run(persistentData, ephemeralData, timeout, &persistentEncoder.cgoRefs) + + // Ensure the ephemerals don't get optimized away by the compiler before the WAF had a chance to use them. + keepAlive(ephemeralEncoder.cgoRefs) + + return +} + +// run executes the ddwaf_run call with the provided data on this context. The caller is responsible for locking the +// context appropriately around this call. +func (context *Context) run(persistentData, ephemeralData *wafObject, timeout time.Duration, cgoRefs *cgoRefPool) (Result, error) { + result := new(wafResult) + defer wafLib.wafResultFree(result) + + ret := wafLib.wafRun(context.cContext, persistentData, ephemeralData, result, uint64(timeout/time.Microsecond)) + + context.totalRuntimeNs.Add(result.total_runtime) + res, err := unwrapWafResult(ret, result) + if err == ErrTimeout { + context.timeoutCount.Inc() + } + + return res, err +} + +func unwrapWafResult(ret wafReturnCode, result *wafResult) (res Result, err error) { + if result.timeout > 0 { + err = ErrTimeout + } else { + // Derivatives can be generated even if no security event gets detected, so we decode them as long as the WAF + // didn't timeout + res.Derivatives, err = decodeMap(&result.derivatives) + } + + if ret == wafOK { + return res, err + } + + if ret != wafMatch { + return res, goRunError(ret) + } + + res.Events, err = decodeArray(&result.events) + if err != nil { + return res, err + } + if size := result.actions.nbEntries; size > 0 { + // using ruleIdArray cause it decodes string array (I think) + res.Actions, err = decodeStringArray(&result.actions) + // TODO: use decode array, and eventually genericize the function + if err != nil { + return res, err + } + } + + return res, err +} + +// Close the underlying `ddwaf_context` and releases the associated internal +// data. Also decreases the reference count of the `ddwaf_hadnle` which created +// this context, possibly releasing it completely (if this was the last context +// created from this handle & it was released by its creator). +func (context *Context) Close() { + context.mutex.Lock() + defer context.mutex.Unlock() + + wafLib.wafContextDestroy(context.cContext) + keepAlive(context.cgoRefs) // Keep the Go pointer references until the end of the context + defer context.handle.release() // Reduce the reference counter of the Handle. + + context.cgoRefs = cgoRefPool{} // The data in context.cgoRefs is no longer needed, explicitly release + context.cContext = 0 // Makes it easy to spot use-after-free/double-free issues +} + +// TotalRuntime returns the cumulated WAF runtime across various run calls within the same WAF context. +// Returned time is in nanoseconds. +func (context *Context) TotalRuntime() (overallRuntimeNs, internalRuntimeNs uint64) { + return context.totalOverallRuntimeNs.Load(), context.totalRuntimeNs.Load() +} + +// TotalTimeouts returns the cumulated amount of WAF timeouts across various run calls within the same WAF context. +func (context *Context) TotalTimeouts() uint64 { + return context.timeoutCount.Load() +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/ctypes.go b/vendor/github.com/DataDog/go-libddwaf/v2/ctypes.go new file mode 100644 index 000000000..6f2adccdd --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/ctypes.go @@ -0,0 +1,206 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "reflect" + "unsafe" +) + +const ( + wafMaxStringLength = 4096 + wafMaxContainerDepth = 20 + wafMaxContainerSize = 256 + wafRunTimeout = 5000 +) + +type wafReturnCode int32 + +const ( + wafErrInternal wafReturnCode = iota - 3 + wafErrInvalidObject + wafErrInvalidArgument + wafOK + wafMatch +) + +// wafObjectType is an enum in C which has the size of DWORD. +// But DWORD is 4 bytes in amd64 and arm64 so uint32 it is. +type wafObjectType uint32 + +const wafInvalidType wafObjectType = 0 +const ( + wafIntType wafObjectType = 1 << iota + wafUintType + wafStringType + wafArrayType + wafMapType + wafBoolType + wafFloatType + wafNilType +) + +type wafObject struct { + parameterName uintptr + parameterNameLength uint64 + value uintptr + nbEntries uint64 + _type wafObjectType + _ [4]byte + // Forced padding + // We only support 2 archs and cgo generated the same padding to both. + // We don't want the C struct to be packed because actually go will do the same padding itself, + // we just add it explicitly to not take any chance. + // And we cannot pack a struct in go so it will get tricky if the struct is + // packed (apart from breaking all tracers of course) +} + +// isInvalid determines whether this WAF Object has the invalid type (which is the 0-value). +func (w *wafObject) isInvalid() bool { + return w._type == wafInvalidType +} + +// isNil determines whether this WAF Object is nil or not. +func (w *wafObject) isNil() bool { + return w._type == wafNilType +} + +// isArray determines whether this WAF Object is an array or not. +func (w *wafObject) isArray() bool { + return w._type == wafArrayType +} + +// isMap determines whether this WAF Object is a map or not. +func (w *wafObject) isMap() bool { + return w._type == wafMapType +} + +// IsUnusable returns true if the wafObject has no impact on the WAF execution +// But we still need this kind of objects to forward map keys in case the value of the map is invalid +func (wo *wafObject) IsUnusable() bool { + return wo._type == wafInvalidType || wo._type == wafNilType +} + +type wafConfig struct { + limits wafConfigLimits + obfuscator wafConfigObfuscator + freeFn uintptr +} + +type wafConfigLimits struct { + maxContainerSize uint32 + maxContainerDepth uint32 + maxStringLength uint32 +} + +type wafConfigObfuscator struct { + keyRegex uintptr // char * + valueRegex uintptr // char * +} + +type wafResult struct { + timeout byte + events wafObject + actions wafObject + derivatives wafObject + total_runtime uint64 +} + +// wafHandle is a forward declaration in ddwaf.h header +// We basically don't need to modify it, only to give it to the waf +type wafHandle uintptr + +// wafContext is a forward declaration in ddwaf.h header +// We basically don't need to modify it, only to give it to the waf +type wafContext uintptr + +// gostring copies a char* to a Go string. +func gostring(ptr *byte) string { + if ptr == nil { + return "" + } + var length int + for { + if *(*byte)(unsafe.Add(unsafe.Pointer(ptr), uintptr(length))) == '\x00' { + break + } + length++ + } + //string builtin copies the slice + return string(unsafe.Slice(ptr, length)) +} + +// nativeStringUnwrap cast a native string type into it's runtime value. Exported as the struct reflect.StringHeader +func nativeStringUnwrap(str string) reflect.StringHeader { + return *(*reflect.StringHeader)(unsafe.Pointer(&str)) +} + +func gostringSized(ptr *byte, size uint64) string { + if ptr == nil { + return "" + } + return string(unsafe.Slice(ptr, size)) +} + +// cstring converts a go string to *byte that can be passed to C code. +func cstring(name string) *byte { + var b = make([]byte, len(name)+1) + copy(b, name) + return &b[0] +} + +// cast is used to centralize unsafe use C of allocated pointer. +// We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer +func cast[T any](ptr uintptr) *T { + return (*T)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr))) +} + +// nativeToUintptr is a helper used by populate wafObject values +// with Go values +func nativeToUintptr[T any](x T) uintptr { + return *(*uintptr)(unsafe.Pointer(&x)) +} + +// uintToNative is a helper used retrieve Go values from an uintptr encoded +// value from a wafObject +func uintptrToNative[T any](x uintptr) T { + return *(*T)(unsafe.Pointer(&x)) +} + +// castWithOffset is the same as cast but adding an offset to the pointer by a multiple of the size +// of the type pointed. +func castWithOffset[T any](ptr uintptr, offset uint64) *T { + return (*T)(unsafe.Add(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)), offset*uint64(unsafe.Sizeof(*new(T))))) +} + +// ptrToUintptr is a helper to centralize of usage of unsafe.Pointer +// do not use this function to cast interfaces +func ptrToUintptr[T any](arg *T) uintptr { + return uintptr(unsafe.Pointer(arg)) +} + +func sliceToUintptr[T any](arg []T) uintptr { + return (*reflect.SliceHeader)(unsafe.Pointer(&arg)).Data +} + +// keepAlive() globals +var ( + alwaysFalse bool + escapeSink any +) + +// keepAlive is a copy of runtime.KeepAlive +// keepAlive has 2 usages: +// - It forces the deallocation of the memory to take place later than expected (just like runtime.KeepAlive) +// - It forces the given argument x to be escaped on the heap by saving it into a global value (Go doesn't provide a standard way to do it as of today) +// It is implemented so that the compiler cannot optimize it. +// +//go:noinline +func keepAlive[T any](x T) { + if alwaysFalse { + escapeSink = x + } +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/decoder.go b/vendor/github.com/DataDog/go-libddwaf/v2/decoder.go new file mode 100644 index 000000000..ceaf2e2bd --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/decoder.go @@ -0,0 +1,239 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +// decodeErrors transforms the wafObject received by the wafRulesetInfo after the call to wafDl.wafInit to a map where +// keys are the error message and the value is a array of all the rule ids which triggered this specific error +func decodeErrors(obj *wafObject) (map[string][]string, error) { + if !obj.isMap() { + return nil, errInvalidObjectType + } + + if obj.value == 0 && obj.nbEntries > 0 { + return nil, errNilObjectPtr + } + + wafErrors := map[string][]string{} + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + + errorMessage := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength) + ruleIds, err := decodeStringArray(objElem) + if err != nil { + return nil, err + } + + wafErrors[errorMessage] = ruleIds + } + + return wafErrors, nil +} + +func decodeDiagnostics(obj *wafObject) (*Diagnostics, error) { + if !obj.isMap() { + return nil, errInvalidObjectType + } + if obj.value == 0 && obj.nbEntries > 0 { + return nil, errNilObjectPtr + } + + var diags Diagnostics + var err error + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + key := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength) + switch key { + case "custom_rules": + diags.CustomRules, err = decodeDiagnosticsEntry(objElem) + case "exclusions": + diags.Exclusions, err = decodeDiagnosticsEntry(objElem) + case "rules": + diags.Rules, err = decodeDiagnosticsEntry(objElem) + case "rules_data": + diags.RulesData, err = decodeDiagnosticsEntry(objElem) + case "rules_override": + diags.RulesOverrides, err = decodeDiagnosticsEntry(objElem) + case "processors": + diags.Processors, err = decodeDiagnosticsEntry(objElem) + case "scanners": + diags.Scanners, err = decodeDiagnosticsEntry(objElem) + case "ruleset_version": + diags.Version = gostringSized(cast[byte](objElem.value), objElem.nbEntries) + default: + // ignore? + } + if err != nil { + return nil, err + } + } + + return &diags, nil +} + +func decodeDiagnosticsEntry(obj *wafObject) (*DiagnosticEntry, error) { + if !obj.isMap() { + return nil, errInvalidObjectType + } + if obj.value == 0 && obj.nbEntries > 0 { + return nil, errNilObjectPtr + } + var entry DiagnosticEntry + var err error + + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + key := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength) + switch key { + case "addresses": + entry.Addresses, err = decodeDiagnosticAddresses(objElem) + case "error": + entry.Error = gostringSized(cast[byte](objElem.value), objElem.nbEntries) + case "errors": + entry.Errors, err = decodeErrors(objElem) + case "failed": + entry.Failed, err = decodeStringArray(objElem) + case "loaded": + entry.Loaded, err = decodeStringArray(objElem) + default: + return nil, errUnsupportedValue + } + + if err != nil { + return nil, err + } + } + + return &entry, nil +} + +func decodeDiagnosticAddresses(obj *wafObject) (*DiagnosticAddresses, error) { + if !obj.isMap() { + return nil, errInvalidObjectType + } + if obj.value == 0 && obj.nbEntries > 0 { + return nil, errNilObjectPtr + } + + addrs := &DiagnosticAddresses{} + + var err error + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + key := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength) + switch key { + case "required": + addrs.Required, err = decodeStringArray(objElem) + if err != nil { + return nil, err + } + case "optional": + addrs.Optional, err = decodeStringArray(objElem) + if err != nil { + return nil, err + } + default: + return nil, errUnsupportedValue + } + } + + return addrs, nil +} + +func decodeStringArray(obj *wafObject) ([]string, error) { + // We consider that nil is an empty array + if obj.isNil() { + return nil, nil + } + + if !obj.isArray() { + return nil, errInvalidObjectType + } + + if obj.value == 0 && obj.nbEntries > 0 { + return nil, errNilObjectPtr + } + + var strArr []string + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + if objElem._type != wafStringType { + return nil, errInvalidObjectType + } + + strArr = append(strArr, gostringSized(cast[byte](objElem.value), objElem.nbEntries)) + } + + return strArr, nil +} + +func decodeObject(obj *wafObject) (any, error) { + switch obj._type { + case wafMapType: + return decodeMap(obj) + case wafArrayType: + return decodeArray(obj) + case wafStringType: + return gostringSized(cast[byte](obj.value), obj.nbEntries), nil + case wafIntType: + return int64(obj.value), nil + case wafUintType: + return uint64(obj.value), nil + case wafFloatType: + return uintptrToNative[float64](obj.value), nil + case wafBoolType: + return uintptrToNative[bool](obj.value), nil + case wafNilType: + return nil, nil + default: + return nil, errUnsupportedValue + } +} + +func decodeArray(obj *wafObject) ([]any, error) { + if obj.isNil() { + return nil, nil + } + + if !obj.isArray() { + return nil, errInvalidObjectType + } + + events := make([]any, obj.nbEntries) + + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + val, err := decodeObject(objElem) + if err != nil { + return nil, err + } + events[i] = val + } + + return events, nil +} + +func decodeMap(obj *wafObject) (map[string]any, error) { + if obj.isNil() { + return nil, nil + } + + if !obj.isMap() { + return nil, errInvalidObjectType + } + + result := make(map[string]any, obj.nbEntries) + for i := uint64(0); i < obj.nbEntries; i++ { + objElem := castWithOffset[wafObject](obj.value, i) + key := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength) + val, err := decodeObject(objElem) + if err != nil { + return nil, err + } + result[key] = val + } + + return result, nil +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/encoder.go b/vendor/github.com/DataDog/go-libddwaf/v2/encoder.go new file mode 100644 index 000000000..09cd732ea --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/encoder.go @@ -0,0 +1,477 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "context" + "math" + "reflect" + "strings" + "time" + "unicode" +) + +// Encode Go values into wafObjects. Only the subset of Go types representable into wafObjects +// will be encoded while ignoring the rest of it. +// The encoder allocates the memory required for new wafObjects into the Go memory, which must be kept +// referenced for their lifetime in the C world. This lifetime depends on the ddwaf function being used with. +// the encoded result. The Go references of the allocated wafObjects, along with every Go pointer they may +// reference now or in the future, are stored and referenced in the `cgoRefs` field. The user MUST leverage +// `keepAlive()` with it according to its ddwaf use-case. +type encoder struct { + // For each TruncationReason, holds the size that is required to avoid truncation for each truncation that happened. + truncations map[TruncationReason][]int + + cgoRefs cgoRefPool + containerMaxSize int + stringMaxSize int + objectMaxDepth int +} + +// TruncationReason is a flag representing reasons why some input was not encoded in full. +type TruncationReason uint8 + +const ( + StringTooLong TruncationReason = 1 << iota + ContainerTooLarge + ObjectTooDeep +) + +const ( + AppsecFieldTag = "ddwaf" + AppsecFieldTagValueIgnore = "ignore" +) + +type native interface { + int64 | uint64 | uintptr +} + +func newLimitedEncoder() encoder { + return encoder{ + containerMaxSize: wafMaxContainerSize, + stringMaxSize: wafMaxStringLength, + objectMaxDepth: wafMaxContainerDepth, + } +} + +func newMaxEncoder() encoder { + return encoder{ + containerMaxSize: math.MaxInt, + stringMaxSize: math.MaxInt, + objectMaxDepth: math.MaxInt, + } +} + +// Encode takes a Go value and returns a wafObject pointer and an error. +// The returned wafObject is the root of the tree of nested wafObjects representing the Go value. +// The only error case is if the top-level object is "Unusable" which means that the data is nil or a non-data type +// like a function or a channel. +func (encoder *encoder) Encode(data any) (wo *wafObject, err error) { + value := reflect.ValueOf(data) + wo = &wafObject{} + + err = encoder.encode(value, wo, encoder.objectMaxDepth) + if len(encoder.truncations[ObjectTooDeep]) != 0 { + encoder.measureObjectDepth(value) + } + + return +} + +// Truncations returns all truncations that happened since the last call to `Truncations()`, and clears the internal +// list. This is a map from truncation reason to the list of un-truncated value sizes. +func (encoder *encoder) Truncations() map[TruncationReason][]int { + result := encoder.truncations + encoder.truncations = nil + return result +} + +// EncodeAddresses takes a map of Go values and returns a wafObject pointer and an error. +// The returned wafObject is the root of the tree of nested wafObjects representing the Go values. +// This function is further optimized from Encode to take addresses as input and avoid further +// errors in case the top-level map with addresses as keys is nil. +// Since errors returned by Encode are not sent up between levels of the tree, this means that all errors come from the +// top layer of encoding, which is the map of addresses. Hence, all errors should be developer errors since the map of +// addresses is not user defined custom data. +func (encoder *encoder) EncodeAddresses(addresses map[string]any) (*wafObject, error) { + if addresses == nil { + return nil, errUnsupportedValue + } + + return encoder.Encode(addresses) +} + +func encodeNative[T native](val T, t wafObjectType, obj *wafObject) { + obj._type = t + obj.value = (uintptr)(val) +} + +var nullableTypeKinds = map[reflect.Kind]struct{}{ + reflect.Interface: {}, + reflect.Pointer: {}, + reflect.UnsafePointer: {}, + reflect.Map: {}, + reflect.Slice: {}, + reflect.Func: {}, + reflect.Chan: {}, +} + +// isValueNil check if the value is nullable and if it is actually nil +// we cannot directly use value.IsNil() because it panics on non-pointer values +func isValueNil(value reflect.Value) bool { + _, nullable := nullableTypeKinds[value.Kind()] + return nullable && value.IsNil() +} + +func (encoder *encoder) encode(value reflect.Value, obj *wafObject, depth int) error { + value, kind := resolvePointer(value) + if (kind == reflect.Interface || kind == reflect.Pointer) && !value.IsNil() { + // resolvePointer failed to resolve to something that's not a pointer, it + // has indirected too many times... + return errTooManyIndirections + } + + // Measure-only runs for leaves + if obj == nil && kind != reflect.Array && kind != reflect.Slice && kind != reflect.Map && kind != reflect.Struct { + // Nothing to do, we were only here to measure object depth! + return nil + } + + switch { + // Terminal cases (leaves of the tree) + // Is invalid type: nil interfaces for example, cannot be used to run any reflect method or it's susceptible to panic + case !value.IsValid() || kind == reflect.Invalid: + return errUnsupportedValue + // Is nullable type: nil pointers, channels, maps or functions + case isValueNil(value): + encodeNative[uintptr](0, wafNilType, obj) + + // Booleans + case kind == reflect.Bool: + encodeNative(nativeToUintptr(value.Bool()), wafBoolType, obj) + + // Numbers + case value.CanInt(): // any int type or alias + encodeNative(value.Int(), wafIntType, obj) + case value.CanUint(): // any Uint type or alias + encodeNative(value.Uint(), wafUintType, obj) + case value.CanFloat(): // any float type or alias + encodeNative(nativeToUintptr(value.Float()), wafFloatType, obj) + + // Strings + case kind == reflect.String: // string type + encoder.encodeString(value.String(), obj) + + case (kind == reflect.Array || kind == reflect.Slice) && value.Type().Elem().Kind() == reflect.Uint8: + // Byte Arrays are skipped voluntarily because they are often used + // to do partial parsing which leads to false positives + return nil + + // Containers (internal nodes of the tree) + + // All recursive cases can only execute if the depth is superior to 0. + case depth <= 0: + // Record that there was a truncation; we will try to measure the actual depth of the object afterwards. + encoder.addTruncation(ObjectTooDeep, -1) + return errMaxDepthExceeded + + // Either an array or a slice of an array + case kind == reflect.Array || kind == reflect.Slice: + encoder.encodeArray(value, obj, depth-1) + case kind == reflect.Map: + encoder.encodeMap(value, obj, depth-1) + case kind == reflect.Struct: + encoder.encodeStruct(value, obj, depth-1) + + default: + return errUnsupportedValue + } + + return nil +} + +func (encoder *encoder) encodeString(str string, obj *wafObject) { + size := len(str) + if size > encoder.stringMaxSize { + str = str[:encoder.stringMaxSize] + encoder.addTruncation(StringTooLong, size) + } + encoder.cgoRefs.AllocWafString(obj, str) +} + +func getFieldNameFromType(field reflect.StructField) (string, bool) { + fieldName := field.Name + + // Private and synthetics fields + if len(fieldName) < 1 || unicode.IsLower(rune(fieldName[0])) { + return "", false + } + + // Use the json tag name as field name if present + if tag, ok := field.Tag.Lookup("json"); ok { + if i := strings.IndexByte(tag, byte(',')); i > 0 { + tag = tag[:i] + } + if len(tag) > 0 { + fieldName = tag + } + } + + return fieldName, true +} + +// encodeStruct takes a reflect.Value and a wafObject pointer and iterates on the struct field to build +// a wafObject map of type wafMapType. The specificities are the following: +// - It will only take the first encoder.containerMaxSize elements of the struct +// - If the field has a json tag it will become the field name +// - Private fields and also values producing an error at encoding will be skipped +// - Even if the element values are invalid or null we still keep them to report the field name +func (encoder *encoder) encodeStruct(value reflect.Value, obj *wafObject, depth int) { + typ := value.Type() + nbFields := typ.NumField() + + capacity := nbFields + length := 0 + if capacity > encoder.containerMaxSize { + capacity = encoder.containerMaxSize + } + + objArray := encoder.cgoRefs.AllocWafArray(obj, wafMapType, uint64(capacity)) + for i := 0; i < nbFields; i++ { + if length == capacity { + encoder.addTruncation(ContainerTooLarge, nbFields) + break + } + + fieldType := typ.Field(i) + fieldName, usable := getFieldNameFromType(fieldType) + if tag, ok := fieldType.Tag.Lookup(AppsecFieldTag); !usable || ok && tag == AppsecFieldTagValueIgnore { + // Either the struct field is ignored by json marshaling so can we, + // or the field was explicitly set with `ddwaf:ignore` + continue + } + + objElem := &objArray[length] + // If the Map key is of unsupported type, skip it + encoder.encodeMapKeyFromString(fieldName, objElem) + + if err := encoder.encode(value.Field(i), objElem, depth); err != nil { + // We still need to keep the map key, so we can't discard the full object, instead, we make the value a noop + encodeNative[uintptr](0, wafInvalidType, objElem) + } + + length++ + } + + // Set the length to the final number of successfully encoded elements + obj.nbEntries = uint64(length) +} + +// encodeMap takes a reflect.Value and a wafObject pointer and iterates on the map elements and returns +// a wafObject map of type wafMapType. The specificities are the following: +// - It will only take the first encoder.containerMaxSize elements of the map +// - Even if the element values are invalid or null we still keep them to report the map key +func (encoder *encoder) encodeMap(value reflect.Value, obj *wafObject, depth int) { + capacity := value.Len() + if capacity > encoder.containerMaxSize { + capacity = encoder.containerMaxSize + } + + objArray := encoder.cgoRefs.AllocWafArray(obj, wafMapType, uint64(capacity)) + + length := 0 + for iter := value.MapRange(); iter.Next(); { + if length == capacity { + encoder.addTruncation(ContainerTooLarge, value.Len()) + break + } + + objElem := &objArray[length] + if err := encoder.encodeMapKey(iter.Key(), objElem); err != nil { + continue + } + + if err := encoder.encode(iter.Value(), objElem, depth); err != nil { + // We still need to keep the map key, so we can't discard the full object, instead, we make the value a noop + encodeNative[uintptr](0, wafInvalidType, objElem) + } + + length++ + } + + // Fix the size because we skipped map entries + obj.nbEntries = uint64(length) +} + +// encodeMapKey takes a reflect.Value and a wafObject and returns a wafObject ready to be considered a map entry. We use +// the function cgoRefPool.AllocWafMapKey to store the key in the wafObject. But first we need to grab the real +// underlying value by recursing through the pointer and interface values. +func (encoder *encoder) encodeMapKey(value reflect.Value, obj *wafObject) error { + value, kind := resolvePointer(value) + + var keyStr string + switch { + case kind == reflect.Invalid: + return errInvalidMapKey + case kind == reflect.String: + keyStr = value.String() + case value.Type() == reflect.TypeOf([]byte(nil)): + keyStr = string(value.Bytes()) + default: + return errInvalidMapKey + } + + encoder.encodeMapKeyFromString(keyStr, obj) + return nil +} + +// encodeMapKeyFromString takes a string and a wafObject and sets the map key attribute on the wafObject to the supplied +// string. The key may be truncated if it exceeds the maximum string size allowed by the encoder. +func (encoder *encoder) encodeMapKeyFromString(keyStr string, obj *wafObject) { + size := len(keyStr) + if size > encoder.stringMaxSize { + keyStr = keyStr[:encoder.stringMaxSize] + encoder.addTruncation(StringTooLong, size) + } + + encoder.cgoRefs.AllocWafMapKey(obj, keyStr) +} + +// encodeArray takes a reflect.Value and a wafObject pointer and iterates on the elements and returns +// a wafObject array of type wafArrayType. The specificities are the following: +// - It will only take the first encoder.containerMaxSize elements of the array +// - Elements producing an error at encoding or null values will be skipped +func (encoder *encoder) encodeArray(value reflect.Value, obj *wafObject, depth int) { + length := value.Len() + + capacity := length + if capacity > encoder.containerMaxSize { + capacity = encoder.containerMaxSize + } + + currIndex := 0 + + objArray := encoder.cgoRefs.AllocWafArray(obj, wafArrayType, uint64(capacity)) + + for i := 0; i < length; i++ { + if currIndex == capacity { + encoder.addTruncation(ContainerTooLarge, length) + break + } + + objElem := &objArray[currIndex] + if err := encoder.encode(value.Index(i), objElem, depth); err != nil { + continue + } + + // If the element is null or invalid it has no impact on the waf execution, therefore we can skip its + // encoding. In this specific case we just overwrite it at the next loop iteration. + if objElem == nil || objElem.IsUnusable() { + continue + } + + currIndex++ + } + + // Fix the size because we skipped map entries + obj.nbEntries = uint64(currIndex) +} + +func (encoder *encoder) addTruncation(reason TruncationReason, size int) { + if encoder.truncations == nil { + encoder.truncations = make(map[TruncationReason][]int, 3) + } + encoder.truncations[reason] = append(encoder.truncations[reason], size) +} + +// mesureObjectDepth traverses the provided object recursively to try and obtain +// the real object depth, but limits itself to about 1ms of time budget, past +// which it'll stop and return whatever it has go to so far. +func (encoder *encoder) measureObjectDepth(obj reflect.Value) { + ctx, cancelCtx := context.WithTimeout(context.Background(), time.Millisecond) + defer cancelCtx() + + depth, _ := depthOf(ctx, obj) + encoder.truncations[ObjectTooDeep] = []int{depth} +} + +// depthOf returns the depth of the provided object. This is 0 for scalar values, +// such as strings. +func depthOf(ctx context.Context, obj reflect.Value) (depth int, err error) { + if err = ctx.Err(); err != nil { + // Timed out, won't go any deeper + return 0, err + } + + obj, kind := resolvePointer(obj) + + //TODO: Remove this once Go 1.21 is the minimum supported version (it adds `builtin.max`) + max := func(x, y int) int { + if x > y { + return x + } + return y + } + + var itemDepth int + switch kind { + case reflect.Array, reflect.Slice: + if obj.Type() == reflect.TypeOf([]byte(nil)) { + // We treat byte slices as strings + return 0, nil + } + for i := 0; i < obj.Len(); i++ { + itemDepth, err = depthOf(ctx, obj.Index(i)) + depth = max(depth, itemDepth) + if err != nil { + break + } + } + return depth + 1, err + case reflect.Map: + for iter := obj.MapRange(); iter.Next(); { + itemDepth, err = depthOf(ctx, iter.Value()) + depth = max(depth, itemDepth) + if err != nil { + break + } + } + return depth + 1, err + case reflect.Struct: + typ := obj.Type() + for i := 0; i < obj.NumField(); i++ { + fieldType := typ.Field(i) + _, usable := getFieldNameFromType(fieldType) + if !usable { + continue + } + + itemDepth, err = depthOf(ctx, obj.Field(i)) + depth = max(depth, itemDepth) + if err != nil { + break + } + } + return depth + 1, err + default: + return 0, nil + } +} + +// resovlePointer attempts to resolve a pointer while limiting the pointer depth +// to be traversed, so that this is not susceptible to an infinite loop when +// provided a self-referencing pointer. +func resolvePointer(obj reflect.Value) (reflect.Value, reflect.Kind) { + kind := obj.Kind() + for limit := 8; limit > 0 && kind == reflect.Pointer || kind == reflect.Interface; limit-- { + if obj.IsNil() { + return obj, kind + } + obj = obj.Elem() + kind = obj.Kind() + } + return obj, kind +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/handle.go b/vendor/github.com/DataDog/go-libddwaf/v2/handle.go new file mode 100644 index 000000000..eb8da0e41 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/handle.go @@ -0,0 +1,222 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "errors" + "fmt" + + "github.com/DataDog/go-libddwaf/v2/internal/noopfree" + "go.uber.org/atomic" +) + +// Handle represents an instance of the WAF for a given ruleset. +type Handle struct { + // diagnostics holds information about rules initialization + diagnostics Diagnostics + + // Lock-less reference counter avoiding blocking calls to the Close() method + // while WAF contexts are still using the WAF handle. Instead, we let the + // release actually happen only when the reference counter reaches 0. + // This can happen either from a request handler calling its WAF context's + // Close() method, or either from the appsec instance calling the WAF + // handle's Close() method when creating a new WAF handle with new rules. + // Note that this means several instances of the WAF can exist at the same + // time with their own set of rules. This choice was done to be able to + // efficiently update the security rules concurrently, without having to + // block the request handlers for the time of the security rules update. + refCounter *atomic.Int32 + + // Instance of the WAF + cHandle wafHandle +} + +// NewHandle creates and returns a new instance of the WAF with the given security rules and configuration +// of the sensitive data obfuscator. The returned handle is nil in case of an error. +// Rules-related metrics, including errors, are accessible with the `RulesetInfo()` method. +func NewHandle(rules any, keyObfuscatorRegex string, valueObfuscatorRegex string) (*Handle, error) { + // The order of action is the following: + // - Open the ddwaf C library + // - Encode the security rules as a ddwaf_object + // - Create a ddwaf_config object and fill the values + // - Run ddwaf_init to create a new handle based on the given rules and config + // - Check for errors and streamline the ddwaf_ruleset_info returned + + if ok, err := Load(); !ok { + return nil, err + // The case where ok == true && err != nil is ignored on purpose, as + // this is out of the scope of NewHandle which only requires a properly + // loaded libddwaf in order to use it + } + + encoder := newMaxEncoder() + obj, err := encoder.Encode(rules) + if err != nil { + return nil, fmt.Errorf("could not encode the WAF ruleset into a WAF object: %w", err) + } + + config := newConfig(&encoder.cgoRefs, keyObfuscatorRegex, valueObfuscatorRegex) + diagnosticsWafObj := new(wafObject) + defer wafLib.wafObjectFree(diagnosticsWafObj) + + cHandle := wafLib.wafInit(obj, config, diagnosticsWafObj, encoder.cgoRefs) + // Upon failure, the WAF may have produced some diagnostics to help signal what went wrong... + var ( + diags *Diagnostics + diagsErr error + ) + if !diagnosticsWafObj.isInvalid() { + diags, diagsErr = decodeDiagnostics(diagnosticsWafObj) + } + + if cHandle == 0 { + // WAF Failed initialization, report the best possible error... + if diags != nil && diagsErr == nil { + // We were able to parse out some diagnostics from the WAF! + err = diags.TopLevelError() + if err != nil { + return nil, fmt.Errorf("could not instantiate the WAF: %w", err) + } + } + return nil, errors.New("could not instantiate the WAF") + } + + // The WAF successfully initialized at this stage... + if diagsErr != nil { + wafLib.wafDestroy(cHandle) + return nil, fmt.Errorf("could not decode the WAF diagnostics: %w", diagsErr) + } + + return &Handle{ + cHandle: cHandle, + refCounter: atomic.NewInt32(1), // We count the handle itself in the counter + diagnostics: *diags, + }, nil +} + +// Diagnostics returns the rules initialization metrics for the current WAF handle +func (handle *Handle) Diagnostics() Diagnostics { + return handle.diagnostics +} + +// Addresses returns the list of addresses the WAF rule is expecting. +func (handle *Handle) Addresses() []string { + return wafLib.wafKnownAddresses(handle.cHandle) +} + +// Update the ruleset of a WAF instance into a new handle on its own +// the previous handle still needs to be closed manually +func (handle *Handle) Update(newRules any) (*Handle, error) { + encoder := newMaxEncoder() + obj, err := encoder.Encode(newRules) + if err != nil { + return nil, fmt.Errorf("could not encode the WAF ruleset into a WAF object: %w", err) + } + + diagnosticsWafObj := new(wafObject) + + cHandle := wafLib.wafUpdate(handle.cHandle, obj, diagnosticsWafObj) + keepAlive(encoder.cgoRefs) + if cHandle == 0 { + return nil, errors.New("could not update the WAF instance") + } + + defer wafLib.wafObjectFree(diagnosticsWafObj) + + if err != nil { // Something is very wrong + return nil, fmt.Errorf("could not decode the WAF ruleset errors: %w", err) + } + + return &Handle{ + cHandle: cHandle, + refCounter: atomic.NewInt32(1), // We count the handle itself in the counter + }, nil +} + +// Close puts the handle in termination state, when all the contexts are closed the handle will be destroyed +func (handle *Handle) Close() { + if handle.addRefCounter(-1) != 0 { + // Either the counter is still positive (this Handle is still referenced), or it had previously + // reached 0 and some other call has done the cleanup already. + return + } + + wafLib.wafDestroy(handle.cHandle) + handle.diagnostics = Diagnostics{} // Data in diagnostics may no longer be valid (e.g: strings from libddwaf) + handle.cHandle = 0 // Makes it easy to spot use-after-free/double-free issues +} + +// retain increments the reference counter of this Handle. Returns true if the +// Handle is still valid, false if it is no longer usable. Calls to retain() +// must be balanced with calls to release() in order to avoid leaking Handles. +func (handle *Handle) retain() bool { + return handle.addRefCounter(1) > 0 +} + +// release decrements the reference counter of this Handle, possibly causing it +// to be completely closed if no other reference to it exist. +func (handle *Handle) release() { + handle.Close() +} + +// addRefCounter adds x to Handle.refCounter. The return valid indicates whether the refCounter reached 0 as part of +// this call or not, which can be used to perform "only-once" activities: +// - result > 0 => the Handle is still usable +// - result == 0 => the handle is no longer usable, ref counter reached 0 as part of this call +// - result == -1 => the handle is no longer usable, ref counter was already 0 previously +func (handle *Handle) addRefCounter(x int32) int32 { + // We use a CAS loop to avoid setting the refCounter to a negative value. + for { + current := handle.refCounter.Load() + if current <= 0 { + // The object had already been released + return -1 + } + + next := current + x + if swapped := handle.refCounter.CompareAndSwap(current, next); swapped { + if next < 0 { + // TODO(romain.marcadier): somehow signal unexpected behavior to the + // caller (panic? error?). We currently clamp to 0 in order to avoid + // causing a customer program crash, but this is the symptom of a bug + // and should be investigated (however this clamping hides the issue). + return 0 + } + return next + } + } +} + +func newConfig(cgoRefs *cgoRefPool, keyObfuscatorRegex string, valueObfuscatorRegex string) *wafConfig { + config := new(wafConfig) + *config = wafConfig{ + limits: wafConfigLimits{ + maxContainerDepth: wafMaxContainerDepth, + maxContainerSize: wafMaxContainerSize, + maxStringLength: wafMaxStringLength, + }, + obfuscator: wafConfigObfuscator{ + keyRegex: cgoRefs.AllocCString(keyObfuscatorRegex), + valueRegex: cgoRefs.AllocCString(valueObfuscatorRegex), + }, + // Prevent libddwaf from freeing our Go-memory-allocated ddwaf_objects + freeFn: noopfree.NoopFreeFn, + } + return config +} + +func goRunError(rc wafReturnCode) error { + switch rc { + case wafErrInternal: + return ErrInternal + case wafErrInvalidObject: + return ErrInvalidObject + case wafErrInvalidArgument: + return ErrInvalidArgument + default: + return fmt.Errorf("unknown waf return code %d", int(rc)) + } +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/.version b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/.version new file mode 100644 index 000000000..71bd5d9ee --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/.version @@ -0,0 +1 @@ +1.16.0 \ No newline at end of file diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/README.md b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/README.md new file mode 100644 index 000000000..010d41bf4 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/README.md @@ -0,0 +1,21 @@ +## Embedded WAF libraries + +This directory contains Datadog's WAF static libraries taken from the releases +of https://github.com/DataDog/libddwaf + +### Updating + +From the root of the repository, run: + +```console +./_tools/libddwaf-updater/update.sh +Will upgrade from v1.14.0 to v1.15.0 +... downloaded /Datadog/go-libddwaf/include/ddwaf.h +... downloaded /Datadog/go-libddwaf/lib/darwin-arm64/libddwaf.dylib +... downloaded /Datadog/go-libddwaf/lib/darwin-amd64/libddwaf.dylib +... downloaded /Datadog/go-libddwaf/lib/linux-arm64/libddwaf.so +... downloaded /Datadog/go-libddwaf/lib/linux-amd64/libddwaf.so +... downloaded /Datadog/go-libddwaf/lib/linux-armv7/libddwaf.so +... downloaded /Datadog/go-libddwaf/lib/linux-i386/libddwaf.so +All done! Don't forget to check in changes to include/ and lib/, check the libddwaf upgrade guide to update bindings! +``` diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/vendor.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/doc.go similarity index 59% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/vendor.go rename to vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/doc.go index 01b5a67b0..5f44fb80d 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/vendor.go +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/doc.go @@ -1,8 +1,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. +// Copyright 2016-present Datadog, Inc. -// Package include is required to help go tools support vendoring. -// DO NOT REMOVE -package include +// Package lib provides a built-in WAF library version for the relevant runtime platform. +package lib diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib.go new file mode 100644 index 000000000..93f41dbe8 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib.go @@ -0,0 +1,49 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build ((darwin && (amd64 || arm64)) || (linux && (amd64 || arm64))) && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package lib + +import ( + "fmt" + "os" + + _ "embed" +) + +//go:embed .version +var EmbeddedWAFVersion string + +func DumpEmbeddedWAF() (path string, err error) { + file, err := os.CreateTemp("", embedNamePattern) + if err != nil { + return path, fmt.Errorf("error creating temp file: %w", err) + } + path = file.Name() + + defer func() { + if closeErr := file.Close(); closeErr != nil { + if err != nil { + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = fmt.Errorf("%w; along with an error while releasingclosing the temporary file: %v", err, closeErr) + } else { + err = fmt.Errorf("error closing file: %w", closeErr) + } + } + if path != "" && err != nil { + if rmErr := os.Remove(path); rmErr != nil { + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = fmt.Errorf("%w; along with an error while releasingclosing the temporary file: %v", err, rmErr) + } + } + }() + + if err := os.WriteFile(file.Name(), libddwaf, 0400); err != nil { + return path, fmt.Errorf("error writing file: %w", err) + } + + return path, nil +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_amd64.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_amd64.go new file mode 100644 index 000000000..8106353c5 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_amd64.go @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build darwin && amd64 && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package lib + +// THIS FILE IS AUTOGENERATED. DO NOT EDIT. + +import _ "embed" // Needed for go:embed + +//go:embed libddwaf-darwin-amd64.dylib +var libddwaf []byte + +const embedNamePattern = "libddwaf-*.dylib" diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_arm64.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_arm64.go new file mode 100644 index 000000000..c9d286308 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_darwin_arm64.go @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build darwin && arm64 && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package lib + +// THIS FILE IS AUTOGENERATED. DO NOT EDIT. + +import _ "embed" // Needed for go:embed + +//go:embed libddwaf-darwin-arm64.dylib +var libddwaf []byte + +const embedNamePattern = "libddwaf-*.dylib" diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_amd64.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_amd64.go new file mode 100644 index 000000000..ad706c068 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_amd64.go @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux && amd64 && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package lib + +// THIS FILE IS AUTOGENERATED. DO NOT EDIT. + +import _ "embed" // Needed for go:embed + +//go:embed libddwaf-linux-amd64.so +var libddwaf []byte + +const embedNamePattern = "libddwaf-*.so" diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_arm64.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_arm64.go new file mode 100644 index 000000000..7aa8c1bdc --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/lib_linux_arm64.go @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux && arm64 && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package lib + +// THIS FILE IS AUTOGENERATED. DO NOT EDIT. + +import _ "embed" // Needed for go:embed + +//go:embed libddwaf-linux-arm64.so +var libddwaf []byte + +const embedNamePattern = "libddwaf-*.so" diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-amd64.dylib b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-amd64.dylib new file mode 100644 index 0000000000000000000000000000000000000000..c0c731615ff3502191c4708d1db0307cea456b3b GIT binary patch literal 1566256 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%0Abk0F`ZvX<9D^z*BHiJto;$!1Uv4HI3 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�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%0APVZV-CRa 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-arm64.dylib b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-darwin-arm64.dylib new file mode 100644 index 0000000000000000000000000000000000000000..ef81c347ca75447e38b318d88b5dec10c1bfd40d GIT binary patch literal 1436976 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
4(U-d&Mq4rlAVk{ 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 ZSYPb1sx-B@T_l!gowLiF^?%~-e*m@s2k!s? literal 0 HcmV?d00001 diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-amd64.so b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-amd64.so new file mode 100644 index 0000000000000000000000000000000000000000..7f3b25ae605be2e8961b137f100aeefd41b28e93 GIT binary patch literal 2136144 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�R9&65oY~Sp2^@M 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 z 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�nuYRk2{b(O@@ze`_-=8}$z5i-* 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 literal 0 HcmV?d00001 diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/ddwaf.h b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/ddwaf.h similarity index 63% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/ddwaf.h rename to vendor/github.com/DataDog/go-libddwaf/v2/internal/log/ddwaf.h index 62b12f4a6..b52b6028d 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include/ddwaf.h +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/ddwaf.h @@ -4,10 +4,17 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#ifndef pw_h -#define pw_h +#ifndef DDWAF_H +#define DDWAF_H #ifdef __cplusplus +namespace ddwaf{ +class waf; +class context_wrapper; +} // namespace ddwaf +using ddwaf_handle = ddwaf::waf *; +using ddwaf_context = ddwaf::context_wrapper *; + extern "C" { #endif @@ -29,16 +36,22 @@ extern "C" typedef enum { DDWAF_OBJ_INVALID = 0, - /** Value shall be decoded as a int64_t (or int32_t on 32bits platforms). **/ + // 64-bit signed integer type DDWAF_OBJ_SIGNED = 1 << 0, - /** Value shall be decoded as a uint64_t (or uint32_t on 32bits platforms). **/ + // 64-bit unsigned integer type DDWAF_OBJ_UNSIGNED = 1 << 1, - /** Value shall be decoded as a UTF-8 string of length nbEntries. **/ + // UTF-8 string of length nbEntries DDWAF_OBJ_STRING = 1 << 2, - /** Value shall be decoded as an array of ddwaf_object of length nbEntries, each item having no parameterName. **/ + // Array of ddwaf_object of length nbEntries, each item having no parameterName DDWAF_OBJ_ARRAY = 1 << 3, - /** Value shall be decoded as an array of ddwaf_object of length nbEntries, each item having a parameterName. **/ + // Array of ddwaf_object of length nbEntries, each item having a parameterName DDWAF_OBJ_MAP = 1 << 4, + // Boolean type + DDWAF_OBJ_BOOL = 1 << 5, + // 64-bit float (or double) type + DDWAF_OBJ_FLOAT = 1 << 6, + // Null type, only used for its semantical value + DDWAF_OBJ_NULL = 1 << 7, } DDWAF_OBJ_TYPE; /** @@ -51,9 +64,8 @@ typedef enum DDWAF_ERR_INTERNAL = -3, DDWAF_ERR_INVALID_OBJECT = -2, DDWAF_ERR_INVALID_ARGUMENT = -1, - DDWAF_GOOD = 0, - DDWAF_MONITOR = 1, - DDWAF_BLOCK = 2 + DDWAF_OK = 0, + DDWAF_MATCH = 1, } DDWAF_RET_CODE; /** @@ -71,12 +83,7 @@ typedef enum DDWAF_LOG_OFF, } DDWAF_LOG_LEVEL; -#ifdef __cplusplus -class PowerWAF; -class PWAdditive; -using ddwaf_handle = PowerWAF *; -using ddwaf_context = PWAdditive *; -#else +#ifndef __cplusplus typedef struct _ddwaf_handle* ddwaf_handle; typedef struct _ddwaf_context* ddwaf_context; #endif @@ -84,8 +91,6 @@ typedef struct _ddwaf_context* ddwaf_context; typedef struct _ddwaf_object ddwaf_object; typedef struct _ddwaf_config ddwaf_config; typedef struct _ddwaf_result ddwaf_result; -typedef struct _ddwaf_version ddwaf_version; -typedef struct _ddwaf_ruleset_info ddwaf_ruleset_info; /** * @struct ddwaf_object * @@ -102,11 +107,20 @@ struct _ddwaf_object uint64_t uintValue; int64_t intValue; ddwaf_object* array; + bool boolean; + double f64; }; uint64_t nbEntries; DDWAF_OBJ_TYPE type; }; +/** + * @typedef ddwaf_object_free_fn + * + * Type of the function to free ddwaf::objects. + **/ +typedef void (*ddwaf_object_free_fn)(ddwaf_object *object); + /** * @struct ddwaf_config * @@ -114,7 +128,7 @@ struct _ddwaf_object **/ struct _ddwaf_config { - struct { + struct _ddwaf_config_limits { /** Maximum size of ddwaf::object containers. */ uint32_t max_container_size; /** Maximum depth of ddwaf::object containers. */ @@ -124,12 +138,17 @@ struct _ddwaf_config } limits; /** Obfuscator regexes - the strings are owned by the caller */ - struct { + struct _ddwaf_config_obfuscator { /** Regular expression for key-based obfuscation */ const char *key_regex; /** Regular expression for value-based obfuscation */ const char *value_regex; } obfuscator; + + /** Function to free the ddwaf::object provided to the context during calls + * to ddwaf_run. If the value of this function is NULL, the objects will + * not be freed. The default value should be ddwaf_object_free. */ + ddwaf_object_free_fn free_fn; }; /** @@ -141,49 +160,16 @@ struct _ddwaf_result { /** Whether there has been a timeout during the operation **/ bool timeout; - /** Run result in JSON format **/ - const char* data; + /** Array of events generated, this is guaranteed to be an array **/ + ddwaf_object events; + /** Array of actions generated, this is guaranteed to be an array **/ + ddwaf_object actions; + /** Map containing all derived objects in the format (address, value) **/ + ddwaf_object derivatives; /** Total WAF runtime in nanoseconds **/ uint64_t total_runtime; }; -/** - * @ddwaf_version - * - * Structure containing the version of the WAF following semver. - **/ -struct _ddwaf_version -{ - uint16_t major; - uint16_t minor; - uint16_t patch; -}; - -/** - * @ddwaf_ruleset_info - * - * Structure containing diagnostics on the provided ruleset. - * */ -struct _ddwaf_ruleset_info -{ - /** Number of rules successfully loaded **/ - uint16_t loaded; - /** Number of rules which failed to parse **/ - uint16_t failed; - /** Map from an error string to an array of all the rule ids for which - * that error was raised. {error: [rule_ids]} **/ - ddwaf_object errors; - /** Ruleset version **/ - const char *version; -}; - -/** - * @typedef ddwaf_object_free_fn - * - * Type of the function to free ddwaf::objects. - **/ -typedef void (*ddwaf_object_free_fn)(ddwaf_object *object); - /** * @typedef ddwaf_log_cb * @@ -205,14 +191,35 @@ typedef void (*ddwaf_log_cb)( * * Initialize a ddwaf instance * - * @param rule ddwaf::object containing the patterns to be used by the WAF. (nonnull) + * @param ruleset ddwaf::object map containing rules, exclusions, rules_override and rules_data. (nonnull) * @param config Optional configuration of the WAF. (nullable) - * @param info Optional ruleset parsing diagnostics. (nullable) + * @param diagnostics Optional ruleset parsing diagnostics. (nullable) + * + * @return Handle to the WAF instance or NULL on error. + * + * @note If config is NULL, default values will be used, including the default + * free function (ddwaf_object_free). + * + * @note If ruleset is NULL, the diagnostics object will not be initialised. + **/ +ddwaf_handle ddwaf_init(const ddwaf_object *ruleset, + const ddwaf_config* config, ddwaf_object *diagnostics); + +/** + * ddwaf_update * - * @return Handle to the WAF instance. + * Update a ddwaf instance + * + * @param ruleset ddwaf::object map containing rules, exclusions, rules_override and rules_data. (nonnull) + * @param diagnostics Optional ruleset parsing diagnostics. (nullable) + * + * @return Handle to the new WAF instance or NULL if there was an error processing the ruleset. + * + * @note If handle or ruleset are NULL, the diagnostics object will not be initialised. + * @note This function is not thread-safe **/ -ddwaf_handle ddwaf_init(const ddwaf_object *rule, - const ddwaf_config* config, ddwaf_ruleset_info *info); +ddwaf_handle ddwaf_update(ddwaf_handle handle, const ddwaf_object *ruleset, + ddwaf_object *diagnostics); /** * ddwaf_destroy @@ -222,42 +229,39 @@ ddwaf_handle ddwaf_init(const ddwaf_object *rule, * @param Handle to the WAF instance. */ void ddwaf_destroy(ddwaf_handle handle); + /** - * ddwaf_ruleset_info_free - * - * Free the memory associated with the ruleset info structure. + * ddwaf_known_addresses * - * @param info Ruleset info to free. - * */ -void ddwaf_ruleset_info_free(ddwaf_ruleset_info *info); -/** - * ddwaf_required_addresses + * Get an array of known (root) addresses used by rules, exclusion filters and + * processors. This array contains both required and optional addresses. A more + * accurate distinction between required and optional addresses is provided + * within the diagnostics. * - * Get a list of required (root) addresses. The memory is owned by the WAF and - * should not be freed. + * The memory is owned by the WAF and should not be freed. * * @param Handle to the WAF instance. * @param size Output parameter in which the size will be returned. The value of - * size will be 0 if the return value is nullptr. - * @return NULL if error, otherwise a pointer to an array with size elements. + * size will be 0 if the return value is NULL. + * @return NULL if empty, otherwise a pointer to an array with size elements. + * + * @Note The returned array should be considered invalid after calling ddwaf_destroy + * on the handle used to obtain it. **/ -const char* const* ddwaf_required_addresses(const ddwaf_handle handle, uint32_t *size); +const char* const* ddwaf_known_addresses(const ddwaf_handle handle, uint32_t *size); + /** * ddwaf_context_init * * Context object to perform matching using the provided WAF instance. * * @param handle Handle of the WAF instance containing the ruleset definition. (nonnull) - * @param obj_free Function to free the ddwaf::object provided to the context - * during calls to ddwaf_run. If the value of this function is - * NULL, the objects will not be freed. By default the value of - * this parameter should be ddwaf_object_free. - * + * @return Handle to the context instance. * * @note The WAF instance needs to be valid for the lifetime of the context. **/ -ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_object_free_fn obj_free); +ddwaf_context ddwaf_context_init(const ddwaf_handle handle); /** * ddwaf_run @@ -267,12 +271,24 @@ ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_object_free_fn * @param context WAF context to be used in this run, this will determine the * ruleset which will be used and it will also ensure that * parameters are taken into account across runs (nonnull) - * @param data Data on which to perform the pattern matching. This data will be - * stored by the context and used across multiple calls to this - * function. Once the context is destroyed, the used-defined free - * function will be used to free the data provided. Note that the - * data passed must be valid until the destruction of the context. - * (nonull) + * + * @param persistent_data Data on which to perform the pattern matching. This + * data will be stored by the context and used across multiple calls to this + * function. Once the context is destroyed, the used-defined free function + * will be used to free the data provided. Note that the data passed must be + * valid until the destruction of the context. The object must be a map of + * {string, } in which each key represents the relevant address + * associated to the value, which can be of an arbitrary type. This parameter + * can be null if ephemeral data is provided. + * + * @param ephemeral_data Data on which to perform the pattern matching. This + * data will not be cached by the WAF. Matches arising from this data will + * also not be cached at any level. The data will be freed at the end of the + * call to ddwaf_run. The object must be a map of {string, } in which + * each key represents the relevant address associated to the value, which + * can be of an arbitrary type. This parameter can be null if persistent data + * is provided. + * * @param result Structure containing the result of the operation. (nullable) * @param timeout Maximum time budget in microseconds. * @@ -282,16 +298,32 @@ ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_object_free_fn * @error DDWAF_ERR_INVALID_OBJECT The data provided didn't match the desired * structure or contained invalid objects, the * data will be freed by this function. - * @error DDWAF_ERR_TIMEOUT The operation timed out, the data will be owned by - * the context and freed during destruction. * @error DDWAF_ERR_INTERNAL There was an unexpected error and the operation did * not succeed. The state of the WAF is undefined if * this error is produced and the ownership of the * data is unknown. The result structure will not be * filled if this error occurs. + * + * Notes on addresses: + * - Within a single run, addresses provided should be unique. + * If duplicate persistent addresses are provided: + * - Within the same batch, the latest one in the structure will be the one + * used for evaluation. + * - Within two different batches, the second batch will only use the new data. + * + * Ephemeral addresses are designed to be duplicated across batches, but if + * duplicate addresses are provided within the same batch, the latest one seen + * will be the one used. + * + * Duplicate addresses of different types (ephemeral, persistent), are not + * permitted. An existing address will never be replaced by a duplicate one + * of a different type, but it doesn't result in a critical failure. Due to the + * nature of ephemerals, an ephemeral address can be replaced in a subsequent + * batch by a persistent address, however taking advantage of this is not + * recommended and might be explicitly rejected in the future. **/ -DDWAF_RET_CODE ddwaf_run(ddwaf_context context, ddwaf_object *data, - ddwaf_result *result, uint64_t timeout); +DDWAF_RET_CODE ddwaf_run(ddwaf_context context, ddwaf_object *persistent_data, + ddwaf_object *ephemeral_data, ddwaf_result *result, uint64_t timeout); /** * ddwaf_context_destroy @@ -323,6 +355,18 @@ void ddwaf_result_free(ddwaf_result *result); **/ ddwaf_object* ddwaf_object_invalid(ddwaf_object *object); +/** + * ddwaf_object_null + * + * Creates an null object. Provides a different semantical value to invalid as + * it can be used to signify that a value is null rather than of an unknown type. + * + * @param object Object to perform the operation on. (nonnull) + * + * @return A pointer to the passed object or NULL if the operation failed. + **/ +ddwaf_object* ddwaf_object_null(ddwaf_object *object); + /** * ddwaf_object_string * @@ -364,32 +408,30 @@ ddwaf_object* ddwaf_object_stringl(ddwaf_object *object, const char *string, siz ddwaf_object* ddwaf_object_stringl_nc(ddwaf_object *object, const char *string, size_t length); /** - * ddwaf_object_unsigned + * ddwaf_object_string_from_unsigned * * Creates an object using an unsigned integer (64-bit). The resulting object - * will contain a string created using the integer provided. This is the - * preferred method for passing an unsigned integer to the WAF. + * will contain a string created using the integer provided. * * @param object Object to perform the operation on. (nonnull) * @param value Integer to initialise the object with. * * @return A pointer to the passed object or NULL if the operation failed. **/ -ddwaf_object* ddwaf_object_unsigned(ddwaf_object *object, uint64_t value); +ddwaf_object* ddwaf_object_string_from_unsigned(ddwaf_object *object, uint64_t value); /** - * ddwaf_object_signed + * ddwaf_object_string_from_signed * * Creates an object using a signed integer (64-bit). The resulting object - * will contain a string created using the integer provided. This is the - * preferred method for passing a signed integer to the WAF. + * will contain a string created using the integer provided. * * @param object Object to perform the operation on. (nonnull) * @param value Integer to initialise the object with. * * @return A pointer to the passed object or NULL if the operation failed. **/ -ddwaf_object* ddwaf_object_signed(ddwaf_object *object, int64_t value); +ddwaf_object* ddwaf_object_string_from_signed(ddwaf_object *object, int64_t value); /** * ddwaf_object_unsigned_force @@ -402,7 +444,7 @@ ddwaf_object* ddwaf_object_signed(ddwaf_object *object, int64_t value); * * @return A pointer to the passed object or NULL if the operation failed. **/ -ddwaf_object* ddwaf_object_unsigned_force(ddwaf_object *object, uint64_t value); +ddwaf_object* ddwaf_object_unsigned(ddwaf_object *object, uint64_t value); /** * ddwaf_object_signed_force @@ -415,7 +457,33 @@ ddwaf_object* ddwaf_object_unsigned_force(ddwaf_object *object, uint64_t value); * * @return A pointer to the passed object or NULL if the operation failed. **/ -ddwaf_object* ddwaf_object_signed_force(ddwaf_object *object, int64_t value); +ddwaf_object* ddwaf_object_signed(ddwaf_object *object, int64_t value); + +/** + * ddwaf_object_bool + * + * Creates an object using a boolean, the resulting object will contain a + * boolean as opposed to a string. + * + * @param object Object to perform the operation on. (nonnull) + * @param value Boolean to initialise the object with. + * + * @return A pointer to the passed object or NULL if the operation failed. + **/ +ddwaf_object* ddwaf_object_bool(ddwaf_object *object, bool value); + +/** + * ddwaf_object_float + * + * Creates an object using a double, the resulting object will contain a + * double as opposed to a string. + * + * @param object Object to perform the operation on. (nonnull) + * @param value Double to initialise the object with. + * + * @return A pointer to the passed object or NULL if the operation failed. + **/ +ddwaf_object* ddwaf_object_float(ddwaf_object *object, double value); /** * ddwaf_object_array @@ -503,7 +571,7 @@ bool ddwaf_object_map_addl_nc(ddwaf_object *map, const char *key, size_t length, * * @return The object type of DDWAF_OBJ_INVALID if NULL. **/ -DDWAF_OBJ_TYPE ddwaf_object_type(ddwaf_object *object); +DDWAF_OBJ_TYPE ddwaf_object_type(const ddwaf_object *object); /** * ddwaf_object_size @@ -514,7 +582,7 @@ DDWAF_OBJ_TYPE ddwaf_object_type(ddwaf_object *object); * * @return The object size or 0 if the object is not a container (array, map). **/ -size_t ddwaf_object_size(ddwaf_object *object); +size_t ddwaf_object_size(const ddwaf_object *object); /** * ddwaf_object_length @@ -525,7 +593,7 @@ size_t ddwaf_object_size(ddwaf_object *object); * * @return The string length or 0 if the object is not a string. **/ -size_t ddwaf_object_length(ddwaf_object *object); +size_t ddwaf_object_length(const ddwaf_object *object); /** * ddwaf_object_get_key @@ -538,7 +606,7 @@ size_t ddwaf_object_length(ddwaf_object *object); * * @return The key of the object or NULL if the object doesn't contain a key. **/ -const char* ddwaf_object_get_key(ddwaf_object *object, size_t *length); +const char* ddwaf_object_get_key(const ddwaf_object *object, size_t *length); /** * ddwaf_object_get_string @@ -551,7 +619,7 @@ const char* ddwaf_object_get_key(ddwaf_object *object, size_t *length); * * @return The string of the object or NULL if the object is not a string. **/ -const char* ddwaf_object_get_string(ddwaf_object *object, size_t *length); +const char* ddwaf_object_get_string(const ddwaf_object *object, size_t *length); /** * ddwaf_object_get_unsigned @@ -562,7 +630,7 @@ const char* ddwaf_object_get_string(ddwaf_object *object, size_t *length); * * @return The integer or 0 if the object is not an unsigned. **/ -uint64_t ddwaf_object_get_unsigned(ddwaf_object *object); +uint64_t ddwaf_object_get_unsigned(const ddwaf_object *object); /** * ddwaf_object_get_signed @@ -573,7 +641,29 @@ uint64_t ddwaf_object_get_unsigned(ddwaf_object *object); * * @return The integer or 0 if the object is not a signed. **/ -int64_t ddwaf_object_get_signed(ddwaf_object *object); +int64_t ddwaf_object_get_signed(const ddwaf_object *object); + +/** + * ddwaf_object_get_float + * + * Returns the float64 (double) contained within the object. + * + * @param object The object from which to get the float. + * + * @return The float or 0.0 if the object is not a float. + **/ +double ddwaf_object_get_float(const ddwaf_object *object); + +/** + * ddwaf_object_get_bool + * + * Returns the boolean contained within the object. + * + * @param object The object from which to get the boolean. + * + * @return The boolean or false if the object is not a boolean. + **/ +bool ddwaf_object_get_bool(const ddwaf_object *object); /** * ddwaf_object_get_index @@ -586,7 +676,7 @@ int64_t ddwaf_object_get_signed(ddwaf_object *object); * @return The requested object or NULL if the index is out of bounds or the * object is not a container. **/ -ddwaf_object* ddwaf_object_get_index(ddwaf_object *object, size_t index); +const ddwaf_object* ddwaf_object_get_index(const ddwaf_object *object, size_t index); /** @@ -601,9 +691,9 @@ void ddwaf_object_free(ddwaf_object *object); * * Return the version of the library * - * @param version Version structure following semver + * @return version Version string, note that this should not be freed **/ -void ddwaf_get_version(ddwaf_version *version); +const char *ddwaf_get_version(); /** * ddwaf_set_log_cb @@ -614,6 +704,8 @@ void ddwaf_get_version(ddwaf_version *version); * @param min_level The minimum logging level for which to relay messages * * @return whether the operation succeeded or not + * + * @note This function is not thread-safe **/ bool ddwaf_set_log_cb(ddwaf_log_cb cb, DDWAF_LOG_LEVEL min_level); @@ -621,4 +713,4 @@ bool ddwaf_set_log_cb(ddwaf_log_cb cb, DDWAF_LOG_LEVEL min_level); } #endif /* __cplusplus */ -#endif /* pw_h */ +#endif /*DDWAF_H */ diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/gostring.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/gostring.go new file mode 100644 index 000000000..cf85a4b93 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/gostring.go @@ -0,0 +1,24 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package log + +import "unsafe" + +// gostring copies a char* to a Go string. +func gostring(ptr *byte) string { + if ptr == nil { + return "" + } + var length int + for { + if *(*byte)(unsafe.Add(unsafe.Pointer(ptr), uintptr(length))) == '\x00' { + break + } + length++ + } + //string builtin copies the slice + return string(unsafe.Slice(ptr, length)) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log.go new file mode 100644 index 000000000..447ac711f --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log.go @@ -0,0 +1,97 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package log + +import ( + "fmt" + "log" + "os" + "regexp" + "strings" +) + +// Level replicates the definition of `DDWAF_LOG_LEVEL` from `ddwaf.h`. +type Level int + +const ( + LevelTrace Level = iota + LevelDebug + LevelInfo + LevelWarning + LevelError + LevelOff +) + +// LevelNamed returns the log level corresponding to the given name, or LevelOff +// if the name corresponds to no known log level. +func LevelNamed(name string) Level { + switch strings.ToLower(name) { + case "trace": + return LevelTrace + case "debug": + return LevelDebug + case "info": + return LevelInfo + case "warn", "warning": + return LevelWarning + case "error": + return LevelError + case "off": + return LevelOff + default: + return LevelOff + } +} + +func (l Level) String() string { + switch l { + case LevelTrace: + return "TRACE" + case LevelDebug: + return "DEBUG" + case LevelInfo: + return "INFO" + case LevelWarning: + return "WARN" + case LevelError: + return "ERROR" + case LevelOff: + return "OFF" + default: + return fmt.Sprintf("0x%X", uintptr(l)) + } +} + +var filter *regexp.Regexp + +func logMessage(level Level, function, file string, line uint, message string) { + entry := fmt.Sprintf("[%s] libddwaf @ %s:%d (%s): %s", level, file, line, function, message) + + if filter != nil && !filter.MatchString(entry) { + return + } + + log.Println(entry) +} + +const EnvVarLogLevel = "DD_APPSEC_WAF_LOG_LEVEL" + +func init() { + const envVarFilter = "DD_APPSEC_WAF_LOG_FILTER" + + if val := os.Getenv(EnvVarLogLevel); val == "" { + // No log level configured, don't even attempt parsing the regexp. + return + } + + if val := os.Getenv(envVarFilter); val != "" { + var err error + filter, err = regexp.Compile(val) + if err != nil { + log.Fatalf("invalid %s value: %v", envVarFilter, err) + } + } +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_cgo.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_cgo.go new file mode 100644 index 000000000..aeca4ebff --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_cgo.go @@ -0,0 +1,35 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build cgo && !datadog.no_waf + +package log + +// #include "./ddwaf.h" +// extern void ddwafLogCallbackFn( +// DDWAF_LOG_LEVEL level, +// char* function, +// char* file, +// unsigned line, +// char* message, +// uint64_t message_len +// ); +import "C" +import "unsafe" + +// CallbackFunctionPointer returns a pointer to the log callback function which +// can be used with libddwaf. +func CallbackFunctionPointer() uintptr { + return uintptr(C.ddwafLogCallbackFn) +} + +//export ddwafLogCallbackFn +func ddwafLogCallbackFn(level C.DDWAF_LOG_LEVEL, fnPtr, filePtr *C.char, line C.unsigned, msgPtr *C.char, _ C.uint64_t) { + function := gostring((*byte)(unsafe.Pointer(fnPtr))) + file := gostring((*byte)(unsafe.Pointer(filePtr))) + message := gostring((*byte)(unsafe.Pointer(msgPtr))) + + logMessage(Level(level), function, file, uint(line), message) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_purego.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_purego.go new file mode 100644 index 000000000..24ab082e1 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_purego.go @@ -0,0 +1,36 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build !cgo && (darwin || freebsd) && !datadog.no_waf && !go1.23 + +package log + +import ( + "sync" + + "github.com/ebitengine/purego" +) + +var ( + once sync.Once + functionPointer uintptr +) + +// CallbackFunctionPointer returns a pointer to the log callback function which +// can be used with libddwaf. +func CallbackFunctionPointer() uintptr { + once.Do(func() { + functionPointer = purego.NewCallback(ddwafLogCallbackFn) + }) + return functionPointer +} + +func ddwafLogCallbackFn(level Level, fnPtr, filePtr *byte, line uint, msgPtr *byte, _ uint64) { + function := gostring(fnPtr) + file := gostring(filePtr) + message := gostring(msgPtr) + + logMessage(level, function, file, line, message) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_unsupported.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_unsupported.go new file mode 100644 index 000000000..78fff4ce0 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/log/log_unsupported.go @@ -0,0 +1,14 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build (!cgo && ((!darwin && !freebsd) || go1.23)) || datadog.no_waf + +package log + +// CallbackFunctionPointer returns a NULL pointer since this particular platform +// configuration is not supported by purego, and cgo is disabled. +func CallbackFunctionPointer() uintptr { + return 0 +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.go new file mode 100644 index 000000000..98dffcf05 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.go @@ -0,0 +1,15 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package noopfree provides a noop-ed free function. A separate package is +// needed to avoid the special go-build case with CGO enabled where it compiles +// .s files with CC instead of the Go assembler that we want here. +package noopfree + +import "unsafe" + +//go:linkname _noop_free_v2 _noop_free_v2 +var _noop_free_v2 byte +var NoopFreeFn uintptr = uintptr(unsafe.Pointer(&_noop_free_v2)) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64/vendor.go b/vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.s similarity index 61% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64/vendor.go rename to vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.s index 8ef856933..25c3def33 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64/vendor.go +++ b/vendor/github.com/DataDog/go-libddwaf/v2/internal/noopfree/noopfree.s @@ -1,8 +1,9 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. +// Copyright 2016-present Datadog, Inc. -// Package vendor is required to help go tools support vendoring. -// DO NOT REMOVE -package vendor +#include "textflag.h" + +TEXT _noop_free_v2(SB), NOSPLIT, $0-0 + RET diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/safe.go b/vendor/github.com/DataDog/go-libddwaf/v2/safe.go new file mode 100644 index 000000000..22c155e90 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/safe.go @@ -0,0 +1,68 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "fmt" + "reflect" + "runtime" + + "github.com/pkg/errors" +) + +// PanicError is an error type wrapping a recovered panic value that happened +// during a function call. Such error must be considered unrecoverable and be +// used to try to gracefully abort. Keeping using this package after such an +// error is unreliable and the caller must rather stop using the library. +// Examples include safety checks errors. +type PanicError struct { + // The recovered panic error while executing the function `in`. + Err error + // The function symbol name that was given to `tryCall()`. + in string +} + +func newPanicError(in func() error, err error) *PanicError { + return &PanicError{ + in: runtime.FuncForPC(reflect.ValueOf(in).Pointer()).Name(), + Err: err, + } +} + +// Unwrap the error and return it. +// Required by errors.Is and errors.As functions. +func (e *PanicError) Unwrap() error { + return e.Err +} + +// Error returns the error string representation. +func (e *PanicError) Error() string { + return fmt.Sprintf("panic while executing %s: %#+v", e.in, e.Err) +} + +// tryCall calls function `f` and recovers from any panic occurring while it +// executes, returning it in a `PanicError` object type. +func tryCall(f func() error) (err error) { + defer func() { + r := recover() + if r == nil { + // Note that panic(nil) matches this case and cannot be really tested for. + return + } + + switch actual := r.(type) { + case error: + err = errors.WithStack(actual) + case string: + err = errors.New(actual) + default: + err = errors.Errorf("%v", r) + } + + err = newPanicError(f, err) + }() + return f() +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_cgo.go b/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_cgo.go new file mode 100644 index 000000000..46b4d95a0 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_cgo.go @@ -0,0 +1,21 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build cgo && linux && !go1.23 && !datadog.no_waf + +package waf + +/* +// Needed otherwise cielf call is optimized away or the builtin version is used +#cgo CFLAGS: -O0 +#cgo LDFLAGS: -lm +float __attribute__((__noinline__)) ceilf(float arg); +*/ +import "C" + +// Required because libddwaf uses ceilf located in libm +// This forces CGO to link with libm, from there since +// libm is loaded, we can dlopen the waf without issues +var _ = C.ceilf(2.3) diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_purego.go b/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_purego.go new file mode 100644 index 000000000..223c500af --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/symbols_linux_purego.go @@ -0,0 +1,15 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build !cgo && appsec && linux && !go1.23 && !datadog.no_waf + +package waf + +// Adds a dynamic import for libm.so because libddwaf needs the ceilf symbol +// This mechanic only works when CGO is not enabled +// +//go:cgo_import_dynamic purego_ceilf ceilf "libm.so.6" +//go:cgo_import_dynamic _ _ "libm.so.6" +var purego_ceilf uintptr diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf.go new file mode 100644 index 000000000..5ee702370 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf.go @@ -0,0 +1,185 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "errors" + "fmt" + "sync" + + "github.com/hashicorp/go-multierror" +) + +// Diagnostics stores the information - provided by the WAF - about WAF rules initialization. +type Diagnostics struct { + Rules *DiagnosticEntry + CustomRules *DiagnosticEntry + Exclusions *DiagnosticEntry + RulesOverrides *DiagnosticEntry + RulesData *DiagnosticEntry + Processors *DiagnosticEntry + Scanners *DiagnosticEntry + Version string +} + +// TopLevelErrors returns the list of top-level errors reported by the WAF on any of the Diagnostics +// entries, rolled up into a single error value. Returns nil if no top-level errors were reported. +// Individual, item-level errors might still exist. +func (d *Diagnostics) TopLevelError() error { + fields := map[string]*DiagnosticEntry{ + "rules": d.Rules, + "custom_rules": d.CustomRules, + "exclusions": d.Exclusions, + "rules_override": d.RulesOverrides, + "rules_data": d.RulesData, + "processors": d.Processors, + "scanners": d.Scanners, + } + + var err *multierror.Error + for field, entry := range fields { + if entry == nil || entry.Error == "" { + // No entry or no error => we're all good. + continue + } + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = multierror.Append(err, fmt.Errorf("in %#v: %s", field, entry.Error)) + } + + return err.ErrorOrNil() +} + +// DiagnosticEntry stores the information - provided by the WAF - about loaded and failed rules +// for a specific entry in the WAF ruleset +type DiagnosticEntry struct { + Addresses *DiagnosticAddresses + Errors map[string][]string // Item-level errors (map of error message to entity identifiers or index:#) + Error string // If the entire entry was in error (e.g: invalid format) + Loaded []string // Successfully loaded entity identifiers (or index:#) + Failed []string // Failed entity identifiers (or index:#) +} + +// DiagnosticAddresses stores the information - provided by the WAF - about the known addresses and +// whether they are required or optional. Addresses used by WAF rules are always required. Addresses +// used by WAF exclusion filters may be required or (rarely) optional. Addresses used by WAF +// processors may be required or optional. +type DiagnosticAddresses struct { + Required []string + Optional []string +} + +// Result stores the multiple values returned by a call to ddwaf_run +type Result struct { + Events []any + Derivatives map[string]any + Actions []string +} + +// Encoder/Decoder errors +var ( + errMaxDepthExceeded = errors.New("max depth exceeded") + errUnsupportedValue = errors.New("unsupported Go value") + errInvalidMapKey = errors.New("invalid WAF object map key") + errNilObjectPtr = errors.New("nil WAF object pointer") + errInvalidObjectType = errors.New("invalid type encountered when decoding") + errTooManyIndirections = errors.New("too many indirections") +) + +// RunError the WAF can return when running it. +type RunError int + +// Errors the WAF can return when running it. +const ( + ErrInternal RunError = iota + 1 + ErrInvalidObject + ErrInvalidArgument + ErrTimeout + ErrOutOfMemory + ErrEmptyRuleAddresses +) + +// Error returns the string representation of the RunError. +func (e RunError) Error() string { + switch e { + case ErrInternal: + return "internal waf error" + case ErrTimeout: + return "waf timeout" + case ErrInvalidObject: + return "invalid waf object" + case ErrInvalidArgument: + return "invalid waf argument" + case ErrOutOfMemory: + return "out of memory" + case ErrEmptyRuleAddresses: + return "empty rule addresses" + default: + return fmt.Sprintf("unknown waf error %d", e) + } +} + +// Globally dlopen() libddwaf only once because several dlopens (eg. in tests) +// aren't supported by macOS. +var ( + // libddwaf's dynamic library handle and entrypoints + wafLib *wafDl + // libddwaf's dlopen error if any + wafLoadErr error + openWafOnce sync.Once +) + +// Load loads libddwaf's dynamic library. The dynamic library is opened only +// once by the first call to this function and internally stored globally, and +// no function is currently provided in this API to close the opened handle. +// Calling this function is not mandatory and is automatically performed by +// calls to NewHandle, the entrypoint of libddwaf, but Load is useful in order +// to explicitly check libddwaf's general health where calling NewHandle doesn't +// necessarily apply nor is doable. +// The function returns ok when libddwaf was successfully loaded, along with a +// non-nil error if any. Note that both ok and err can be set, meaning that +// libddwaf is usable but some non-critical errors happened, such as failures +// to remove temporary files. It is safe to continue using libddwaf in such +// case. +func Load() (ok bool, err error) { + if ok, err = Health(); !ok { + return false, err + } + + openWafOnce.Do(func() { + wafLib, wafLoadErr = newWafDl() + if wafLoadErr != nil { + return + } + wafVersion = wafLib.wafGetVersion() + }) + + return wafLib != nil, wafLoadErr +} + +var wafVersion string + +// Version returns the version returned by libddwaf. +// It relies on the dynamic loading of the library, which can fail and return +// an empty string or the previously loaded version, if any. +func Version() string { + Load() + return wafVersion +} + +// HasEvents return true if the result holds at least 1 event +func (r *Result) HasEvents() bool { + return len(r.Events) > 0 +} + +// HasDerivatives return true if the result holds at least 1 derivative +func (r *Result) HasDerivatives() bool { + return len(r.Derivatives) > 0 +} + +// HasActions return true if the result holds at least 1 action +func (r *Result) HasActions() bool { + return len(r.Actions) > 0 +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_cgo_disabled.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_cgo_disabled.go new file mode 100644 index 000000000..6f1bcd874 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_cgo_disabled.go @@ -0,0 +1,14 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// The Go build tag "appsec" was introduced to avoid having CGO_ENABLED=0 breaking changes +// due to purego's dynamic link against libdl.so, which is not expected when CGO is disabled. +//go:build !cgo && !appsec + +package waf + +func init() { + wafSupportErrors = append(wafSupportErrors, CgoDisabledError{}) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl.go new file mode 100644 index 000000000..a68eceb30 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl.go @@ -0,0 +1,234 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build (linux || darwin) && (amd64 || arm64) && !go1.23 && !datadog.no_waf && (cgo || appsec) + +package waf + +import ( + "fmt" + "os" + + "github.com/DataDog/go-libddwaf/v2/internal/lib" + "github.com/DataDog/go-libddwaf/v2/internal/log" + "github.com/ebitengine/purego" +) + +// wafDl is the type wrapper for all C calls to the waf +// It uses `libwaf` to make C calls +// All calls must go through this one-liner to be type safe +// since purego calls are not type safe +type wafDl struct { + wafSymbols + handle uintptr +} + +type wafSymbols struct { + init uintptr + update uintptr + destroy uintptr + knownAddresses uintptr + getVersion uintptr + contextInit uintptr + contextDestroy uintptr + objectFree uintptr + resultFree uintptr + run uintptr +} + +// newWafDl loads the libddwaf shared library and resolves all tge relevant symbols. +// The caller is responsible for calling wafDl.Close on the returned object once they +// are done with it so that associated resources can be released. +func newWafDl() (dl *wafDl, err error) { + var file string + file, err = lib.DumpEmbeddedWAF() + if err != nil { + return + } + defer func() { + rmErr := os.Remove(file) + if rmErr != nil { + if err == nil { + err = rmErr + } else { + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = fmt.Errorf("%w; along with an error while removing %s: %v", err, file, rmErr) + } + } + }() + + var handle uintptr + if handle, err = purego.Dlopen(file, purego.RTLD_GLOBAL|purego.RTLD_NOW); err != nil { + return + } + + var symbols wafSymbols + if symbols, err = resolveWafSymbols(handle); err != nil { + if closeErr := purego.Dlclose(handle); closeErr != nil { + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = fmt.Errorf("%w; along with an error while releasing the shared libddwaf library: %v", err, closeErr) + } + return + } + + dl = &wafDl{symbols, handle} + + // Try calling the waf to make sure everything is fine + err = tryCall(func() error { + dl.wafGetVersion() + return nil + }) + if err != nil { + if closeErr := purego.Dlclose(handle); closeErr != nil { + // TODO: rely on errors.Join() once go1.20 is our min supported Go version + err = fmt.Errorf("%w; along with an error while releasing the shared libddwaf library: %v", err, closeErr) + } + return + } + + if val := os.Getenv(log.EnvVarLogLevel); val != "" { + setLogSym, symErr := purego.Dlsym(handle, "ddwaf_set_log_cb") + if symErr != nil { + return + } + logLevel := log.LevelNamed(val) + dl.syscall(setLogSym, log.CallbackFunctionPointer(), uintptr(logLevel)) + } + + return +} + +func (waf *wafDl) Close() error { + return purego.Dlclose(waf.handle) +} + +// wafGetVersion returned string is a static string so we do not need to free it +func (waf *wafDl) wafGetVersion() string { + return gostring(cast[byte](waf.syscall(waf.getVersion))) +} + +// wafInit initializes a new WAF with the provided ruleset, configuration and info objects. A +// cgoRefPool ensures that the provided input values are not moved or garbage collected by the Go +// runtime during the WAF call. +func (waf *wafDl) wafInit(ruleset *wafObject, config *wafConfig, info *wafObject, refs cgoRefPool) wafHandle { + handle := wafHandle(waf.syscall(waf.init, ptrToUintptr(ruleset), ptrToUintptr(config), ptrToUintptr(info))) + keepAlive(ruleset) + keepAlive(config) + keepAlive(info) + keepAlive(refs) + return handle +} + +func (waf *wafDl) wafUpdate(handle wafHandle, ruleset *wafObject, info *wafObject) wafHandle { + newHandle := wafHandle(waf.syscall(waf.update, uintptr(handle), ptrToUintptr(ruleset), ptrToUintptr(info))) + keepAlive(ruleset) + keepAlive(info) + return newHandle +} + +func (waf *wafDl) wafDestroy(handle wafHandle) { + waf.syscall(waf.destroy, uintptr(handle)) + keepAlive(handle) +} + +// wafKnownAddresses returns static strings so we do not need to free them +func (waf *wafDl) wafKnownAddresses(handle wafHandle) []string { + var nbAddresses uint32 + + arrayVoidC := waf.syscall(waf.knownAddresses, uintptr(handle), ptrToUintptr(&nbAddresses)) + if arrayVoidC == 0 { + return nil + } + + addresses := make([]string, int(nbAddresses)) + for i := 0; i < int(nbAddresses); i++ { + addresses[i] = gostring(*castWithOffset[*byte](arrayVoidC, uint64(i))) + } + + keepAlive(&nbAddresses) + keepAlive(handle) + + return addresses +} + +func (waf *wafDl) wafContextInit(handle wafHandle) wafContext { + ctx := wafContext(waf.syscall(waf.contextInit, uintptr(handle))) + keepAlive(handle) + return ctx +} + +func (waf *wafDl) wafContextDestroy(context wafContext) { + waf.syscall(waf.contextDestroy, uintptr(context)) + keepAlive(context) +} + +func (waf *wafDl) wafResultFree(result *wafResult) { + waf.syscall(waf.resultFree, ptrToUintptr(result)) + keepAlive(result) +} + +func (waf *wafDl) wafObjectFree(obj *wafObject) { + waf.syscall(waf.objectFree, ptrToUintptr(obj)) + keepAlive(obj) +} + +func (waf *wafDl) wafRun(context wafContext, persistentData, ephemeralData *wafObject, result *wafResult, timeout uint64) wafReturnCode { + rc := wafReturnCode(waf.syscall(waf.run, uintptr(context), ptrToUintptr(persistentData), ptrToUintptr(ephemeralData), ptrToUintptr(result), uintptr(timeout))) + keepAlive(context) + keepAlive(persistentData) + keepAlive(ephemeralData) + keepAlive(result) + keepAlive(timeout) + return rc +} + +// syscall is the only way to make C calls with this interface. +// purego implementation limits the number of arguments to 9, it will panic if more are provided +// Note: `purego.SyscallN` has 3 return values: these are the following: +// +// 1st - The return value is a pointer or a int of any type +// 2nd - The return value is a float +// 3rd - The value of `errno` at the end of the call +func (waf *wafDl) syscall(fn uintptr, args ...uintptr) uintptr { + ret, _, _ := purego.SyscallN(fn, args...) + return ret +} + +// resolveWafSymbols resolves relevant symbols from the libddwaf shared library using the provided +// purego.Dlopen handle. +func resolveWafSymbols(handle uintptr) (symbols wafSymbols, err error) { + if symbols.init, err = purego.Dlsym(handle, "ddwaf_init"); err != nil { + return + } + if symbols.update, err = purego.Dlsym(handle, "ddwaf_update"); err != nil { + return + } + if symbols.destroy, err = purego.Dlsym(handle, "ddwaf_destroy"); err != nil { + return + } + if symbols.knownAddresses, err = purego.Dlsym(handle, "ddwaf_known_addresses"); err != nil { + return + } + if symbols.getVersion, err = purego.Dlsym(handle, "ddwaf_get_version"); err != nil { + return + } + if symbols.contextInit, err = purego.Dlsym(handle, "ddwaf_context_init"); err != nil { + return + } + if symbols.contextDestroy, err = purego.Dlsym(handle, "ddwaf_context_destroy"); err != nil { + return + } + if symbols.resultFree, err = purego.Dlsym(handle, "ddwaf_result_free"); err != nil { + return + } + if symbols.objectFree, err = purego.Dlsym(handle, "ddwaf_object_free"); err != nil { + return + } + if symbols.run, err = purego.Dlsym(handle, "ddwaf_run"); err != nil { + return + } + + return +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl_unsupported.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl_unsupported.go new file mode 100644 index 000000000..d8966fd7c --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_dl_unsupported.go @@ -0,0 +1,52 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Build when the target OS or architecture are not supported +//go:build (!linux && !darwin) || (!amd64 && !arm64) || go1.23 || datadog.no_waf || (!cgo && !appsec) + +package waf + +type wafDl struct{} + +func newWafDl() (dl *wafDl, err error) { + _, err = Health() + return nil, err +} + +func (waf *wafDl) wafGetVersion() string { + return "" +} + +func (waf *wafDl) wafInit(obj *wafObject, config *wafConfig, info *wafObject, refs cgoRefPool) wafHandle { + return 0 +} + +func (waf *wafDl) wafUpdate(handle wafHandle, ruleset *wafObject, info *wafObject) wafHandle { + return 0 +} + +func (waf *wafDl) wafDestroy(handle wafHandle) { +} + +func (waf *wafDl) wafKnownAddresses(handle wafHandle) []string { + return nil +} + +func (waf *wafDl) wafContextInit(handle wafHandle) wafContext { + return 0 +} + +func (waf *wafDl) wafContextDestroy(context wafContext) { +} + +func (waf *wafDl) wafResultFree(result *wafResult) { +} + +func (waf *wafDl) wafObjectFree(obj *wafObject) { +} + +func (waf *wafDl) wafRun(context wafContext, persistentData, ephemeralData *wafObject, result *wafResult, timeout uint64) wafReturnCode { + return wafErrInternal +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_manually_disabled.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_manually_disabled.go new file mode 100644 index 000000000..0a37e1841 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_manually_disabled.go @@ -0,0 +1,13 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Manually set datadog.no_waf build tag +//go:build datadog.no_waf + +package waf + +func init() { + wafManuallyDisabledErr = ManuallyDisabledError{} +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_support.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_support.go new file mode 100644 index 000000000..c2fb14090 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_support.go @@ -0,0 +1,85 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package waf + +import ( + "fmt" + "runtime" + + "github.com/hashicorp/go-multierror" +) + +// Errors used to report data using the Health function +// Store all the errors related to why go-liddwaf is unavailable for the current target at runtime. +var wafSupportErrors []error + +// Not nil if the build tag `datadog.no_waf` is set +var wafManuallyDisabledErr error + +// UnsupportedOSArchError is a wrapper error type helping to handle the error +// case of trying to execute this package when the OS or architecture is not supported. +type UnsupportedOSArchError struct { + Os string + Arch string +} + +func (e UnsupportedOSArchError) Error() string { + return fmt.Sprintf("unsupported OS/Arch: %s/%s", e.Os, e.Arch) +} + +// UnsupportedGoVersionError is a wrapper error type helping to handle the error +// case of trying to execute this package when the Go version is not supported. +type UnsupportedGoVersionError struct{} + +func (e UnsupportedGoVersionError) Error() string { + return fmt.Sprintf("unsupported Go version: %s", runtime.Version()) +} + +type CgoDisabledError struct{} + +func (e CgoDisabledError) Error() string { + return "go-libddwaf is disabled when cgo is disabled unless you compile with the go build tag `appsec`. It will require libdl.so.2. libpthread.so.0, libc.so.6 and libm.so.6 shared libraries at run time on linux" +} + +// ManuallyDisabledError is a wrapper error type helping to handle the error +// case of trying to execute this package when the WAF has been manually disabled with +// the `datadog.no_waf` go build tag. +type ManuallyDisabledError struct{} + +func (e ManuallyDisabledError) Error() string { + return "the WAF has been manually disabled using the `datadog.no_waf` go build tag" +} + +// SupportsTarget returns true and a nil error when the target host environment +// is supported by this package and can be further used. +// Otherwise, it returns false along with an error detailing why. +func SupportsTarget() (bool, error) { + return len(wafSupportErrors) == 0, multierror.Append(nil, wafSupportErrors...).ErrorOrNil() +} + +// Health returns true if the waf is usable, false otherwise. At the same time it can return an error +// if the waf is not usable, but the error is not blocking if true is returned, otherwise it is. +// The following conditions are checked: +// - The Waf library has been loaded successfully (you need to call `Load()` first for this case to be taken into account) +// - The Waf library has not been manually disabled with the `datadog.no_waf` go build tag +// - The Waf library is not in an unsupported OS/Arch +// - The Waf library is not in an unsupported Go version +func Health() (bool, error) { + var err *multierror.Error + if wafLoadErr != nil { + err = multierror.Append(err, wafLoadErr) + } + + if len(wafSupportErrors) > 0 { + err = multierror.Append(err, wafSupportErrors...) + } + + if wafManuallyDisabledErr != nil { + err = multierror.Append(err, wafManuallyDisabledErr) + } + + return (wafLib != nil || wafLoadErr == nil) && len(wafSupportErrors) == 0 && wafManuallyDisabledErr == nil, err.ErrorOrNil() +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_go.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_go.go new file mode 100644 index 000000000..7d82aaaea --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_go.go @@ -0,0 +1,13 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Unsupported Go versions (>=) +//go:build go1.23 + +package waf + +func init() { + wafSupportErrors = append(wafSupportErrors, UnsupportedGoVersionError{}) +} diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_target.go b/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_target.go new file mode 100644 index 000000000..d8da6e9b8 --- /dev/null +++ b/vendor/github.com/DataDog/go-libddwaf/v2/waf_unsupported_target.go @@ -0,0 +1,18 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Unsupported target OS or architecture +// Unsupported OS Unsupported Arch +//go:build (!linux && !darwin) || (!amd64 && !arm64) + +package waf + +import ( + "runtime" +) + +func init() { + wafSupportErrors = append(wafSupportErrors, UnsupportedOSArchError{runtime.GOOS, runtime.GOARCH}) +} diff --git a/vendor/github.com/DataDog/go-tuf/LICENSE b/vendor/github.com/DataDog/go-tuf/LICENSE new file mode 100644 index 000000000..38163dd4b --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014-2020 Prime Directive, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Prime Directive, Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/DataDog/go-tuf/client/client.go b/vendor/github.com/DataDog/go-tuf/client/client.go new file mode 100644 index 000000000..b364648e7 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/client.go @@ -0,0 +1,982 @@ +package client + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "io" + + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/internal/roles" + "github.com/DataDog/go-tuf/util" + "github.com/DataDog/go-tuf/verify" +) + +const ( + // This is the upper limit in bytes we will use to limit the download + // size of the root/timestamp roles, since we might not don't know how + // big it is. + defaultRootDownloadLimit = 512000 + defaultTimestampDownloadLimit = 16384 + defaultMaxDelegations = 32 + defaultMaxRootRotations = 1e3 +) + +// LocalStore is local storage for downloaded top-level metadata. +type LocalStore interface { + io.Closer + + // GetMeta returns top-level metadata from local storage. The keys are + // in the form `ROLE.json`, with ROLE being a valid top-level role. + GetMeta() (map[string]json.RawMessage, error) + + // SetMeta persists the given top-level metadata in local storage, the + // name taking the same format as the keys returned by GetMeta. + SetMeta(name string, meta json.RawMessage) error + + // DeleteMeta deletes a given metadata. + DeleteMeta(name string) error +} + +// RemoteStore downloads top-level metadata and target files from a remote +// repository. +type RemoteStore interface { + // GetMeta downloads the given metadata from remote storage. + // + // `name` is the filename of the metadata (e.g. "root.json") + // + // `err` is ErrNotFound if the given file does not exist. + // + // `size` is the size of the stream, -1 indicating an unknown length. + GetMeta(name string) (stream io.ReadCloser, size int64, err error) + + // GetTarget downloads the given target file from remote storage. + // + // `path` is the path of the file relative to the root of the remote + // targets directory (e.g. "/path/to/file.txt"). + // + // `err` is ErrNotFound if the given file does not exist. + // + // `size` is the size of the stream, -1 indicating an unknown length. + GetTarget(path string) (stream io.ReadCloser, size int64, err error) +} + +// Client provides methods for fetching updates from a remote repository and +// downloading remote target files. +type Client struct { + local LocalStore + remote RemoteStore + + // The following four fields represent the versions of metatdata either + // from local storage or from recently downloaded metadata + rootVer int64 + targetsVer int64 + snapshotVer int64 + timestampVer int64 + + // targets is the list of available targets, either from local storage + // or from recently downloaded targets metadata + targets data.TargetFiles + + // localMeta is the raw metadata from local storage and is used to + // check whether remote metadata is present locally + localMeta map[string]json.RawMessage + + // db is a key DB used for verifying metadata + db *verify.DB + + // consistentSnapshot indicates whether the remote storage is using + // consistent snapshots (as specified in root.json) + consistentSnapshot bool + + // MaxDelegations limits by default the number of delegations visited for any + // target + MaxDelegations int + + // MaxRootRotations limits the number of downloaded roots in 1.0.19 root updater + MaxRootRotations int +} + +func NewClient(local LocalStore, remote RemoteStore) *Client { + return &Client{ + local: local, + remote: remote, + MaxDelegations: defaultMaxDelegations, + MaxRootRotations: defaultMaxRootRotations, + } +} + +// Init initializes a local repository from root metadata. +// +// The root's keys are extracted from the root and saved in local storage. +// Root expiration is not checked. +// It is expected that rootJSON was securely distributed with the software +// being updated. +func (c *Client) Init(rootJSON []byte) error { + err := c.loadAndVerifyRootMeta(rootJSON, true /*ignoreExpiredCheck*/) + if err != nil { + return err + } + return c.local.SetMeta("root.json", rootJSON) +} + +// Update downloads and verifies remote metadata and returns updated targets. +// It always performs root update (5.2 and 5.3) section of the v1.0.19 spec. +// +// https://theupdateframework.github.io/specification/v1.0.19/index.html#load-trusted-root +func (c *Client) Update() (data.TargetFiles, error) { + if err := c.UpdateRoots(); err != nil { + if _, ok := err.(verify.ErrExpired); ok { + // For backward compatibility, we wrap the ErrExpired inside + // ErrDecodeFailed. + return nil, ErrDecodeFailed{"root.json", err} + } + return nil, err + } + + // Load trusted metadata files, if any, and verify them against the latest root + c.getLocalMeta() + + // 5.4.1 - Download the timestamp metadata + timestampJSON, err := c.downloadMetaUnsafe("timestamp.json", defaultTimestampDownloadLimit) + if err != nil { + return nil, err + } + // 5.4.(2,3 and 4) - Verify timestamp against various attacks + // Returns the extracted snapshot metadata + snapshotMeta, sameTimestampVersion, err := c.decodeTimestamp(timestampJSON) + if sameTimestampVersion { + // The new timestamp.json file had the same version; we don't need to + // update, so bail early. + return c.targets, nil + } + + if err != nil { + return nil, err + } + // 5.4.5 - Persist the timestamp metadata + if err := c.local.SetMeta("timestamp.json", timestampJSON); err != nil { + return nil, err + } + + // 5.5.1 - Download snapshot metadata + // 5.5.2 and 5.5.4 - Check against timestamp role's snapshot hash and version + snapshotJSON, err := c.downloadMetaFromTimestamp("snapshot.json", snapshotMeta) + if err != nil { + return nil, err + } + // 5.5.(3,5 and 6) - Verify snapshot against various attacks + // Returns the extracted metadata files + snapshotMetas, err := c.decodeSnapshot(snapshotJSON) + if err != nil { + return nil, err + } + // 5.5.7 - Persist snapshot metadata + if err := c.local.SetMeta("snapshot.json", snapshotJSON); err != nil { + return nil, err + } + + // If we don't have the targets.json, download it, determine updated + // targets and save targets.json in local storage + var updatedTargets data.TargetFiles + targetsMeta := snapshotMetas["targets.json"] + if !c.hasMetaFromSnapshot("targets.json", targetsMeta) { + // 5.6.1 - Download the top-level targets metadata file + // 5.6.2 and 5.6.4 - Check against snapshot role's targets hash and version + targetsJSON, err := c.downloadMetaFromSnapshot("targets.json", targetsMeta) + if err != nil { + return nil, err + } + // 5.6.(3 and 5) - Verify signatures and check against freeze attack + updatedTargets, err = c.decodeTargets(targetsJSON) + if err != nil { + return nil, err + } + // 5.6.6 - Persist targets metadata + if err := c.local.SetMeta("targets.json", targetsJSON); err != nil { + return nil, err + } + } + + return updatedTargets, nil +} + +func (c *Client) UpdateRoots() error { + // https://theupdateframework.github.io/specification/v1.0.19/index.html#load-trusted-root + // 5.2 Load the trusted root metadata file. We assume that a good, + // trusted copy of this file was shipped with the package manager + // or software updater using an out-of-band process. + if err := c.loadAndVerifyLocalRootMeta( /*ignoreExpiredCheck=*/ true); err != nil { + return err + } + m, err := c.local.GetMeta() + if err != nil { + return err + } + + type KeyInfo struct { + KeyIDs map[string]bool + Threshold int + } + + // Prepare for 5.3.11: If the timestamp and / or snapshot keys have been rotated, + // then delete the trusted timestamp and snapshot metadata files. + getKeyInfo := func(role string) KeyInfo { + keyIDs := make(map[string]bool) + for k := range c.db.GetRole(role).KeyIDs { + keyIDs[k] = true + } + return KeyInfo{keyIDs, c.db.GetRole(role).Threshold} + } + + // The nonRootKeyInfo looks like this: + // { + // "timestamp": {KeyIDs={"KEYID1": true, "KEYID2": true}, Threshold=2}, + // "snapshot": {KeyIDs={"KEYID3": true}, Threshold=1}, + // "targets": {KeyIDs={"KEYID4": true, "KEYID5": true, "KEYID6": true}, Threshold=1} + // } + + nonRootKeyInfo := map[string]KeyInfo{"timestamp": {}, "snapshot": {}, "targets": {}} + for k := range nonRootKeyInfo { + nonRootKeyInfo[k] = getKeyInfo(k) + } + + // 5.3.1 Temorarily turn on the consistent snapshots in order to download + // versioned root metadata files as described next. + consistentSnapshot := c.consistentSnapshot + c.consistentSnapshot = true + + nRootMetadata := m["root.json"] + + // https://theupdateframework.github.io/specification/v1.0.19/index.html#update-root + + // 5.3.1 Since it may now be signed using entirely different keys, + // the client MUST somehow be able to establish a trusted line of + // continuity to the latest set of keys (see § 6.1 Key + // management and migration). To do so, the client MUST + // download intermediate root metadata files, until the + // latest available one is reached. Therefore, it MUST + // temporarily turn on consistent snapshots in order to + // download versioned root metadata files as described next. + + // This loop returns on error or breaks after downloading the lastest root metadata. + // 5.3.2 Let N denote the version number of the trusted root metadata file. + for i := 0; i < c.MaxRootRotations; i++ { + // 5.3.3 Try downloading version nPlusOne of the root metadata file. + // NOTE: as a side effect, we do update c.rootVer to nPlusOne between iterations. + nPlusOne := c.rootVer + 1 + nPlusOneRootPath := util.VersionedPath("root.json", nPlusOne) + nPlusOneRootMetadata, err := c.downloadMetaUnsafe(nPlusOneRootPath, defaultRootDownloadLimit) + + if err != nil { + if _, ok := err.(ErrMissingRemoteMetadata); ok { + // stop when the next root can't be downloaded + break + } + return err + } + + // 5.3.4 Check for an arbitrary software attack. + // 5.3.4.1 Check that N signed N+1 + nPlusOneRootMetadataSigned, err := c.verifyRoot(nRootMetadata, nPlusOneRootMetadata) + if err != nil { + return err + } + + // 5.3.4.2 check that N+1 signed itself. + if _, err := c.verifyRoot(nPlusOneRootMetadata, nPlusOneRootMetadata); err != nil { + // 5.3.6 Note that the expiration of the new (intermediate) root + // metadata file does not matter yet, because we will check for + // it in step 5.3.10. + return err + } + + // 5.3.5 Check for a rollback attack. Here, we check that nPlusOneRootMetadataSigned.version == nPlusOne. + if nPlusOneRootMetadataSigned.Version != nPlusOne { + return verify.ErrWrongVersion{ + Given: nPlusOneRootMetadataSigned.Version, + Expected: nPlusOne, + } + } + + // 5.3.7 Set the trusted root metadata file to the new root metadata file. + c.rootVer = nPlusOneRootMetadataSigned.Version + // NOTE: following up on 5.3.1, we want to always have consistent snapshots on for the duration + // of root rotation. AFTER the rotation is over, we will set it to the value of the last root. + consistentSnapshot = nPlusOneRootMetadataSigned.ConsistentSnapshot + // 5.3.8 Persist root metadata. The client MUST write the file to non-volatile storage as FILENAME.EXT (e.g. root.json). + // NOTE: Internally, setMeta stores metadata in LevelDB in a persistent manner. + if err := c.local.SetMeta("root.json", nPlusOneRootMetadata); err != nil { + return err + } + nRootMetadata = nPlusOneRootMetadata + // 5.3.9 Repeat steps 5.3.2 to 5.3.9 + + } // End of the for loop. + + // 5.3.10 Check for a freeze attack. + // NOTE: This will check for any, including freeze, attack. + if err := c.loadAndVerifyLocalRootMeta( /*ignoreExpiredCheck=*/ false); err != nil { + return err + } + + countDeleted := func(s1 map[string]bool, s2 map[string]bool) int { + c := 0 + for k := range s1 { + if _, ok := s2[k]; !ok { + c++ + } + } + return c + } + + // 5.3.11 To recover from fast-forward attack, certain metadata files need + // to be deleted if a threshold of keys are revoked. + // List of metadata that should be deleted per role if a threshold of keys + // are revoked: + // (based on the ongoing PR: https://github.com/mnm678/specification/tree/e50151d9df632299ddea364c4f44fe8ca9c10184) + // timestamp -> delete timestamp.json + // snapshot -> delete timestamp.json and snapshot.json + // targets -> delete snapshot.json and targets.json + // + // nonRootKeyInfo contains the keys and thresholds from root.json + // that were on disk before the root update process begins. + for topLevelRolename := range nonRootKeyInfo { + // ki contains the keys and thresholds from the latest downloaded root.json. + ki := getKeyInfo(topLevelRolename) + if countDeleted(nonRootKeyInfo[topLevelRolename].KeyIDs, ki.KeyIDs) >= nonRootKeyInfo[topLevelRolename].Threshold { + deleteMeta := map[string][]string{ + "timestamp": {"timestamp.json"}, + "snapshot": {"timestamp.json", "snapshot.json"}, + "targets": {"snapshot.json", "targets.json"}, + } + + for _, r := range deleteMeta[topLevelRolename] { + c.local.DeleteMeta(r) + } + } + } + + // 5.3.12 Set whether consistent snapshots are used as per the trusted root metadata file. + c.consistentSnapshot = consistentSnapshot + return nil +} + +// getLocalMeta decodes and verifies metadata from local storage. +// The verification of local files is purely for consistency, if an attacker +// has compromised the local storage, there is no guarantee it can be trusted. +// Before trying to load the metadata files, it clears the in-memory copy of the local metadata. +// This is to insure that all of the loaded metadata files at the end are indeed verified by the latest root. +// If some of the metadata files fail to load it will proceed with trying to load the rest, +// but still return an error at the end, if such occurred. Otherwise returns nil. +func (c *Client) getLocalMeta() error { + var retErr error + loadFailed := false + // Clear the in-memory copy of the local metadata. The goal is to reload and take into account + // only the metadata files that are verified by the latest root. Otherwise, their content should + // be ignored. + c.localMeta = make(map[string]json.RawMessage) + + // Load the latest root meta + if err := c.loadAndVerifyLocalRootMeta( /*ignoreExpiredCheck=*/ false); err != nil { + return err + } + + // Load into memory the existing meta, if any, from the local storage + meta, err := c.local.GetMeta() + if err != nil { + return nil + } + + // Verify the top-level metadata (timestamp, snapshot and targets) against the latest root and load it, if okay + if timestampJSON, ok := meta["timestamp.json"]; ok { + timestamp := &data.Timestamp{} + if err := c.db.UnmarshalTrusted(timestampJSON, timestamp, "timestamp"); err != nil { + loadFailed = true + retErr = err + } else { + c.localMeta["timestamp.json"] = meta["timestamp.json"] + c.timestampVer = timestamp.Version + } + } + + snapshot := &data.Snapshot{} + if snapshotJSON, ok := meta["snapshot.json"]; ok { + if err := c.db.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot"); err != nil { + loadFailed = true + retErr = err + } else { + c.localMeta["snapshot.json"] = meta["snapshot.json"] + c.snapshotVer = snapshot.Version + } + } + + if targetsJSON, ok := meta["targets.json"]; ok { + targets := &data.Targets{} + if err := c.db.UnmarshalTrusted(targetsJSON, targets, "targets"); err != nil { + loadFailed = true + retErr = err + } else { + c.localMeta["targets.json"] = meta["targets.json"] + c.targetsVer = targets.Version + // FIXME(TUF-0.9) temporarily support files with leading path separators. + // c.targets = targets.Targets + c.loadTargets(targets.Targets) + } + } + + if loadFailed { + // If any of the metadata failed to be verified, return the reason for that failure + // and fail fast before delegated targets + return retErr + } + + // verifiedDelegatedTargets is a set of verified delegated targets + for fileName, fileContent := range meta { + if roles.IsDelegatedTargetsManifest(fileName) { + c.localMeta[fileName] = fileContent + } + } + + if loadFailed { + // If any of the metadata failed to be verified, return the reason for that failure + return retErr + } + return nil +} + +// getDelegationPathFromRaw verifies a delegated targets against +// a given snapshot and returns an error if it's invalid +// +// Delegation must have targets to get a path, else an empty list +// will be returned: this is because the delegation iterator is leveraged. +// +// Concrete example: +// targets +// └── a.json +//   └── b.json +//      └── c.json +//        └── target_file.txt +// +// If you try to use that function on "a.json" or "b.json", it'll return an empty list +// with no error, as neither of them declare a target file +// On the other hand, if you use that function on "c.json", it'll return & verify +// [c.json, b.json, a.json]. Running that function on every delegated targets +// guarantees that if a delegated targets is in the path of a target file, then it will +// appear at least once in the result +func (c *Client) getDelegationPathFromRaw(snapshot *data.Snapshot, delegatedTargetsJSON json.RawMessage) ([]string, error) { + // unmarshal the delegated targets first without verifying as + // we need at least one targets file name to leverage the + // getTargetFileMetaDelegationPath method + s := &data.Signed{} + if err := json.Unmarshal(delegatedTargetsJSON, s); err != nil { + return nil, err + } + targets := &data.Targets{} + if err := json.Unmarshal(s.Signed, targets); err != nil { + return nil, err + } + for targetPath := range targets.Targets { + // Gets target file from remote store + _, resp, err := c.getTargetFileMetaDelegationPath(targetPath, snapshot) + // We only need to test one targets file: + // - If it is valid, it means the delegated targets has been validated + // - If it is not, the delegated targets isn't valid + if errors.As(err, &ErrMissingRemoteMetadata{}) { + // As this function is used to fill the local store cache, the targets + // will be downloaded from the remote store as the local store cache is + // empty, meaning that the delegated targets may not exist anymore. In + // that case, ignore it. + return nil, nil + } + return resp, err + } + return nil, nil +} + +// loadAndVerifyLocalRootMeta decodes and verifies root metadata from +// local storage and loads the top-level keys. This method first clears +// the DB for top-level keys and then loads the new keys. +func (c *Client) loadAndVerifyLocalRootMeta(ignoreExpiredCheck bool) error { + meta, err := c.local.GetMeta() + if err != nil { + return err + } + rootJSON, ok := meta["root.json"] + if !ok { + return ErrNoRootKeys + } + return c.loadAndVerifyRootMeta(rootJSON, ignoreExpiredCheck) +} + +// loadAndVerifyRootMeta decodes and verifies root metadata and loads the top-level keys. +// This method first clears the DB for top-level keys and then loads the new keys. +func (c *Client) loadAndVerifyRootMeta(rootJSON []byte, ignoreExpiredCheck bool) error { + // unmarshal root.json without verifying as we need the root + // keys first + s := &data.Signed{} + if err := json.Unmarshal(rootJSON, s); err != nil { + return err + } + root := &data.Root{} + if err := json.Unmarshal(s.Signed, root); err != nil { + return err + } + ndb := verify.NewDB() + for id, k := range root.Keys { + if err := ndb.AddKey(id, k); err != nil { + return err + } + } + for name, role := range root.Roles { + if err := ndb.AddRole(name, role); err != nil { + return err + } + } + // Any trusted local root metadata version must be greater than 0. + if ignoreExpiredCheck { + if err := ndb.VerifyIgnoreExpiredCheck(s, "root", 0); err != nil { + return err + } + } else { + if err := ndb.Verify(s, "root", 0); err != nil { + return err + } + } + c.consistentSnapshot = root.ConsistentSnapshot + c.rootVer = root.Version + c.db = ndb + return nil +} + +// verifyRoot verifies Signed section of the bJSON +// using verification keys in aJSON. +func (c *Client) verifyRoot(aJSON []byte, bJSON []byte) (*data.Root, error) { + aSigned := &data.Signed{} + if err := json.Unmarshal(aJSON, aSigned); err != nil { + return nil, err + } + aRoot := &data.Root{} + if err := json.Unmarshal(aSigned.Signed, aRoot); err != nil { + return nil, err + } + + bSigned := &data.Signed{} + if err := json.Unmarshal(bJSON, bSigned); err != nil { + return nil, err + } + bRoot := &data.Root{} + if err := json.Unmarshal(bSigned.Signed, bRoot); err != nil { + return nil, err + } + + ndb := verify.NewDB() + for id, k := range aRoot.Keys { + if err := ndb.AddKey(id, k); err != nil { + return nil, err + } + } + for name, role := range aRoot.Roles { + if err := ndb.AddRole(name, role); err != nil { + return nil, err + } + } + + if err := ndb.VerifySignatures(bSigned, "root"); err != nil { + return nil, err + } + return bRoot, nil +} + +// FIXME(TUF-0.9) TUF is considering removing support for target files starting +// with a leading path separator. In order to be backwards compatible, we'll +// just remove leading separators for now. +func (c *Client) loadTargets(targets data.TargetFiles) { + c.targets = make(data.TargetFiles) + for name, meta := range targets { + c.targets[name] = meta + c.targets[util.NormalizeTarget(name)] = meta + } +} + +// downloadMetaUnsafe downloads top-level metadata from remote storage without +// verifying it's length and hashes (used for example to download timestamp.json +// which has unknown size). It will download at most maxMetaSize bytes. +func (c *Client) downloadMetaUnsafe(name string, maxMetaSize int64) ([]byte, error) { + r, size, err := c.remote.GetMeta(name) + if err != nil { + if IsNotFound(err) { + return nil, ErrMissingRemoteMetadata{name} + } + return nil, ErrDownloadFailed{name, err} + } + defer r.Close() + + // return ErrMetaTooLarge if the reported size is greater than maxMetaSize + if size > maxMetaSize { + return nil, ErrMetaTooLarge{name, size, maxMetaSize} + } + + // although the size has been checked above, use a LimitReader in case + // the reported size is inaccurate, or size is -1 which indicates an + // unknown length + return io.ReadAll(io.LimitReader(r, maxMetaSize)) +} + +// remoteGetFunc is the type of function the download method uses to download +// remote files +type remoteGetFunc func(string) (io.ReadCloser, int64, error) + +// downloadHashed tries to download the hashed prefixed version of the file. +func (c *Client) downloadHashed(file string, get remoteGetFunc, hashes data.Hashes) (io.ReadCloser, int64, error) { + // try each hashed path in turn, and either return the contents, + // try the next one if a 404 is returned, or return an error + for _, path := range util.HashedPaths(file, hashes) { + r, size, err := get(path) + if err != nil { + if IsNotFound(err) { + continue + } + return nil, 0, err + } + return r, size, nil + } + return nil, 0, ErrNotFound{file} +} + +// download downloads the given target file from remote storage using the get +// function, adding hashes to the path if consistent snapshots are in use +func (c *Client) downloadTarget(file string, get remoteGetFunc, hashes data.Hashes) (io.ReadCloser, int64, error) { + if c.consistentSnapshot { + return c.downloadHashed(file, get, hashes) + } else { + return get(file) + } +} + +// downloadVersionedMeta downloads top-level metadata from remote storage and +// verifies it using the given file metadata. +func (c *Client) downloadMeta(name string, version int64, m data.FileMeta) ([]byte, error) { + r, size, err := func() (io.ReadCloser, int64, error) { + if c.consistentSnapshot { + path := util.VersionedPath(name, version) + r, size, err := c.remote.GetMeta(path) + if err == nil { + return r, size, nil + } + + return nil, 0, err + } else { + return c.remote.GetMeta(name) + } + }() + if err != nil { + if IsNotFound(err) { + return nil, ErrMissingRemoteMetadata{name} + } + return nil, err + } + defer r.Close() + + // return ErrWrongSize if the reported size is known and incorrect + var stream io.Reader + if m.Length != 0 { + if size >= 0 && size != m.Length { + return nil, ErrWrongSize{name, size, m.Length} + } + + // wrap the data in a LimitReader so we download at most m.Length bytes + stream = io.LimitReader(r, m.Length) + } else { + stream = r + } + + return io.ReadAll(stream) +} + +func (c *Client) downloadMetaFromSnapshot(name string, m data.SnapshotFileMeta) ([]byte, error) { + b, err := c.downloadMeta(name, m.Version, data.FileMeta{Length: m.Length, Hashes: m.Hashes}) + if err != nil { + return nil, err + } + + // 5.6.2 – Check length and hashes of fetched bytes *before* parsing metadata + if err := util.BytesMatchLenAndHashes(b, m.Length, m.Hashes); err != nil { + return nil, ErrDownloadFailed{name, err} + } + + meta, err := util.GenerateSnapshotFileMeta(bytes.NewReader(b), m.Hashes.HashAlgorithms()...) + if err != nil { + return nil, err + } + + // 5.6.4 - Check against snapshot role's version + if err := util.VersionEqual(meta.Version, m.Version); err != nil { + return nil, ErrDownloadFailed{name, err} + } + + return b, nil +} + +func (c *Client) downloadMetaFromTimestamp(name string, m data.TimestampFileMeta) ([]byte, error) { + b, err := c.downloadMeta(name, m.Version, data.FileMeta{Length: m.Length, Hashes: m.Hashes}) + if err != nil { + return nil, err + } + + // 5.2.2. – Check length and hashes of fetched bytes *before* parsing metadata + if err := util.BytesMatchLenAndHashes(b, m.Length, m.Hashes); err != nil { + return nil, ErrDownloadFailed{name, err} + } + + meta, err := util.GenerateTimestampFileMeta(bytes.NewReader(b), m.Hashes.HashAlgorithms()...) + if err != nil { + return nil, err + } + + // 5.5.4 - Check against timestamp role's version + if err := util.VersionEqual(meta.Version, m.Version); err != nil { + return nil, ErrDownloadFailed{name, err} + } + + return b, nil +} + +// decodeSnapshot decodes and verifies snapshot metadata, and returns the new +// root and targets file meta. +func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFiles, error) { + snapshot := &data.Snapshot{} + // 5.5.(3 and 6) - Verify it's signed correctly and it's not expired + if err := c.db.Unmarshal(b, snapshot, "snapshot", c.snapshotVer); err != nil { + return data.SnapshotFiles{}, ErrDecodeFailed{"snapshot.json", err} + } + // 5.5.5 - Check for top-level targets rollback attack + // Verify explicitly that current targets meta version is less than or equal to the new one + if snapshot.Meta["targets.json"].Version < c.targetsVer { + return data.SnapshotFiles{}, verify.ErrLowVersion{Actual: snapshot.Meta["targets.json"].Version, Current: c.targetsVer} + } + + // 5.5.5 - Get the local/trusted snapshot metadata, if any, and check all target metafiles against rollback attack + // In case the local snapshot metadata was not verified by the keys in the latest root during getLocalMeta(), + // snapshot.json won't be present in c.localMeta and thus this check will not be processed. + if snapshotJSON, ok := c.localMeta["snapshot.json"]; ok { + currentSnapshot := &data.Snapshot{} + if err := c.db.UnmarshalTrusted(snapshotJSON, currentSnapshot, "snapshot"); err != nil { + return data.SnapshotFiles{}, err + } + // 5.5.5 - Check for rollback attacks in both top-level and delegated targets roles (note that the Meta object includes both) + for path, local := range currentSnapshot.Meta { + if newMeta, ok := snapshot.Meta[path]; ok { + // 5.5.5 - Check for rollback attack + if newMeta.Version < local.Version { + return data.SnapshotFiles{}, verify.ErrLowVersion{Actual: newMeta.Version, Current: local.Version} + } + } else { + // 5.5.5 - Abort the update if a target file has been removed from the new snapshot file + return data.SnapshotFiles{}, verify.ErrMissingTargetFile + } + } + } + // At this point we can trust the new snapshot, the top-level targets, and any delegated targets versions it refers to + // so we can update the client's trusted versions and proceed with persisting the new snapshot metadata + // c.snapshotVer was already set when we verified the timestamp metadata + c.targetsVer = snapshot.Meta["targets.json"].Version + return snapshot.Meta, nil +} + +// decodeTargets decodes and verifies targets metadata, sets c.targets and +// returns updated targets. +func (c *Client) decodeTargets(b json.RawMessage) (data.TargetFiles, error) { + targets := &data.Targets{} + // 5.6.(3 and 5) - Verify signatures and check against freeze attack + if err := c.db.Unmarshal(b, targets, "targets", c.targetsVer); err != nil { + return nil, ErrDecodeFailed{"targets.json", err} + } + // Generate a list with the updated targets + updatedTargets := make(data.TargetFiles) + for path, meta := range targets.Targets { + if local, ok := c.targets[path]; ok { + if err := util.TargetFileMetaEqual(local, meta); err == nil { + continue + } + } + updatedTargets[path] = meta + } + // c.targetsVer was already updated when we verified the snapshot metadata + // FIXME(TUF-0.9) temporarily support files with leading path separators. + // c.targets = targets.Targets + c.loadTargets(targets.Targets) + return updatedTargets, nil +} + +// decodeTimestamp decodes and verifies timestamp metadata, and returns the +// new snapshot file meta and signals whether the update should be aborted early +// (the new timestamp has the same version as the old one, so there's no need to +// complete the update). +func (c *Client) decodeTimestamp(b json.RawMessage) (data.TimestampFileMeta, bool, error) { + timestamp := &data.Timestamp{} + + if err := c.db.Unmarshal(b, timestamp, "timestamp", c.timestampVer); err != nil { + return data.TimestampFileMeta{}, false, ErrDecodeFailed{"timestamp.json", err} + } + // 5.4.3.1 - Check for timestamp rollback attack + // We already checked for timestamp.Version < c.timestampVer in the Unmarshal call above. + // Here, we're checking for version equality, which indicates that we can abandon this update. + if timestamp.Version == c.timestampVer { + return data.TimestampFileMeta{}, true, nil + } + // 5.4.3.2 - Check for snapshot rollback attack + // Verify that the current snapshot meta version is less than or equal to the new one + if timestamp.Meta["snapshot.json"].Version < c.snapshotVer { + return data.TimestampFileMeta{}, false, verify.ErrLowVersion{Actual: timestamp.Meta["snapshot.json"].Version, Current: c.snapshotVer} + } + // At this point we can trust the new timestamp and the snapshot version it refers to + // so we can update the client's trusted versions and proceed with persisting the new timestamp + c.timestampVer = timestamp.Version + c.snapshotVer = timestamp.Meta["snapshot.json"].Version + return timestamp.Meta["snapshot.json"], false, nil +} + +// hasMetaFromSnapshot checks whether local metadata has the given meta +func (c *Client) hasMetaFromSnapshot(name string, m data.SnapshotFileMeta) bool { + _, ok := c.localMetaFromSnapshot(name, m) + return ok +} + +// localMetaFromSnapshot returns localmetadata if it matches the snapshot +func (c *Client) localMetaFromSnapshot(name string, m data.SnapshotFileMeta) (json.RawMessage, bool) { + b, ok := c.localMeta[name] + if !ok { + return nil, false + } + meta, err := util.GenerateSnapshotFileMeta(bytes.NewReader(b), m.Hashes.HashAlgorithms()...) + if err != nil { + return nil, false + } + err = util.SnapshotFileMetaEqual(meta, m) + return b, err == nil +} + +type Destination interface { + io.Writer + Delete() error +} + +// Download downloads the given target file from remote storage into dest. +// +// dest will be deleted and an error returned in the following situations: +// +// - The target does not exist in the local targets.json +// - Failed to fetch the chain of delegations accessible from local snapshot.json +// - The target does not exist in any targets +// - Metadata cannot be generated for the downloaded data +// - Generated metadata does not match local metadata for the given file +// - Size of the download does not match if the reported size is known and +// incorrect +func (c *Client) Download(name string, dest Destination) (err error) { + // delete dest if there is an error + defer func() { + if err != nil { + dest.Delete() + } + }() + + // populate c.targets from local storage if not set + if c.targets == nil { + if err := c.getLocalMeta(); err != nil { + return err + } + } + + normalizedName := util.NormalizeTarget(name) + localMeta, ok := c.targets[normalizedName] + if !ok { + // search in delegations + localMeta, err = c.getTargetFileMeta(normalizedName) + if err != nil { + return err + } + } + + // get the data from remote storage + r, size, err := c.downloadTarget(normalizedName, c.remote.GetTarget, localMeta.Hashes) + if err != nil { + return err + } + defer r.Close() + + // return ErrWrongSize if the reported size is known and incorrect + if size >= 0 && size != localMeta.Length { + return ErrWrongSize{name, size, localMeta.Length} + } + + // wrap the data in a LimitReader so we download at most localMeta.Length bytes + stream := io.LimitReader(r, localMeta.Length) + + // read the data, simultaneously writing it to dest and generating metadata + actual, err := util.GenerateTargetFileMeta(io.TeeReader(stream, dest), localMeta.HashAlgorithms()...) + if err != nil { + return ErrDownloadFailed{name, err} + } + + // check the data has the correct length and hashes + if err := util.TargetFileMetaEqual(actual, localMeta); err != nil { + if e, ok := err.(util.ErrWrongLength); ok { + return ErrWrongSize{name, e.Actual, e.Expected} + } + return ErrDownloadFailed{name, err} + } + + return nil +} + +func (c *Client) VerifyDigest(digest string, digestAlg string, length int64, path string) error { + localMeta, ok := c.targets[path] + if !ok { + return ErrUnknownTarget{Name: path, SnapshotVersion: c.snapshotVer} + } + + actual := data.FileMeta{Length: length, Hashes: make(data.Hashes, 1)} + var err error + actual.Hashes[digestAlg], err = hex.DecodeString(digest) + if err != nil { + return err + } + + if err := util.TargetFileMetaEqual(data.TargetFileMeta{FileMeta: actual}, localMeta); err != nil { + if e, ok := err.(util.ErrWrongLength); ok { + return ErrWrongSize{path, e.Actual, e.Expected} + } + return ErrDownloadFailed{path, err} + } + + return nil +} + +// Target returns the target metadata for a specific target if it +// exists, searching from top-level level targets then through +// all delegations. If it does not, ErrNotFound will be returned. +func (c *Client) Target(name string) (data.TargetFileMeta, error) { + target, err := c.getTargetFileMeta(util.NormalizeTarget(name)) + if err == nil { + return target, nil + } + + if _, ok := err.(ErrUnknownTarget); ok { + return data.TargetFileMeta{}, ErrNotFound{name} + } + + return data.TargetFileMeta{}, err +} + +// Targets returns the complete list of available top-level targets. +func (c *Client) Targets() (data.TargetFiles, error) { + // populate c.targets from local storage if not set + if c.targets == nil { + if err := c.getLocalMeta(); err != nil { + return nil, err + } + } + return c.targets, nil +} diff --git a/vendor/github.com/DataDog/go-tuf/client/delegations.go b/vendor/github.com/DataDog/go-tuf/client/delegations.go new file mode 100644 index 000000000..4cf540455 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/delegations.go @@ -0,0 +1,152 @@ +package client + +import ( + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/pkg/targets" + "github.com/DataDog/go-tuf/verify" +) + +// getTargetFileMeta searches for a verified TargetFileMeta matching a target +// Requires a local snapshot to be loaded and is locked to the snapshot versions. +func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { + snapshot, err := c.loadLocalSnapshot() + if err != nil { + return data.TargetFileMeta{}, err + } + + targetFileMeta, _, err := c.getTargetFileMetaDelegationPath(target, snapshot) + if err != nil { + return data.TargetFileMeta{}, err + } + return targetFileMeta, nil +} + +// getTargetFileMetaDelegationPath searches for a verified TargetFileMeta matching a target +// Requires snapshot to be passed and is locked to that specific snapshot versions. +// Searches through delegated targets following TUF spec 1.0.19 section 5.6. +func (c *Client) getTargetFileMetaDelegationPath(target string, snapshot *data.Snapshot) (data.TargetFileMeta, []string, error) { + // delegationsIterator covers 5.6.7 + // - pre-order depth-first search starting with the top targets + // - filter delegations with paths or path_hash_prefixes matching searched target + // - 5.6.7.1 cycles protection + // - 5.6.7.2 terminations + delegations, err := targets.NewDelegationsIterator(target, c.db) + if err != nil { + return data.TargetFileMeta{}, nil, err + } + + targetFileMeta := data.TargetFileMeta{} + delegationRole := "" + + for i := 0; i < c.MaxDelegations; i++ { + d, ok := delegations.Next() + if !ok { + return data.TargetFileMeta{}, nil, ErrUnknownTarget{target, snapshot.Version} + } + + // covers 5.6.{1,2,3,4,5,6} + targets, err := c.loadDelegatedTargets(snapshot, d.Delegatee.Name, d.DB) + if err != nil { + return data.TargetFileMeta{}, nil, err + } + + // stop when the searched TargetFileMeta is found + if m, ok := targets.Targets[target]; ok { + delegationRole = d.Delegatee.Name + targetFileMeta = m + break + } + + if targets.Delegations != nil { + delegationsDB, err := verify.NewDBFromDelegations(targets.Delegations) + if err != nil { + return data.TargetFileMeta{}, nil, err + } + err = delegations.Add(targets.Delegations.Roles, d.Delegatee.Name, delegationsDB) + if err != nil { + return data.TargetFileMeta{}, nil, err + } + } + } + + if len(delegationRole) > 0 { + return targetFileMeta, buildPath(delegations.Parent, delegationRole, ""), nil + } + + return data.TargetFileMeta{}, nil, ErrMaxDelegations{ + Target: target, + MaxDelegations: c.MaxDelegations, + SnapshotVersion: snapshot.Version, + } +} + +func buildPath(parent func(string) string, start string, end string) []string { + if start == end { + return nil + } + + path := []string{start} + current := start + for { + current = parent(current) + if current == end { + break + } + path = append(path, current) + } + return path +} + +func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) { + if err := c.getLocalMeta(); err != nil { + return nil, err + } + rawS, ok := c.localMeta["snapshot.json"] + if !ok { + return nil, ErrNoLocalSnapshot + } + + snapshot := &data.Snapshot{} + if err := c.db.Unmarshal(rawS, snapshot, "snapshot", c.snapshotVer); err != nil { + return nil, ErrDecodeFailed{"snapshot.json", err} + } + return snapshot, nil +} + +// loadDelegatedTargets downloads, decodes, verifies and stores targets +func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, db *verify.DB) (*data.Targets, error) { + var err error + fileName := role + ".json" + fileMeta, ok := snapshot.Meta[fileName] + if !ok { + return nil, ErrRoleNotInSnapshot{role, snapshot.Version} + } + + // 5.6.1 download target if not in the local store + // 5.6.2 check against snapshot hash + // 5.6.4 check against snapshot version + raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta) + if !alreadyStored { + raw, err = c.downloadMetaFromSnapshot(fileName, fileMeta) + if err != nil { + return nil, err + } + } + + targets := &data.Targets{} + // 5.6.3 verify signature with parent public keys + // 5.6.5 verify that the targets is not expired + // role "targets" is a top role verified by root keys loaded in the client db + err = db.Unmarshal(raw, targets, role, fileMeta.Version) + if err != nil { + return nil, ErrDecodeFailed{fileName, err} + } + + // 5.6.6 persist + if !alreadyStored { + if err := c.local.SetMeta(fileName, raw); err != nil { + return nil, err + } + } + return targets, nil +} diff --git a/vendor/github.com/DataDog/go-tuf/client/errors.go b/vendor/github.com/DataDog/go-tuf/client/errors.go new file mode 100644 index 000000000..3e7a5dcc4 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/errors.go @@ -0,0 +1,107 @@ +package client + +import ( + "errors" + "fmt" +) + +var ( + ErrNoRootKeys = errors.New("tuf: no root keys found in local meta store") + ErrInsufficientKeys = errors.New("tuf: insufficient keys to meet threshold") + ErrNoLocalSnapshot = errors.New("tuf: no snapshot stored locally") +) + +type ErrMissingRemoteMetadata struct { + Name string +} + +func (e ErrMissingRemoteMetadata) Error() string { + return fmt.Sprintf("tuf: missing remote metadata %s", e.Name) +} + +type ErrDownloadFailed struct { + File string + Err error +} + +func (e ErrDownloadFailed) Error() string { + return fmt.Sprintf("tuf: failed to download %s: %s", e.File, e.Err) +} + +type ErrDecodeFailed struct { + File string + Err error +} + +func (e ErrDecodeFailed) Error() string { + return fmt.Sprintf("tuf: failed to decode %s: %s", e.File, e.Err) +} + +type ErrMaxDelegations struct { + Target string + MaxDelegations int + SnapshotVersion int64 +} + +func (e ErrMaxDelegations) Error() string { + return fmt.Sprintf("tuf: max delegation of %d reached searching for %s with snapshot version %d", e.MaxDelegations, e.Target, e.SnapshotVersion) +} + +type ErrNotFound struct { + File string +} + +func (e ErrNotFound) Error() string { + return fmt.Sprintf("tuf: file not found: %s", e.File) +} + +func IsNotFound(err error) bool { + _, ok := err.(ErrNotFound) + return ok +} + +type ErrWrongSize struct { + File string + Actual int64 + Expected int64 +} + +func (e ErrWrongSize) Error() string { + return fmt.Sprintf("tuf: unexpected file size: %s (expected %d bytes, got %d bytes)", e.File, e.Expected, e.Actual) +} + +type ErrUnknownTarget struct { + Name string + SnapshotVersion int64 +} + +func (e ErrUnknownTarget) Error() string { + return fmt.Sprintf("tuf: unknown target file: %s with snapshot version %d", e.Name, e.SnapshotVersion) +} + +type ErrMetaTooLarge struct { + Name string + Size int64 + MaxSize int64 +} + +func (e ErrMetaTooLarge) Error() string { + return fmt.Sprintf("tuf: %s size %d bytes greater than maximum %d bytes", e.Name, e.Size, e.MaxSize) +} + +type ErrInvalidURL struct { + URL string +} + +func (e ErrInvalidURL) Error() string { + return fmt.Sprintf("tuf: invalid repository URL %s", e.URL) +} + +type ErrRoleNotInSnapshot struct { + Role string + SnapshotVersion int64 +} + +func (e ErrRoleNotInSnapshot) Error() string { + return fmt.Sprintf("tuf: role %s not in snapshot version %d", e.Role, e.SnapshotVersion) +} diff --git a/vendor/github.com/DataDog/go-tuf/client/file_store.go b/vendor/github.com/DataDog/go-tuf/client/file_store.go new file mode 100644 index 000000000..520bbe73a --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/file_store.go @@ -0,0 +1,90 @@ +package client + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/fs" +) + +// FileRemoteStore provides a RemoteStore interface compatible +// implementation that can be used where the RemoteStore is backed by a +// fs.FS. This is useful for example in air-gapped environments where there's no +// possibility to make outbound network connections. +// By having this be a fs.FS instead of directories allows the repository to +// be backed by something that's not persisted to disk. +func NewFileRemoteStore(fsys fs.FS, targetDir string) (*FileRemoteStore, error) { + if fsys == nil { + return nil, errors.New("nil fs.FS") + } + t := targetDir + if t == "" { + t = "targets" + } + // Make sure directory exists + d, err := fsys.Open(t) + if err != nil { + return nil, fmt.Errorf("failed to open targets directory %s: %w", t, err) + } + fi, err := d.Stat() + if err != nil { + return nil, fmt.Errorf("failed to stat targets directory %s: %w", t, err) + } + if !fi.IsDir() { + return nil, fmt.Errorf("targets directory not a directory %s", t) + } + + fsysT, err := fs.Sub(fsys, t) + if err != nil { + return nil, fmt.Errorf("failed to open targets directory %s: %w", t, err) + } + return &FileRemoteStore{fsys: fsys, targetDir: fsysT}, nil +} + +type FileRemoteStore struct { + // Meta directory fs + fsys fs.FS + // Target directory fs. + targetDir fs.FS + // In order to be able to make write operations (create, delete) we can't + // use fs.FS for it (it's read only), so we have to know the underlying + // directory that add/delete test methods can use. This is only necessary + // for testing purposes. + testDir string +} + +func (f *FileRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) { + rc, b, err := f.get(f.fsys, name) + return handleErrors(name, rc, b, err) +} + +func (f *FileRemoteStore) GetTarget(name string) (io.ReadCloser, int64, error) { + rc, b, err := f.get(f.targetDir, name) + return handleErrors(name, rc, b, err) +} + +func (f *FileRemoteStore) get(fsys fs.FS, s string) (io.ReadCloser, int64, error) { + if !fs.ValidPath(s) { + return nil, 0, fmt.Errorf("invalid path %s", s) + } + + b, err := fs.ReadFile(fsys, s) + if err != nil { + return nil, -1, err + } + return io.NopCloser(bytes.NewReader(b)), int64(len(b)), nil +} + +// handleErrors converts NotFound errors to something that TUF knows how to +// handle properly. For example, when looking for n+1 root files, this is a +// signal that it will stop looking. +func handleErrors(name string, rc io.ReadCloser, b int64, err error) (io.ReadCloser, int64, error) { + if err == nil { + return rc, b, err + } + if errors.Is(err, fs.ErrNotExist) { + return rc, b, ErrNotFound{name} + } + return rc, b, err +} diff --git a/vendor/github.com/DataDog/go-tuf/client/local_store.go b/vendor/github.com/DataDog/go-tuf/client/local_store.go new file mode 100644 index 000000000..bb9421f5d --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/local_store.go @@ -0,0 +1,29 @@ +package client + +import ( + "encoding/json" +) + +func MemoryLocalStore() LocalStore { + return make(memoryLocalStore) +} + +type memoryLocalStore map[string]json.RawMessage + +func (m memoryLocalStore) GetMeta() (map[string]json.RawMessage, error) { + return m, nil +} + +func (m memoryLocalStore) SetMeta(name string, meta json.RawMessage) error { + m[name] = meta + return nil +} + +func (m memoryLocalStore) DeleteMeta(name string) error { + delete(m, name) + return nil +} + +func (m memoryLocalStore) Close() error { + return nil +} diff --git a/vendor/github.com/DataDog/go-tuf/client/remote_store.go b/vendor/github.com/DataDog/go-tuf/client/remote_store.go new file mode 100644 index 000000000..17a63fc59 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/client/remote_store.go @@ -0,0 +1,109 @@ +package client + +import ( + "fmt" + "io" + "net/http" + "net/url" + "path" + "strconv" + "strings" + "time" +) + +type HTTPRemoteOptions struct { + MetadataPath string + TargetsPath string + UserAgent string + Retries *HTTPRemoteRetries +} + +type HTTPRemoteRetries struct { + Delay time.Duration + Total time.Duration +} + +var DefaultHTTPRetries = &HTTPRemoteRetries{ + Delay: time.Second, + Total: 10 * time.Second, +} + +func HTTPRemoteStore(baseURL string, opts *HTTPRemoteOptions, client *http.Client) (RemoteStore, error) { + if !strings.HasPrefix(baseURL, "http") { + return nil, ErrInvalidURL{baseURL} + } + if opts == nil { + opts = &HTTPRemoteOptions{} + } + if opts.TargetsPath == "" { + opts.TargetsPath = "targets" + } + if client == nil { + client = http.DefaultClient + } + return &httpRemoteStore{baseURL, opts, client}, nil +} + +type httpRemoteStore struct { + baseURL string + opts *HTTPRemoteOptions + cli *http.Client +} + +func (h *httpRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) { + return h.get(path.Join(h.opts.MetadataPath, name)) +} + +func (h *httpRemoteStore) GetTarget(name string) (io.ReadCloser, int64, error) { + return h.get(path.Join(h.opts.TargetsPath, name)) +} + +func (h *httpRemoteStore) get(s string) (io.ReadCloser, int64, error) { + u := h.url(s) + req, err := http.NewRequest("GET", u, nil) + if err != nil { + return nil, 0, err + } + if h.opts.UserAgent != "" { + req.Header.Set("User-Agent", h.opts.UserAgent) + } + var res *http.Response + if r := h.opts.Retries; r != nil { + for start := time.Now(); time.Since(start) < r.Total; time.Sleep(r.Delay) { + res, err = h.cli.Do(req) + if err == nil && (res.StatusCode < 500 || res.StatusCode > 599) { + break + } + } + } else { + res, err = h.cli.Do(req) + } + if err != nil { + return nil, 0, err + } + + if res.StatusCode == http.StatusNotFound { + res.Body.Close() + return nil, 0, ErrNotFound{s} + } else if res.StatusCode != http.StatusOK { + res.Body.Close() + return nil, 0, &url.Error{ + Op: "GET", + URL: u, + Err: fmt.Errorf("unexpected HTTP status %d", res.StatusCode), + } + } + + size, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 0) + if err != nil { + return res.Body, -1, nil + } + return res.Body, size, nil +} + +func (h *httpRemoteStore) url(path string) string { + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + return h.baseURL + path +} diff --git a/vendor/github.com/DataDog/go-tuf/data/hex_bytes.go b/vendor/github.com/DataDog/go-tuf/data/hex_bytes.go new file mode 100644 index 000000000..ec200412e --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/data/hex_bytes.go @@ -0,0 +1,42 @@ +package data + +import ( + "crypto/sha256" + "encoding/hex" + "errors" +) + +type HexBytes []byte + +func (b *HexBytes) UnmarshalJSON(data []byte) error { + if len(data) < 2 || len(data)%2 != 0 || data[0] != '"' || data[len(data)-1] != '"' { + return errors.New("tuf: invalid JSON hex bytes") + } + res := make([]byte, hex.DecodedLen(len(data)-2)) + _, err := hex.Decode(res, data[1:len(data)-1]) + if err != nil { + return err + } + *b = res + return nil +} + +func (b HexBytes) MarshalJSON() ([]byte, error) { + res := make([]byte, hex.EncodedLen(len(b))+2) + res[0] = '"' + res[len(res)-1] = '"' + hex.Encode(res[1:], b) + return res, nil +} + +func (b HexBytes) String() string { + return hex.EncodeToString(b) +} + +// 4.5. File formats: targets.json and delegated target roles: +// ...each target path, when hashed with the SHA-256 hash function to produce +// a 64-byte hexadecimal digest (HEX_DIGEST)... +func PathHexDigest(s string) string { + b := sha256.Sum256([]byte(s)) + return hex.EncodeToString(b[:]) +} diff --git a/vendor/github.com/DataDog/go-tuf/data/types.go b/vendor/github.com/DataDog/go-tuf/data/types.go new file mode 100644 index 000000000..eb00489b6 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/data/types.go @@ -0,0 +1,348 @@ +package data + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "path" + "strings" + "sync" + "time" + + "github.com/secure-systems-lab/go-securesystemslib/cjson" +) + +type KeyType string + +type KeyScheme string + +type HashAlgorithm string + +const ( + KeyIDLength = sha256.Size * 2 + + KeyTypeEd25519 KeyType = "ed25519" + // From version 1.0.32, the reference implementation defines 'ecdsa', + // not 'ecdsa-sha2-nistp256' for NIST P-256 curves. + KeyTypeECDSA_SHA2_P256 KeyType = "ecdsa" + KeyTypeECDSA_SHA2_P256_OLD_FMT KeyType = "ecdsa-sha2-nistp256" + KeyTypeRSASSA_PSS_SHA256 KeyType = "rsa" + + KeySchemeEd25519 KeyScheme = "ed25519" + KeySchemeECDSA_SHA2_P256 KeyScheme = "ecdsa-sha2-nistp256" + KeySchemeRSASSA_PSS_SHA256 KeyScheme = "rsassa-pss-sha256" + + HashAlgorithmSHA256 HashAlgorithm = "sha256" + HashAlgorithmSHA512 HashAlgorithm = "sha512" +) + +var ( + HashAlgorithms = []HashAlgorithm{HashAlgorithmSHA256, HashAlgorithmSHA512} + ErrPathsAndPathHashesSet = errors.New("tuf: failed validation of delegated target: paths and path_hash_prefixes are both set") +) + +type Signed struct { + Signed json.RawMessage `json:"signed"` + Signatures []Signature `json:"signatures"` +} + +type Signature struct { + KeyID string `json:"keyid"` + Signature HexBytes `json:"sig"` +} + +type PublicKey struct { + Type KeyType `json:"keytype"` + Scheme KeyScheme `json:"scheme"` + Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"` + Value json.RawMessage `json:"keyval"` + + ids []string + idOnce sync.Once +} + +type PrivateKey struct { + Type KeyType `json:"keytype"` + Scheme KeyScheme `json:"scheme,omitempty"` + Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"` + Value json.RawMessage `json:"keyval"` +} + +func (k *PublicKey) IDs() []string { + k.idOnce.Do(func() { + data, err := cjson.EncodeCanonical(k) + if err != nil { + panic(fmt.Errorf("tuf: error creating key ID: %w", err)) + } + digest := sha256.Sum256(data) + k.ids = []string{hex.EncodeToString(digest[:])} + }) + return k.ids +} + +func (k *PublicKey) ContainsID(id string) bool { + for _, keyid := range k.IDs() { + if id == keyid { + return true + } + } + return false +} + +func DefaultExpires(role string) time.Time { + var t time.Time + switch role { + case "root": + t = time.Now().AddDate(1, 0, 0) + case "snapshot": + t = time.Now().AddDate(0, 0, 7) + case "timestamp": + t = time.Now().AddDate(0, 0, 1) + default: + // targets and delegated targets + t = time.Now().AddDate(0, 3, 0) + } + return t.UTC().Round(time.Second) +} + +type Root struct { + Type string `json:"_type"` + SpecVersion string `json:"spec_version"` + Version int64 `json:"version"` + Expires time.Time `json:"expires"` + Keys map[string]*PublicKey `json:"keys"` + Roles map[string]*Role `json:"roles"` + Custom *json.RawMessage `json:"custom,omitempty"` + + ConsistentSnapshot bool `json:"consistent_snapshot"` +} + +func NewRoot() *Root { + return &Root{ + Type: "root", + SpecVersion: "1.0", + Expires: DefaultExpires("root"), + Keys: make(map[string]*PublicKey), + Roles: make(map[string]*Role), + ConsistentSnapshot: true, + } +} + +func (r *Root) AddKey(key *PublicKey) bool { + changed := false + for _, id := range key.IDs() { + if _, ok := r.Keys[id]; !ok { + changed = true + r.Keys[id] = key + } + } + return changed +} + +type Role struct { + KeyIDs []string `json:"keyids"` + Threshold int `json:"threshold"` +} + +func (r *Role) AddKeyIDs(ids []string) bool { + roleIDs := make(map[string]struct{}) + for _, id := range r.KeyIDs { + roleIDs[id] = struct{}{} + } + changed := false + for _, id := range ids { + if _, ok := roleIDs[id]; !ok { + changed = true + r.KeyIDs = append(r.KeyIDs, id) + } + } + return changed +} + +type Files map[string]TargetFileMeta + +type Hashes map[string]HexBytes + +func (f Hashes) HashAlgorithms() []string { + funcs := make([]string, 0, len(f)) + for name := range f { + funcs = append(funcs, name) + } + return funcs +} + +type metapathFileMeta struct { + Length int64 `json:"length,omitempty"` + Hashes Hashes `json:"hashes,omitempty"` + Version int64 `json:"version"` + Custom *json.RawMessage `json:"custom,omitempty"` +} + +// SnapshotFileMeta is the meta field of a snapshot +// Note: Contains a `custom` field +type SnapshotFileMeta metapathFileMeta + +type SnapshotFiles map[string]SnapshotFileMeta + +type Snapshot struct { + Type string `json:"_type"` + SpecVersion string `json:"spec_version"` + Version int64 `json:"version"` + Expires time.Time `json:"expires"` + Meta SnapshotFiles `json:"meta"` + Custom *json.RawMessage `json:"custom,omitempty"` +} + +func NewSnapshot() *Snapshot { + return &Snapshot{ + Type: "snapshot", + SpecVersion: "1.0", + Expires: DefaultExpires("snapshot"), + Meta: make(SnapshotFiles), + } +} + +type FileMeta struct { + Length int64 `json:"length"` + Hashes Hashes `json:"hashes"` +} + +type TargetFiles map[string]TargetFileMeta + +type TargetFileMeta struct { + FileMeta + Custom *json.RawMessage `json:"custom,omitempty"` +} + +func (f TargetFileMeta) HashAlgorithms() []string { + return f.FileMeta.Hashes.HashAlgorithms() +} + +type Targets struct { + Type string `json:"_type"` + SpecVersion string `json:"spec_version"` + Version int64 `json:"version"` + Expires time.Time `json:"expires"` + Targets TargetFiles `json:"targets"` + Delegations *Delegations `json:"delegations,omitempty"` + Custom *json.RawMessage `json:"custom,omitempty"` +} + +// Delegations represents the edges from a parent Targets role to one or more +// delegated target roles. See spec v1.0.19 section 4.5. +type Delegations struct { + Keys map[string]*PublicKey `json:"keys"` + Roles []DelegatedRole `json:"roles"` +} + +// DelegatedRole describes a delegated role, including what paths it is +// reponsible for. See spec v1.0.19 section 4.5. +type DelegatedRole struct { + Name string `json:"name"` + KeyIDs []string `json:"keyids"` + Threshold int `json:"threshold"` + Terminating bool `json:"terminating"` + PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` + Paths []string `json:"paths"` +} + +// MatchesPath evaluates whether the path patterns or path hash prefixes match +// a given file. This determines whether a delegated role is responsible for +// signing and verifying the file. +func (d *DelegatedRole) MatchesPath(file string) (bool, error) { + if err := d.validatePaths(); err != nil { + return false, err + } + + for _, pattern := range d.Paths { + if matched, _ := path.Match(pattern, file); matched { + return true, nil + } + } + + pathHash := PathHexDigest(file) + for _, hashPrefix := range d.PathHashPrefixes { + if strings.HasPrefix(pathHash, hashPrefix) { + return true, nil + } + } + + return false, nil +} + +// validatePaths enforces the spec +// https://theupdateframework.github.io/specification/v1.0.19/index.html#file-formats-targets +// 'role MUST specify only one of the "path_hash_prefixes" or "paths"' +// Marshalling and unmarshalling JSON will fail and return +// ErrPathsAndPathHashesSet if both fields are set and not empty. +func (d *DelegatedRole) validatePaths() error { + if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 { + return ErrPathsAndPathHashesSet + } + + return nil +} + +// MarshalJSON is called when writing the struct to JSON. We validate prior to +// marshalling to ensure that an invalid delegated role can not be serialized +// to JSON. +func (d *DelegatedRole) MarshalJSON() ([]byte, error) { + type delegatedRoleAlias DelegatedRole + + if err := d.validatePaths(); err != nil { + return nil, err + } + + return json.Marshal((*delegatedRoleAlias)(d)) +} + +// UnmarshalJSON is called when reading the struct from JSON. We validate once +// unmarshalled to ensure that an error is thrown if an invalid delegated role +// is read. +func (d *DelegatedRole) UnmarshalJSON(b []byte) error { + type delegatedRoleAlias DelegatedRole + + // Prepare decoder + dec := json.NewDecoder(bytes.NewReader(b)) + + // Unmarshal delegated role + if err := dec.Decode((*delegatedRoleAlias)(d)); err != nil { + return err + } + + return d.validatePaths() +} + +func NewTargets() *Targets { + return &Targets{ + Type: "targets", + SpecVersion: "1.0", + Expires: DefaultExpires("targets"), + Targets: make(TargetFiles), + } +} + +type TimestampFileMeta metapathFileMeta + +type TimestampFiles map[string]TimestampFileMeta + +type Timestamp struct { + Type string `json:"_type"` + SpecVersion string `json:"spec_version"` + Version int64 `json:"version"` + Expires time.Time `json:"expires"` + Meta TimestampFiles `json:"meta"` + Custom *json.RawMessage `json:"custom,omitempty"` +} + +func NewTimestamp() *Timestamp { + return &Timestamp{ + Type: "timestamp", + SpecVersion: "1.0", + Expires: DefaultExpires("timestamp"), + Meta: make(TimestampFiles), + } +} diff --git a/vendor/github.com/DataDog/go-tuf/internal/roles/roles.go b/vendor/github.com/DataDog/go-tuf/internal/roles/roles.go new file mode 100644 index 000000000..0b134b2a0 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/internal/roles/roles.go @@ -0,0 +1,48 @@ +package roles + +import ( + "strconv" + "strings" +) + +var TopLevelRoles = map[string]struct{}{ + "root": {}, + "targets": {}, + "snapshot": {}, + "timestamp": {}, +} + +func IsTopLevelRole(name string) bool { + _, ok := TopLevelRoles[name] + return ok +} + +func IsDelegatedTargetsRole(name string) bool { + return !IsTopLevelRole(name) +} + +func IsTopLevelManifest(name string) bool { + if IsVersionedManifest(name) { + var found bool + _, name, found = strings.Cut(name, ".") + if !found { + panic("expected a versioned manifest of the form x.role.json") + } + } + return IsTopLevelRole(strings.TrimSuffix(name, ".json")) +} + +func IsDelegatedTargetsManifest(name string) bool { + return !IsTopLevelManifest(name) +} + +func IsVersionedManifest(name string) bool { + parts := strings.Split(name, ".") + // Versioned manifests have the form "x.role.json" + if len(parts) < 3 { + return false + } + + _, err := strconv.Atoi(parts[0]) + return err == nil +} diff --git a/vendor/github.com/DataDog/go-tuf/internal/sets/strings.go b/vendor/github.com/DataDog/go-tuf/internal/sets/strings.go new file mode 100644 index 000000000..7eee57d09 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/internal/sets/strings.go @@ -0,0 +1,24 @@ +package sets + +func StringSliceToSet(items []string) map[string]struct{} { + s := map[string]struct{}{} + for _, item := range items { + s[item] = struct{}{} + } + return s +} + +func StringSetToSlice(items map[string]struct{}) []string { + ret := []string{} + + for k := range items { + ret = append(ret, k) + } + + return ret +} + +func DeduplicateStrings(items []string) []string { + s := StringSliceToSet(items) + return StringSetToSlice(s) +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/deprecated_ecdsa.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/deprecated_ecdsa.go new file mode 100644 index 000000000..6c4c20682 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/deprecated_ecdsa.go @@ -0,0 +1,101 @@ +package keys + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "io" + + "github.com/DataDog/go-tuf/data" +) + +func NewDeprecatedEcdsaVerifier() Verifier { + return &ecdsaVerifierWithDeprecatedSupport{} +} + +type ecdsaVerifierWithDeprecatedSupport struct { + key *data.PublicKey + // This will switch based on whether this is a PEM-encoded key + // or a deprecated hex-encoded key. + Verifier +} + +func (p *ecdsaVerifierWithDeprecatedSupport) UnmarshalPublicKey(key *data.PublicKey) error { + p.key = key + pemVerifier := &EcdsaVerifier{} + if err := pemVerifier.UnmarshalPublicKey(key); err != nil { + // Try the deprecated hex-encoded verifier + hexVerifier := &deprecatedP256Verifier{} + if err := hexVerifier.UnmarshalPublicKey(key); err != nil { + return err + } + p.Verifier = hexVerifier + return nil + } + p.Verifier = pemVerifier + return nil +} + +/* + Deprecated ecdsaVerifier that used hex-encoded public keys. + This MAY be used to verify existing metadata that used this + old format. This will be deprecated soon, ensure that repositories + are re-signed and clients receieve a fully compliant root. +*/ + +type deprecatedP256Verifier struct { + PublicKey data.HexBytes `json:"public"` + key *data.PublicKey +} + +func (p *deprecatedP256Verifier) Public() string { + return p.PublicKey.String() +} + +func (p *deprecatedP256Verifier) Verify(msg, sigBytes []byte) error { + x, y := elliptic.Unmarshal(elliptic.P256(), p.PublicKey) + k := &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } + + hash := sha256.Sum256(msg) + + if !ecdsa.VerifyASN1(k, hash[:], sigBytes) { + return errors.New("tuf: deprecated ecdsa signature verification failed") + } + return nil +} + +func (p *deprecatedP256Verifier) MarshalPublicKey() *data.PublicKey { + return p.key +} + +func (p *deprecatedP256Verifier) UnmarshalPublicKey(key *data.PublicKey) error { + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(p); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } + return err + } + + curve := elliptic.P256() + + // Parse as uncompressed marshalled point. + x, _ := elliptic.Unmarshal(curve, p.PublicKey) + if x == nil { + return errors.New("tuf: invalid ecdsa public key point") + } + + p.key = key + return nil +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/ecdsa.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/ecdsa.go new file mode 100644 index 000000000..ea75b97e8 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/ecdsa.go @@ -0,0 +1,173 @@ +package keys + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/x509" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" + + "github.com/DataDog/go-tuf/data" +) + +func init() { + // Note: we use LoadOrStore here to prevent accidentally overriding the + // an explicit deprecated ECDSA verifier. + // TODO: When deprecated ECDSA is removed, this can switch back to Store. + VerifierMap.LoadOrStore(data.KeyTypeECDSA_SHA2_P256_OLD_FMT, NewEcdsaVerifier) + VerifierMap.LoadOrStore(data.KeyTypeECDSA_SHA2_P256, NewEcdsaVerifier) + SignerMap.Store(data.KeyTypeECDSA_SHA2_P256_OLD_FMT, newEcdsaSigner) + SignerMap.Store(data.KeyTypeECDSA_SHA2_P256, newEcdsaSigner) +} + +func NewEcdsaVerifier() Verifier { + return &EcdsaVerifier{} +} + +func newEcdsaSigner() Signer { + return &ecdsaSigner{} +} + +type EcdsaVerifier struct { + PublicKey *PKIXPublicKey `json:"public"` + ecdsaKey *ecdsa.PublicKey + key *data.PublicKey +} + +func (p *EcdsaVerifier) Public() string { + // This is already verified to succeed when unmarshalling a public key. + r, err := x509.MarshalPKIXPublicKey(p.ecdsaKey) + if err != nil { + // TODO: Gracefully handle these errors. + // See https://github.com/DataDog/go-tuf/issues/363 + panic(err) + } + return string(r) +} + +func (p *EcdsaVerifier) Verify(msg, sigBytes []byte) error { + hash := sha256.Sum256(msg) + + if !ecdsa.VerifyASN1(p.ecdsaKey, hash[:], sigBytes) { + return errors.New("tuf: ecdsa signature verification failed") + } + return nil +} + +func (p *EcdsaVerifier) MarshalPublicKey() *data.PublicKey { + return p.key +} + +func (p *EcdsaVerifier) UnmarshalPublicKey(key *data.PublicKey) error { + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(p); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } + return err + } + + ecdsaKey, ok := p.PublicKey.PublicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("invalid public key") + } + + if _, err := x509.MarshalPKIXPublicKey(ecdsaKey); err != nil { + return fmt.Errorf("marshalling to PKIX key: invalid public key") + } + + p.ecdsaKey = ecdsaKey + p.key = key + return nil +} + +type ecdsaSigner struct { + *ecdsa.PrivateKey +} + +type ecdsaPrivateKeyValue struct { + Private string `json:"private"` + Public *PKIXPublicKey `json:"public"` +} + +func (s *ecdsaSigner) PublicData() *data.PublicKey { + // This uses a trusted public key JSON format with a trusted Public value. + keyValBytes, _ := json.Marshal(EcdsaVerifier{PublicKey: &PKIXPublicKey{PublicKey: s.Public()}}) + return &data.PublicKey{ + Type: data.KeyTypeECDSA_SHA2_P256, + Scheme: data.KeySchemeECDSA_SHA2_P256, + Algorithms: data.HashAlgorithms, + Value: keyValBytes, + } +} + +func (s *ecdsaSigner) SignMessage(message []byte) ([]byte, error) { + hash := sha256.Sum256(message) + return ecdsa.SignASN1(rand.Reader, s.PrivateKey, hash[:]) +} + +func (s *ecdsaSigner) MarshalPrivateKey() (*data.PrivateKey, error) { + priv, err := x509.MarshalECPrivateKey(s.PrivateKey) + if err != nil { + return nil, err + } + pemKey := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: priv}) + val, err := json.Marshal(ecdsaPrivateKeyValue{ + Private: string(pemKey), + Public: &PKIXPublicKey{PublicKey: s.Public()}, + }) + if err != nil { + return nil, err + } + return &data.PrivateKey{ + Type: data.KeyTypeECDSA_SHA2_P256, + Scheme: data.KeySchemeECDSA_SHA2_P256, + Algorithms: data.HashAlgorithms, + Value: val, + }, nil +} + +func (s *ecdsaSigner) UnmarshalPrivateKey(key *data.PrivateKey) error { + val := ecdsaPrivateKeyValue{} + if err := json.Unmarshal(key.Value, &val); err != nil { + return err + } + block, _ := pem.Decode([]byte(val.Private)) + if block == nil { + return errors.New("invalid PEM value") + } + if block.Type != "EC PRIVATE KEY" { + return fmt.Errorf("invalid block type: %s", block.Type) + } + k, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return err + } + if k.Curve != elliptic.P256() { + return errors.New("unsupported ecdsa curve") + } + if _, err := json.Marshal(EcdsaVerifier{ + PublicKey: &PKIXPublicKey{PublicKey: k.Public()}}); err != nil { + return fmt.Errorf("invalid public key: %s", err) + } + + s.PrivateKey = k + return nil +} + +func GenerateEcdsaKey() (*ecdsaSigner, error) { + privkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + return &ecdsaSigner{privkey}, nil +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/ed25519.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/ed25519.go new file mode 100644 index 000000000..4667147fd --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/ed25519.go @@ -0,0 +1,161 @@ +package keys + +import ( + "bytes" + "crypto" + "crypto/ed25519" + "crypto/rand" + "crypto/subtle" + "encoding/json" + "errors" + "fmt" + "io" + + "github.com/DataDog/go-tuf/data" +) + +func init() { + SignerMap.Store(data.KeyTypeEd25519, NewEd25519Signer) + VerifierMap.Store(data.KeyTypeEd25519, NewEd25519Verifier) +} + +func NewEd25519Signer() Signer { + return &ed25519Signer{} +} + +func NewEd25519Verifier() Verifier { + return &ed25519Verifier{} +} + +type ed25519Verifier struct { + PublicKey data.HexBytes `json:"public"` + key *data.PublicKey +} + +func (e *ed25519Verifier) Public() string { + return string(e.PublicKey) +} + +func (e *ed25519Verifier) Verify(msg, sig []byte) error { + if !ed25519.Verify([]byte(e.PublicKey), msg, sig) { + return errors.New("tuf: ed25519 signature verification failed") + } + return nil +} + +func (e *ed25519Verifier) MarshalPublicKey() *data.PublicKey { + return e.key +} + +func (e *ed25519Verifier) UnmarshalPublicKey(key *data.PublicKey) error { + e.key = key + + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(e); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } + return err + } + if n := len(e.PublicKey); n != ed25519.PublicKeySize { + return fmt.Errorf("tuf: unexpected public key length for ed25519 key, expected %d, got %d", ed25519.PublicKeySize, n) + } + return nil +} + +type Ed25519PrivateKeyValue struct { + Public data.HexBytes `json:"public"` + Private data.HexBytes `json:"private"` +} + +type ed25519Signer struct { + ed25519.PrivateKey +} + +func GenerateEd25519Key() (*ed25519Signer, error) { + _, private, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + if err != nil { + return nil, err + } + return &ed25519Signer{ + PrivateKey: ed25519.PrivateKey(data.HexBytes(private)), + }, nil +} + +func NewEd25519SignerFromKey(keyValue Ed25519PrivateKeyValue) *ed25519Signer { + return &ed25519Signer{ + PrivateKey: ed25519.PrivateKey(data.HexBytes(keyValue.Private)), + } +} + +func (e *ed25519Signer) SignMessage(message []byte) ([]byte, error) { + return e.Sign(rand.Reader, message, crypto.Hash(0)) +} + +func (e *ed25519Signer) MarshalPrivateKey() (*data.PrivateKey, error) { + valueBytes, err := json.Marshal(Ed25519PrivateKeyValue{ + Public: data.HexBytes([]byte(e.PrivateKey.Public().(ed25519.PublicKey))), + Private: data.HexBytes(e.PrivateKey), + }) + if err != nil { + return nil, err + } + return &data.PrivateKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: valueBytes, + }, nil +} + +func (e *ed25519Signer) UnmarshalPrivateKey(key *data.PrivateKey) error { + keyValue := &Ed25519PrivateKeyValue{} + + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(keyValue); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the private key is truncated or too large: %w", err) + } + } + + // Check private key length + if n := len(keyValue.Private); n != ed25519.PrivateKeySize { + return fmt.Errorf("tuf: invalid ed25519 private key length, expected %d, got %d", ed25519.PrivateKeySize, n) + } + + // Generate public key from private key + pub, _, err := ed25519.GenerateKey(bytes.NewReader(keyValue.Private)) + if err != nil { + return fmt.Errorf("tuf: unable to derive public key from private key: %w", err) + } + + // Compare keys + if subtle.ConstantTimeCompare(keyValue.Public, pub) != 1 { + return errors.New("tuf: public and private keys don't match") + } + + // Prepare signer + *e = ed25519Signer{ + PrivateKey: ed25519.PrivateKey(data.HexBytes(keyValue.Private)), + } + return nil +} + +func (e *ed25519Signer) PublicData() *data.PublicKey { + keyValBytes, _ := json.Marshal(ed25519Verifier{PublicKey: []byte(e.PrivateKey.Public().(ed25519.PublicKey))}) + return &data.PublicKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: keyValBytes, + } +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/keys.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/keys.go new file mode 100644 index 000000000..7fc25316e --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/keys.go @@ -0,0 +1,82 @@ +package keys + +import ( + "errors" + "fmt" + "sync" + + "github.com/DataDog/go-tuf/data" +) + +// MaxJSONKeySize defines the maximum length of a JSON payload. +const MaxJSONKeySize = 512 * 1024 // 512Kb + +// SignerMap stores mapping between key type strings and signer constructors. +var SignerMap sync.Map + +// Verifier stores mapping between key type strings and verifier constructors. +var VerifierMap sync.Map + +var ( + ErrInvalid = errors.New("tuf: signature verification failed") + ErrInvalidKey = errors.New("invalid key") +) + +// A Verifier verifies public key signatures. +type Verifier interface { + // UnmarshalPublicKey takes key data to a working verifier implementation for the key type. + // This performs any validation over the data.PublicKey to ensure that the verifier is usable + // to verify signatures. + UnmarshalPublicKey(key *data.PublicKey) error + + // MarshalPublicKey returns the data.PublicKey object associated with the verifier. + MarshalPublicKey() *data.PublicKey + + // This is the public string used as a unique identifier for the verifier instance. + Public() string + + // Verify takes a message and signature, all as byte slices, + // and determines whether the signature is valid for the given + // key and message. + Verify(msg, sig []byte) error +} + +type Signer interface { + // MarshalPrivateKey returns the private key data. + MarshalPrivateKey() (*data.PrivateKey, error) + + // UnmarshalPrivateKey takes private key data to a working Signer implementation for the key type. + UnmarshalPrivateKey(key *data.PrivateKey) error + + // Returns the public data.PublicKey from the private key + PublicData() *data.PublicKey + + // Sign returns the signature of the message. + // The signer is expected to do its own hashing, so the full message will be + // provided as the message to Sign with a zero opts.HashFunc(). + SignMessage(message []byte) ([]byte, error) +} + +func GetVerifier(key *data.PublicKey) (Verifier, error) { + st, ok := VerifierMap.Load(key.Type) + if !ok { + return nil, ErrInvalidKey + } + s := st.(func() Verifier)() + if err := s.UnmarshalPublicKey(key); err != nil { + return nil, fmt.Errorf("tuf: error unmarshalling key: %w", err) + } + return s, nil +} + +func GetSigner(key *data.PrivateKey) (Signer, error) { + st, ok := SignerMap.Load(key.Type) + if !ok { + return nil, ErrInvalidKey + } + s := st.(func() Signer)() + if err := s.UnmarshalPrivateKey(key); err != nil { + return nil, fmt.Errorf("tuf: error unmarshalling key: %w", err) + } + return s, nil +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/pkix.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/pkix.go new file mode 100644 index 000000000..e58d4c9f8 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/pkix.go @@ -0,0 +1,56 @@ +package keys + +import ( + "bytes" + "crypto" + "crypto/x509" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" +) + +type PKIXPublicKey struct { + crypto.PublicKey +} + +func (p *PKIXPublicKey) MarshalJSON() ([]byte, error) { + bytes, err := x509.MarshalPKIXPublicKey(p.PublicKey) + if err != nil { + return nil, err + } + pemBytes := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: bytes, + }) + return json.Marshal(string(pemBytes)) +} + +func (p *PKIXPublicKey) UnmarshalJSON(b []byte) error { + var pemValue string + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(b), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(&pemValue); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } + return err + } + + block, _ := pem.Decode([]byte(pemValue)) + if block == nil { + return errors.New("invalid PEM value") + } + if block.Type != "PUBLIC KEY" { + return fmt.Errorf("invalid block type: %s", block.Type) + } + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return err + } + p.PublicKey = pub + return nil +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/keys/rsa.go b/vendor/github.com/DataDog/go-tuf/pkg/keys/rsa.go new file mode 100644 index 000000000..17d7690a7 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/keys/rsa.go @@ -0,0 +1,162 @@ +package keys + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" + + "github.com/DataDog/go-tuf/data" +) + +func init() { + VerifierMap.Store(data.KeyTypeRSASSA_PSS_SHA256, newRsaVerifier) + SignerMap.Store(data.KeyTypeRSASSA_PSS_SHA256, newRsaSigner) +} + +func newRsaVerifier() Verifier { + return &rsaVerifier{} +} + +func newRsaSigner() Signer { + return &rsaSigner{} +} + +type rsaVerifier struct { + PublicKey *PKIXPublicKey `json:"public"` + rsaKey *rsa.PublicKey + key *data.PublicKey +} + +func (p *rsaVerifier) Public() string { + // This is already verified to succeed when unmarshalling a public key. + r, err := x509.MarshalPKIXPublicKey(p.rsaKey) + if err != nil { + // TODO: Gracefully handle these errors. + // See https://github.com/DataDog/go-tuf/issues/363 + panic(err) + } + return string(r) +} + +func (p *rsaVerifier) Verify(msg, sigBytes []byte) error { + hash := sha256.Sum256(msg) + + return rsa.VerifyPSS(p.rsaKey, crypto.SHA256, hash[:], sigBytes, &rsa.PSSOptions{}) +} + +func (p *rsaVerifier) MarshalPublicKey() *data.PublicKey { + return p.key +} + +func (p *rsaVerifier) UnmarshalPublicKey(key *data.PublicKey) error { + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(p); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } + return err + } + + rsaKey, ok := p.PublicKey.PublicKey.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("invalid public key") + } + + if _, err := x509.MarshalPKIXPublicKey(rsaKey); err != nil { + return fmt.Errorf("marshalling to PKIX key: invalid public key") + } + + p.rsaKey = rsaKey + p.key = key + return nil +} + +type rsaSigner struct { + *rsa.PrivateKey +} + +type rsaPrivateKeyValue struct { + Private string `json:"private"` + Public *PKIXPublicKey `json:"public"` +} + +func (s *rsaSigner) PublicData() *data.PublicKey { + keyValBytes, _ := json.Marshal(rsaVerifier{PublicKey: &PKIXPublicKey{PublicKey: s.Public()}}) + return &data.PublicKey{ + Type: data.KeyTypeRSASSA_PSS_SHA256, + Scheme: data.KeySchemeRSASSA_PSS_SHA256, + Algorithms: data.HashAlgorithms, + Value: keyValBytes, + } +} + +func (s *rsaSigner) SignMessage(message []byte) ([]byte, error) { + hash := sha256.Sum256(message) + return rsa.SignPSS(rand.Reader, s.PrivateKey, crypto.SHA256, hash[:], &rsa.PSSOptions{}) +} + +func (s *rsaSigner) ContainsID(id string) bool { + return s.PublicData().ContainsID(id) +} + +func (s *rsaSigner) MarshalPrivateKey() (*data.PrivateKey, error) { + priv := x509.MarshalPKCS1PrivateKey(s.PrivateKey) + pemKey := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: priv}) + val, err := json.Marshal(rsaPrivateKeyValue{ + Private: string(pemKey), + Public: &PKIXPublicKey{PublicKey: s.Public()}, + }) + if err != nil { + return nil, err + } + return &data.PrivateKey{ + Type: data.KeyTypeRSASSA_PSS_SHA256, + Scheme: data.KeySchemeRSASSA_PSS_SHA256, + Algorithms: data.HashAlgorithms, + Value: val, + }, nil +} + +func (s *rsaSigner) UnmarshalPrivateKey(key *data.PrivateKey) error { + val := rsaPrivateKeyValue{} + if err := json.Unmarshal(key.Value, &val); err != nil { + return err + } + block, _ := pem.Decode([]byte(val.Private)) + if block == nil { + return errors.New("invalid PEM value") + } + if block.Type != "RSA PRIVATE KEY" { + return fmt.Errorf("invalid block type: %s", block.Type) + } + k, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + if _, err := json.Marshal(rsaVerifier{ + PublicKey: &PKIXPublicKey{PublicKey: k.Public()}}); err != nil { + return fmt.Errorf("invalid public key: %s", err) + } + + s.PrivateKey = k + return nil +} + +func GenerateRsaKey() (*rsaSigner, error) { + privkey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + return &rsaSigner{privkey}, nil +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/targets/delegation.go b/vendor/github.com/DataDog/go-tuf/pkg/targets/delegation.go new file mode 100644 index 000000000..d155d070f --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/targets/delegation.go @@ -0,0 +1,102 @@ +package targets + +import ( + "errors" + + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/internal/sets" + "github.com/DataDog/go-tuf/verify" +) + +type Delegation struct { + Delegator string + Delegatee data.DelegatedRole + DB *verify.DB +} + +type delegationsIterator struct { + stack []Delegation + target string + visitedRoles map[string]struct{} + parents map[string]string +} + +var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role missing from top level keys DB") + +// NewDelegationsIterator initialises an iterator with a first step +// on top level targets. +func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegationsIterator, error) { + targetsRole := topLevelKeysDB.GetRole("targets") + if targetsRole == nil { + return nil, ErrTopLevelTargetsRoleMissing + } + + i := &delegationsIterator{ + target: target, + stack: []Delegation{ + { + Delegatee: data.DelegatedRole{ + Name: "targets", + KeyIDs: sets.StringSetToSlice(targetsRole.KeyIDs), + Threshold: targetsRole.Threshold, + }, + DB: topLevelKeysDB, + }, + }, + visitedRoles: make(map[string]struct{}), + parents: make(map[string]string), + } + return i, nil +} + +func (d *delegationsIterator) Next() (value Delegation, ok bool) { + if len(d.stack) == 0 { + return Delegation{}, false + } + delegation := d.stack[len(d.stack)-1] + d.stack = d.stack[:len(d.stack)-1] + + // 5.6.7.1: If this role has been visited before, then skip this role (so + // that cycles in the delegation graph are avoided). + roleName := delegation.Delegatee.Name + if _, ok := d.visitedRoles[roleName]; ok { + return d.Next() + } + d.visitedRoles[roleName] = struct{}{} + + // 5.6.7.2 trim delegations to visit, only the current role and its delegations + // will be considered + // https://github.com/theupdateframework/specification/issues/168 + if delegation.Delegatee.Terminating { + // Empty the stack. + d.stack = d.stack[0:0] + } + return delegation, true +} + +func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string, db *verify.DB) error { + for i := len(roles) - 1; i >= 0; i-- { + // Push the roles onto the stack in reverse so we get an preorder traversal + // of the delegations graph. + r := roles[i] + matchesPath, err := r.MatchesPath(d.target) + if err != nil { + return err + } + if matchesPath { + delegation := Delegation{ + Delegator: delegator, + Delegatee: r, + DB: db, + } + d.stack = append(d.stack, delegation) + d.parents[r.Name] = delegator + } + } + + return nil +} + +func (d *delegationsIterator) Parent(role string) string { + return d.parents[role] +} diff --git a/vendor/github.com/DataDog/go-tuf/pkg/targets/hash_bins.go b/vendor/github.com/DataDog/go-tuf/pkg/targets/hash_bins.go new file mode 100644 index 000000000..95f4405d4 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/pkg/targets/hash_bins.go @@ -0,0 +1,113 @@ +package targets + +import ( + "fmt" + "strconv" + "strings" +) + +const MinDelegationHashPrefixBitLen = 1 +const MaxDelegationHashPrefixBitLen = 32 + +// hexEncode formats x as a hex string. The hex string is left padded with +// zeros to padWidth, if necessary. +func hexEncode(x uint64, padWidth int) string { + // Benchmarked to be more than 10x faster than padding with Sprintf. + s := strconv.FormatUint(x, 16) + if len(s) >= padWidth { + return s + } + return strings.Repeat("0", padWidth-len(s)) + s +} + +const bitsPerHexDigit = 4 + +// numHexDigits returns the number of hex digits required to encode the given +// number of bits. +func numHexDigits(numBits int) int { + // ceil(numBits / bitsPerHexDigit) + return ((numBits - 1) / bitsPerHexDigit) + 1 +} + +// HashBins represents an ordered list of hash bin target roles, which together +// partition the space of target path hashes equal-sized buckets based on path +// has prefix. +type HashBins struct { + rolePrefix string + bitLen int + hexDigitLen int + + numBins uint64 + numPrefixesPerBin uint64 +} + +// NewHashBins creates a HashBins partitioning with 2^bitLen buckets. +func NewHashBins(rolePrefix string, bitLen int) (*HashBins, error) { + if bitLen < MinDelegationHashPrefixBitLen || bitLen > MaxDelegationHashPrefixBitLen { + return nil, fmt.Errorf("bitLen is out of bounds, should be between %v and %v inclusive", MinDelegationHashPrefixBitLen, MaxDelegationHashPrefixBitLen) + } + + hexDigitLen := numHexDigits(bitLen) + numBins := uint64(1) << bitLen + + numPrefixesTotal := uint64(1) << (bitsPerHexDigit * hexDigitLen) + numPrefixesPerBin := numPrefixesTotal / numBins + + return &HashBins{ + rolePrefix: rolePrefix, + bitLen: bitLen, + hexDigitLen: hexDigitLen, + numBins: numBins, + numPrefixesPerBin: numPrefixesPerBin, + }, nil +} + +// NumBins returns the number of hash bin partitions. +func (hb *HashBins) NumBins() uint64 { + return hb.numBins +} + +// GetBin returns the HashBin at index i, or nil if i is out of bounds. +func (hb *HashBins) GetBin(i uint64) *HashBin { + if i >= hb.numBins { + return nil + } + + return &HashBin{ + rolePrefix: hb.rolePrefix, + hexDigitLen: hb.hexDigitLen, + first: i * hb.numPrefixesPerBin, + last: ((i + 1) * hb.numPrefixesPerBin) - 1, + } +} + +// HashBin represents a hex prefix range. First should be less than Last. +type HashBin struct { + rolePrefix string + hexDigitLen int + first uint64 + last uint64 +} + +// RoleName returns the name of the role that signs for the HashBin. +func (b *HashBin) RoleName() string { + if b.first == b.last { + return b.rolePrefix + hexEncode(b.first, b.hexDigitLen) + } + + return b.rolePrefix + hexEncode(b.first, b.hexDigitLen) + "-" + hexEncode(b.last, b.hexDigitLen) +} + +// HashPrefixes returns a slice of all hash prefixes in the bin. +func (b *HashBin) HashPrefixes() []string { + n := int(b.last - b.first + 1) + ret := make([]string, int(n)) + + x := b.first + for i := 0; i < n; i++ { + ret[i] = hexEncode(x, b.hexDigitLen) + x++ + } + + return ret +} diff --git a/vendor/github.com/DataDog/go-tuf/util/util.go b/vendor/github.com/DataDog/go-tuf/util/util.go new file mode 100644 index 000000000..a9b23b007 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/util/util.go @@ -0,0 +1,332 @@ +package util + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" + "encoding/json" + "fmt" + "hash" + "io" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + "github.com/DataDog/go-tuf/data" +) + +type ErrWrongLength struct { + Expected int64 + Actual int64 +} + +func (e ErrWrongLength) Error() string { + return fmt.Sprintf("wrong length, expected %d got %d", e.Expected, e.Actual) +} + +type ErrWrongVersion struct { + Expected int64 + Actual int64 +} + +func (e ErrWrongVersion) Error() string { + return fmt.Sprintf("wrong version, expected %d got %d", e.Expected, e.Actual) +} + +type ErrWrongHash struct { + Type string + Expected data.HexBytes + Actual data.HexBytes +} + +func (e ErrWrongHash) Error() string { + return fmt.Sprintf("wrong %s hash, expected %s got %s", e.Type, hex.EncodeToString(e.Expected), hex.EncodeToString(e.Actual)) +} + +type ErrNoCommonHash struct { + Expected data.Hashes + Actual data.Hashes +} + +func (e ErrNoCommonHash) Error() string { + types := func(a data.Hashes) []string { + t := make([]string, 0, len(a)) + for typ := range a { + t = append(t, typ) + } + return t + } + return fmt.Sprintf("no common hash function, expected one of %s, got %s", types(e.Expected), types(e.Actual)) +} + +type ErrUnknownHashAlgorithm struct { + Name string +} + +func (e ErrUnknownHashAlgorithm) Error() string { + return fmt.Sprintf("unknown hash algorithm: %s", e.Name) +} + +type PassphraseFunc func(role string, confirm bool, change bool) ([]byte, error) + +func FileMetaEqual(actual data.FileMeta, expected data.FileMeta) error { + if actual.Length != expected.Length { + return ErrWrongLength{expected.Length, actual.Length} + } + + if err := hashEqual(actual.Hashes, expected.Hashes); err != nil { + return err + } + + return nil +} + +func BytesMatchLenAndHashes(fetched []byte, length int64, hashes data.Hashes) error { + flen := int64(len(fetched)) + if length != 0 && flen != length { + return ErrWrongLength{length, flen} + } + + for alg, expected := range hashes { + var h hash.Hash + switch alg { + case "sha256": + h = sha256.New() + case "sha512": + h = sha512.New() + default: + return ErrUnknownHashAlgorithm{alg} + } + h.Write(fetched) + hash := h.Sum(nil) + if !hmac.Equal(hash, expected) { + return ErrWrongHash{alg, expected, hash} + } + } + + return nil +} + +func hashEqual(actual data.Hashes, expected data.Hashes) error { + hashChecked := false + for typ, hash := range expected { + if h, ok := actual[typ]; ok { + hashChecked = true + if !hmac.Equal(h, hash) { + return ErrWrongHash{typ, hash, h} + } + } + } + if !hashChecked { + return ErrNoCommonHash{expected, actual} + } + return nil +} + +func VersionEqual(actual int64, expected int64) error { + if actual != expected { + return ErrWrongVersion{expected, actual} + } + return nil +} + +func SnapshotFileMetaEqual(actual data.SnapshotFileMeta, expected data.SnapshotFileMeta) error { + // TUF-1.0 no longer considers the length and hashes to be a required + // member of snapshots. However they are considering requiring hashes + // for delegated roles to avoid an attack described in Section 5.6 of + // the Mercury paper: + // https://github.com/theupdateframework/specification/pull/40 + if expected.Length != 0 && actual.Length != expected.Length { + return ErrWrongLength{expected.Length, actual.Length} + } + // 5.6.2 - Check against snapshot role's targets hash + if len(expected.Hashes) != 0 { + if err := hashEqual(actual.Hashes, expected.Hashes); err != nil { + return err + } + } + // 5.6.4 - Check against snapshot role's snapshot version + if err := VersionEqual(actual.Version, expected.Version); err != nil { + return err + } + + return nil +} + +func TargetFileMetaEqual(actual data.TargetFileMeta, expected data.TargetFileMeta) error { + return FileMetaEqual(actual.FileMeta, expected.FileMeta) +} + +func TimestampFileMetaEqual(actual data.TimestampFileMeta, expected data.TimestampFileMeta) error { + // TUF no longer considers the length and hashes to be a required + // member of Timestamp. + if expected.Length != 0 && actual.Length != expected.Length { + return ErrWrongLength{expected.Length, actual.Length} + } + // 5.5.2 - Check against timestamp role's snapshot hash + if len(expected.Hashes) != 0 { + if err := hashEqual(actual.Hashes, expected.Hashes); err != nil { + return err + } + } + // 5.5.4 - Check against timestamp role's snapshot version + if err := VersionEqual(actual.Version, expected.Version); err != nil { + return err + } + + return nil +} + +const defaultHashAlgorithm = "sha512" + +func GenerateFileMeta(r io.Reader, hashAlgorithms ...string) (data.FileMeta, error) { + if len(hashAlgorithms) == 0 { + hashAlgorithms = []string{defaultHashAlgorithm} + } + hashes := make(map[string]hash.Hash, len(hashAlgorithms)) + for _, hashAlgorithm := range hashAlgorithms { + var h hash.Hash + switch hashAlgorithm { + case "sha256": + h = sha256.New() + case "sha512": + h = sha512.New() + default: + return data.FileMeta{}, ErrUnknownHashAlgorithm{hashAlgorithm} + } + hashes[hashAlgorithm] = h + r = io.TeeReader(r, h) + } + n, err := io.Copy(io.Discard, r) + if err != nil { + return data.FileMeta{}, err + } + m := data.FileMeta{Length: n, Hashes: make(data.Hashes, len(hashes))} + for hashAlgorithm, h := range hashes { + m.Hashes[hashAlgorithm] = h.Sum(nil) + } + return m, nil +} + +type versionedMeta struct { + Version int64 `json:"version"` +} + +func generateVersionedFileMeta(r io.Reader, hashAlgorithms ...string) (data.FileMeta, int64, error) { + b, err := io.ReadAll(r) + if err != nil { + return data.FileMeta{}, 0, err + } + + m, err := GenerateFileMeta(bytes.NewReader(b), hashAlgorithms...) + if err != nil { + return data.FileMeta{}, 0, err + } + + s := data.Signed{} + if err := json.Unmarshal(b, &s); err != nil { + return data.FileMeta{}, 0, err + } + + vm := versionedMeta{} + if err := json.Unmarshal(s.Signed, &vm); err != nil { + return data.FileMeta{}, 0, err + } + + return m, vm.Version, nil +} + +func GenerateSnapshotFileMeta(r io.Reader, hashAlgorithms ...string) (data.SnapshotFileMeta, error) { + m, v, err := generateVersionedFileMeta(r, hashAlgorithms...) + if err != nil { + return data.SnapshotFileMeta{}, err + } + return data.SnapshotFileMeta{ + Length: m.Length, + Hashes: m.Hashes, + Version: v, + }, nil +} + +func GenerateTargetFileMeta(r io.Reader, hashAlgorithms ...string) (data.TargetFileMeta, error) { + m, err := GenerateFileMeta(r, hashAlgorithms...) + if err != nil { + return data.TargetFileMeta{}, err + } + return data.TargetFileMeta{ + FileMeta: m, + }, nil +} + +func GenerateTimestampFileMeta(r io.Reader, hashAlgorithms ...string) (data.TimestampFileMeta, error) { + m, v, err := generateVersionedFileMeta(r, hashAlgorithms...) + if err != nil { + return data.TimestampFileMeta{}, err + } + return data.TimestampFileMeta{ + Length: m.Length, + Hashes: m.Hashes, + Version: v, + }, nil +} + +func NormalizeTarget(p string) string { + // FIXME(TUF-0.9) TUF-1.0 is considering banning paths that begin with + // a leading path separator, to avoid surprising behavior when joining + // target and delgated paths. python-tuf raises an exception if any + // path starts with '/', but since we need to be cross compatible with + // TUF-0.9 we still need to support leading slashes. For now, we will + // just strip them out, but eventually we should also consider turning + // them into an error. + return strings.TrimPrefix(path.Join("/", p), "/") +} + +func VersionedPath(p string, version int64) string { + return path.Join(path.Dir(p), strconv.FormatInt(version, 10)+"."+path.Base(p)) +} + +func HashedPaths(p string, hashes data.Hashes) []string { + paths := make([]string, 0, len(hashes)) + for _, hash := range hashes { + hashedPath := path.Join(path.Dir(p), hash.String()+"."+path.Base(p)) + paths = append(paths, hashedPath) + } + return paths +} + +func AtomicallyWriteFile(filename string, data []byte, perm os.FileMode) error { + dir, name := filepath.Split(filename) + f, err := os.CreateTemp(dir, name) + if err != nil { + return err + } + + _, err = f.Write(data) + if err != nil { + f.Close() + os.Remove(f.Name()) + return err + } + + if err = f.Chmod(perm); err != nil { + f.Close() + os.Remove(f.Name()) + return err + } + + if err := f.Close(); err != nil { + os.Remove(f.Name()) + return err + } + + if err := os.Rename(f.Name(), filename); err != nil { + os.Remove(f.Name()) + return err + } + + return nil +} diff --git a/vendor/github.com/DataDog/go-tuf/verify/db.go b/vendor/github.com/DataDog/go-tuf/verify/db.go new file mode 100644 index 000000000..1961c98fb --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/verify/db.go @@ -0,0 +1,104 @@ +package verify + +import ( + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/internal/roles" + "github.com/DataDog/go-tuf/pkg/keys" +) + +type Role struct { + KeyIDs map[string]struct{} + Threshold int +} + +func (r *Role) ValidKey(id string) bool { + _, ok := r.KeyIDs[id] + return ok +} + +type DB struct { + roles map[string]*Role + verifiers map[string]keys.Verifier +} + +func NewDB() *DB { + return &DB{ + roles: make(map[string]*Role), + verifiers: make(map[string]keys.Verifier), + } +} + +// NewDBFromDelegations returns a DB that verifies delegations +// of a given Targets. +func NewDBFromDelegations(d *data.Delegations) (*DB, error) { + db := &DB{ + roles: make(map[string]*Role, len(d.Roles)), + verifiers: make(map[string]keys.Verifier, len(d.Keys)), + } + for _, r := range d.Roles { + if _, ok := roles.TopLevelRoles[r.Name]; ok { + return nil, ErrInvalidDelegatedRole + } + role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs} + if err := db.AddRole(r.Name, role); err != nil { + return nil, err + } + } + for id, k := range d.Keys { + if err := db.AddKey(id, k); err != nil { + return nil, err + } + } + return db, nil +} + +func (db *DB) AddKey(id string, k *data.PublicKey) error { + verifier, err := keys.GetVerifier(k) + if err != nil { + return err // ErrInvalidKey + } + + // TUF is considering in TAP-12 removing the + // requirement that the keyid hash algorithm be derived + // from the public key. So to be forwards compatible, + // we allow any key ID, rather than checking k.ContainsID(id) + // + // AddKey should be idempotent, so we allow re-adding the same PublicKey. + // + // TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md + if oldVerifier, exists := db.verifiers[id]; exists && oldVerifier.Public() != verifier.Public() { + return ErrRepeatID{id} + } + + db.verifiers[id] = verifier + return nil +} + +func (db *DB) AddRole(name string, r *data.Role) error { + if r.Threshold < 1 { + return ErrInvalidThreshold + } + + role := &Role{ + KeyIDs: make(map[string]struct{}), + Threshold: r.Threshold, + } + for _, id := range r.KeyIDs { + role.KeyIDs[id] = struct{}{} + } + + db.roles[name] = role + return nil +} + +func (db *DB) GetVerifier(id string) (keys.Verifier, error) { + k, ok := db.verifiers[id] + if !ok { + return nil, ErrMissingKey + } + return k, nil +} + +func (db *DB) GetRole(name string) *Role { + return db.roles[name] +} diff --git a/vendor/github.com/DataDog/go-tuf/verify/errors.go b/vendor/github.com/DataDog/go-tuf/verify/errors.go new file mode 100644 index 000000000..f71d4bda9 --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/verify/errors.go @@ -0,0 +1,73 @@ +package verify + +import ( + "errors" + "fmt" + "time" +) + +var ( + ErrMissingKey = errors.New("tuf: missing key") + ErrNoSignatures = errors.New("tuf: data has no signatures") + ErrInvalid = errors.New("tuf: signature verification failed") + ErrWrongMethod = errors.New("tuf: invalid signature type") + ErrWrongMetaType = errors.New("tuf: meta file has wrong type") + ErrExists = errors.New("tuf: key already in db") + ErrInvalidKey = errors.New("tuf: invalid key") + ErrInvalidRole = errors.New("tuf: invalid role") + ErrInvalidDelegatedRole = errors.New("tuf: invalid delegated role") + ErrInvalidKeyID = errors.New("tuf: invalid key id") + ErrInvalidThreshold = errors.New("tuf: invalid role threshold") + ErrMissingTargetFile = errors.New("tuf: missing previously listed targets metadata file") +) + +type ErrRepeatID struct { + KeyID string +} + +func (e ErrRepeatID) Error() string { + return fmt.Sprintf("tuf: duplicate key id (%s)", e.KeyID) +} + +type ErrUnknownRole struct { + Role string +} + +func (e ErrUnknownRole) Error() string { + return fmt.Sprintf("tuf: unknown role %q", e.Role) +} + +type ErrExpired struct { + Expired time.Time +} + +func (e ErrExpired) Error() string { + return fmt.Sprintf("expired at %s", e.Expired) +} + +type ErrLowVersion struct { + Actual int64 + Current int64 +} + +func (e ErrLowVersion) Error() string { + return fmt.Sprintf("version %d is lower than current version %d", e.Actual, e.Current) +} + +type ErrWrongVersion struct { + Given int64 + Expected int64 +} + +func (e ErrWrongVersion) Error() string { + return fmt.Sprintf("version %d does not match the expected version %d", e.Given, e.Expected) +} + +type ErrRoleThreshold struct { + Expected int + Actual int +} + +func (e ErrRoleThreshold) Error() string { + return "tuf: valid signatures did not meet threshold" +} diff --git a/vendor/github.com/DataDog/go-tuf/verify/verify.go b/vendor/github.com/DataDog/go-tuf/verify/verify.go new file mode 100644 index 000000000..b0cf333ca --- /dev/null +++ b/vendor/github.com/DataDog/go-tuf/verify/verify.go @@ -0,0 +1,187 @@ +package verify + +import ( + "encoding/json" + "strings" + "time" + + "github.com/DataDog/go-tuf/data" + "github.com/DataDog/go-tuf/internal/roles" + "github.com/DataDog/go-tuf/pkg/keys" + "github.com/secure-systems-lab/go-securesystemslib/cjson" +) + +type signedMeta struct { + Type string `json:"_type"` + Expires time.Time `json:"expires"` + Version int64 `json:"version"` +} + +// VerifySignature takes a signed JSON message, a signature, and a +// verifier and verifies the given signature on the JSON message +// using the verifier. It returns an error if verification fails. +func VerifySignature(signed json.RawMessage, sig data.HexBytes, + verifier keys.Verifier) error { + var decoded map[string]interface{} + if err := json.Unmarshal(signed, &decoded); err != nil { + return err + } + msg, err := cjson.EncodeCanonical(decoded) + if err != nil { + return err + } + return verifier.Verify(msg, sig) +} + +func (db *DB) VerifyIgnoreExpiredCheck(s *data.Signed, role string, minVersion int64) error { + if err := db.VerifySignatures(s, role); err != nil { + return err + } + + sm := &signedMeta{} + if err := json.Unmarshal(s.Signed, sm); err != nil { + return err + } + + if roles.IsTopLevelRole(role) { + // Top-level roles can only sign metadata of the same type (e.g. snapshot + // metadata must be signed by the snapshot role). + if !strings.EqualFold(sm.Type, role) { + return ErrWrongMetaType + } + } else { + // Delegated (non-top-level) roles may only sign targets metadata. + if strings.ToLower(sm.Type) != "targets" { + return ErrWrongMetaType + } + } + + if sm.Version < minVersion { + return ErrLowVersion{sm.Version, minVersion} + } + + return nil +} + +func (db *DB) Verify(s *data.Signed, role string, minVersion int64) error { + // Verify signatures and versions + err := db.VerifyIgnoreExpiredCheck(s, role, minVersion) + + if err != nil { + return err + } + + sm := &signedMeta{} + if err := json.Unmarshal(s.Signed, sm); err != nil { + return err + } + // Verify expiration + if IsExpired(sm.Expires) { + return ErrExpired{sm.Expires} + } + + return nil +} + +var IsExpired = func(t time.Time) bool { + return time.Until(t) <= 0 +} + +func (db *DB) VerifySignatures(s *data.Signed, role string) error { + if len(s.Signatures) == 0 { + return ErrNoSignatures + } + + roleData := db.GetRole(role) + if roleData == nil { + return ErrUnknownRole{role} + } + + // Verify that a threshold of keys signed the data. Since keys can have + // multiple key ids, we need to protect against multiple attached + // signatures that just differ on the key id. + verifiedKeyIDs := make(map[string]struct{}) + numVerifiedKeys := 0 + for _, sig := range s.Signatures { + if !roleData.ValidKey(sig.KeyID) { + continue + } + verifier, err := db.GetVerifier(sig.KeyID) + if err != nil { + continue + } + + if err := VerifySignature(s.Signed, sig.Signature, verifier); err != nil { + // If a signature fails verification, don't count it towards the + // threshold but also return early and error out immediately. + // Note: Because of this, it is impossible to distinguish between + // an error of an invalid signature and a threshold not achieved. + // Invalid signatures lead to not achieving the threshold. + continue + } + + // Only consider this key valid if we haven't seen any of it's + // key ids before. + // Careful: we must not rely on the key IDs _declared in the file_, + // instead we get to decide what key IDs this key correspond to. + // XXX dangerous; better stop supporting multiple key IDs altogether. + keyIDs := verifier.MarshalPublicKey().IDs() + wasKeySeen := false + for _, keyID := range keyIDs { + if _, present := verifiedKeyIDs[keyID]; present { + wasKeySeen = true + } + } + if !wasKeySeen { + for _, id := range keyIDs { + verifiedKeyIDs[id] = struct{}{} + } + + numVerifiedKeys++ + } + } + if numVerifiedKeys < roleData.Threshold { + return ErrRoleThreshold{roleData.Threshold, numVerifiedKeys} + } + + return nil +} + +func (db *DB) Unmarshal(b []byte, v interface{}, role string, minVersion int64) error { + s := &data.Signed{} + if err := json.Unmarshal(b, s); err != nil { + return err + } + if err := db.Verify(s, role, minVersion); err != nil { + return err + } + return json.Unmarshal(s.Signed, v) +} + +// UnmarshalExpired is exactly like Unmarshal except ignores expired timestamp error. +func (db *DB) UnmarshalIgnoreExpired(b []byte, v interface{}, role string, minVersion int64) error { + s := &data.Signed{} + if err := json.Unmarshal(b, s); err != nil { + return err + } + // Note: If verification fails, then we wont attempt to unmarshal + // unless when verification error is errExpired. + verifyErr := db.Verify(s, role, minVersion) + if verifyErr != nil { + if _, ok := verifyErr.(ErrExpired); !ok { + return verifyErr + } + } + return json.Unmarshal(s.Signed, v) +} + +func (db *DB) UnmarshalTrusted(b []byte, v interface{}, role string) error { + s := &data.Signed{} + if err := json.Unmarshal(b, s); err != nil { + return err + } + if err := db.VerifySignatures(s, role); err != nil { + return err + } + return json.Unmarshal(s.Signed, v) +} diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/ddsketch.go b/vendor/github.com/DataDog/sketches-go/ddsketch/ddsketch.go index 3f0f943c2..187c10f8a 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/ddsketch.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/ddsketch.go @@ -18,8 +18,12 @@ import ( ) var ( - errEmptySketch error = errors.New("no such element exists") - errUnknownFlag error = errors.New("unknown encoding flag") + ErrUntrackableNaN = errors.New("input value is NaN and cannot be tracked by the sketch") + ErrUntrackableTooLow = errors.New("input value is too low and cannot be tracked by the sketch") + ErrUntrackableTooHigh = errors.New("input value is too high and cannot be tracked by the sketch") + ErrNegativeCount = errors.New("count cannot be negative") + errEmptySketch = errors.New("no such element exists") + errUnknownFlag = errors.New("unknown encoding flag") ) // Unexported to prevent usage and avoid the cost of dynamic dispatch @@ -27,7 +31,10 @@ type quantileSketch interface { RelativeAccuracy() float64 IsEmpty() bool GetCount() float64 + GetZeroCount() float64 GetSum() float64 + GetPositiveValueStore() store.Store + GetNegativeValueStore() store.Store GetMinValue() (float64, error) GetMaxValue() (float64, error) GetValueAtQuantile(quantile float64) (float64, error) @@ -49,11 +56,9 @@ var _ quantileSketch = (*DDSketchWithExactSummaryStatistics)(nil) type DDSketch struct { mapping.IndexMapping - positiveValueStore store.Store - negativeValueStore store.Store - zeroCount float64 - minIndexableAbsoluteValue float64 - maxIndexableValue float64 + positiveValueStore store.Store + negativeValueStore store.Store + zeroCount float64 } func NewDDSketchFromStoreProvider(indexMapping mapping.IndexMapping, storeProvider store.Provider) *DDSketch { @@ -62,11 +67,9 @@ func NewDDSketchFromStoreProvider(indexMapping mapping.IndexMapping, storeProvid func NewDDSketch(indexMapping mapping.IndexMapping, positiveValueStore store.Store, negativeValueStore store.Store) *DDSketch { return &DDSketch{ - IndexMapping: indexMapping, - positiveValueStore: positiveValueStore, - negativeValueStore: negativeValueStore, - minIndexableAbsoluteValue: indexMapping.MinIndexableValue(), - maxIndexableValue: indexMapping.MaxIndexableValue(), + IndexMapping: indexMapping, + positiveValueStore: positiveValueStore, + negativeValueStore: negativeValueStore, } } @@ -119,17 +122,22 @@ func (s *DDSketch) Add(value float64) error { // Adds a value to the sketch with a float64 count. func (s *DDSketch) AddWithCount(value, count float64) error { - if value < -s.maxIndexableValue || value > s.maxIndexableValue { - return errors.New("The input value is outside the range that is tracked by the sketch.") - } if count < 0 { - return errors.New("The count cannot be negative.") + return ErrNegativeCount } - if value > s.minIndexableAbsoluteValue { + if value > s.MinIndexableValue() { + if value > s.MaxIndexableValue() { + return ErrUntrackableTooHigh + } s.positiveValueStore.AddWithCount(s.Index(value), count) - } else if value < -s.minIndexableAbsoluteValue { + } else if value < -s.MinIndexableValue() { + if value < -s.MaxIndexableValue() { + return ErrUntrackableTooLow + } s.negativeValueStore.AddWithCount(s.Index(-value), count) + } else if math.IsNaN(value) { + return ErrUntrackableNaN } else { s.zeroCount += count } @@ -139,12 +147,10 @@ func (s *DDSketch) AddWithCount(value, count float64) error { // Return a (deep) copy of this sketch. func (s *DDSketch) Copy() *DDSketch { return &DDSketch{ - IndexMapping: s.IndexMapping, - positiveValueStore: s.positiveValueStore.Copy(), - negativeValueStore: s.negativeValueStore.Copy(), - zeroCount: s.zeroCount, - minIndexableAbsoluteValue: s.minIndexableAbsoluteValue, - maxIndexableValue: s.maxIndexableValue, + IndexMapping: s.IndexMapping, + positiveValueStore: s.positiveValueStore.Copy(), + negativeValueStore: s.negativeValueStore.Copy(), + zeroCount: s.zeroCount, } } @@ -197,6 +203,13 @@ func (s *DDSketch) GetCount() float64 { return s.zeroCount + s.positiveValueStore.TotalCount() + s.negativeValueStore.TotalCount() } +// GetZeroCount returns the number of zero values that have been added to this sketch. +// Note: values that are very small (lower than MinIndexableValue if positive, or higher than -MinIndexableValue if negative) +// are also mapped to the zero bucket. +func (s *DDSketch) GetZeroCount() float64 { + return s.zeroCount +} + // Return true iff no value has been added to this sketch. func (s *DDSketch) IsEmpty() bool { return s.zeroCount == 0 && s.positiveValueStore.IsEmpty() && s.negativeValueStore.IsEmpty() @@ -247,6 +260,18 @@ func (s *DDSketch) GetSum() (sum float64) { return sum } +// GetPositiveValueStore returns the store.Store object that contains the positive +// values of the sketch. +func (s *DDSketch) GetPositiveValueStore() (store.Store) { + return s.positiveValueStore +} + +// GetNegativeValueStore returns the store.Store object that contains the negative +// values of the sketch. +func (s *DDSketch) GetNegativeValueStore() (store.Store) { + return s.negativeValueStore +} + // ForEach applies f on the bins of the sketches until f returns true. // There is no guarantee on the bin iteration order. func (s *DDSketch) ForEach(f func(value, count float64) (stop bool)) { @@ -303,12 +328,10 @@ func FromProtoWithStoreProvider(pb *sketchpb.DDSketch, storeProvider store.Provi return nil, err } return &DDSketch{ - IndexMapping: m, - positiveValueStore: positiveValueStore, - negativeValueStore: negativeValueStore, - zeroCount: pb.ZeroCount, - minIndexableAbsoluteValue: m.MinIndexableValue(), - maxIndexableValue: m.MaxIndexableValue(), + IndexMapping: m, + positiveValueStore: positiveValueStore, + negativeValueStore: negativeValueStore, + zeroCount: pb.ZeroCount, }, nil } @@ -420,8 +443,6 @@ func (s *DDSketch) decodeAndMergeWith(bb []byte, fallbackDecode func(b *[]byte, if s.IndexMapping == nil { return errors.New("missing index mapping") } - s.minIndexableAbsoluteValue = s.IndexMapping.MinIndexableValue() - s.maxIndexableValue = s.IndexMapping.MaxIndexableValue() return nil } @@ -484,7 +505,7 @@ func (s *DDSketch) Reweight(w float64) error { // statistics. Because of the need to track them exactly, adding and merging // operations are slightly more exepensive than those of DDSketch. type DDSketchWithExactSummaryStatistics struct { - sketch *DDSketch + *DDSketch summaryStatistics *stat.SummaryStatistics } @@ -494,20 +515,27 @@ func NewDefaultDDSketchWithExactSummaryStatistics(relativeAccuracy float64) (*DD return nil, err } return &DDSketchWithExactSummaryStatistics{ - sketch: sketch, + DDSketch: sketch, summaryStatistics: stat.NewSummaryStatistics(), }, nil } func NewDDSketchWithExactSummaryStatistics(mapping mapping.IndexMapping, storeProvider store.Provider) *DDSketchWithExactSummaryStatistics { return &DDSketchWithExactSummaryStatistics{ - sketch: NewDDSketchFromStoreProvider(mapping, storeProvider), + DDSketch: NewDDSketchFromStoreProvider(mapping, storeProvider), summaryStatistics: stat.NewSummaryStatistics(), } } -func (s *DDSketchWithExactSummaryStatistics) RelativeAccuracy() float64 { - return s.sketch.RelativeAccuracy() +// NewDDSketchWithExactSummaryStatisticsFromData constructs DDSketchWithExactSummaryStatistics from the provided sketch and exact summary statistics. +func NewDDSketchWithExactSummaryStatisticsFromData(sketch *DDSketch, summaryStatistics *stat.SummaryStatistics) (*DDSketchWithExactSummaryStatistics, error) { + if sketch.IsEmpty() != (summaryStatistics.Count() == 0) { + return nil, errors.New("sketch and summary statistics do not match") + } + return &DDSketchWithExactSummaryStatistics{ + DDSketch: sketch, + summaryStatistics: summaryStatistics, + }, nil } func (s *DDSketchWithExactSummaryStatistics) IsEmpty() bool { @@ -518,26 +546,45 @@ func (s *DDSketchWithExactSummaryStatistics) GetCount() float64 { return s.summaryStatistics.Count() } +// GetZeroCount returns the number of zero values that have been added to this sketch. +// Note: values that are very small (lower than MinIndexableValue if positive, or higher than -MinIndexableValue if negative) +// are also mapped to the zero bucket. +func (s *DDSketchWithExactSummaryStatistics) GetZeroCount() float64 { + return s.DDSketch.zeroCount +} + func (s *DDSketchWithExactSummaryStatistics) GetSum() float64 { return s.summaryStatistics.Sum() } +// GetPositiveValueStore returns the store.Store object that contains the positive +// values of the sketch. +func (s *DDSketchWithExactSummaryStatistics) GetPositiveValueStore() (store.Store) { + return s.DDSketch.positiveValueStore +} + +// GetNegativeValueStore returns the store.Store object that contains the negative +// values of the sketch. +func (s *DDSketchWithExactSummaryStatistics) GetNegativeValueStore() (store.Store) { + return s.DDSketch.negativeValueStore +} + func (s *DDSketchWithExactSummaryStatistics) GetMinValue() (float64, error) { - if s.sketch.IsEmpty() { + if s.DDSketch.IsEmpty() { return math.NaN(), errEmptySketch } return s.summaryStatistics.Min(), nil } func (s *DDSketchWithExactSummaryStatistics) GetMaxValue() (float64, error) { - if s.sketch.IsEmpty() { + if s.DDSketch.IsEmpty() { return math.NaN(), errEmptySketch } return s.summaryStatistics.Max(), nil } func (s *DDSketchWithExactSummaryStatistics) GetValueAtQuantile(quantile float64) (float64, error) { - value, err := s.sketch.GetValueAtQuantile(quantile) + value, err := s.DDSketch.GetValueAtQuantile(quantile) min := s.summaryStatistics.Min() if value < min { return min, err @@ -550,7 +597,7 @@ func (s *DDSketchWithExactSummaryStatistics) GetValueAtQuantile(quantile float64 } func (s *DDSketchWithExactSummaryStatistics) GetValuesAtQuantiles(quantiles []float64) ([]float64, error) { - values, err := s.sketch.GetValuesAtQuantiles(quantiles) + values, err := s.DDSketch.GetValuesAtQuantiles(quantiles) min := s.summaryStatistics.Min() max := s.summaryStatistics.Max() for i := range values { @@ -564,16 +611,16 @@ func (s *DDSketchWithExactSummaryStatistics) GetValuesAtQuantiles(quantiles []fl } func (s *DDSketchWithExactSummaryStatistics) ForEach(f func(value, count float64) (stop bool)) { - s.sketch.ForEach(f) + s.DDSketch.ForEach(f) } func (s *DDSketchWithExactSummaryStatistics) Clear() { - s.sketch.Clear() + s.DDSketch.Clear() s.summaryStatistics.Clear() } func (s *DDSketchWithExactSummaryStatistics) Add(value float64) error { - err := s.sketch.Add(value) + err := s.DDSketch.Add(value) if err != nil { return err } @@ -585,7 +632,7 @@ func (s *DDSketchWithExactSummaryStatistics) AddWithCount(value, count float64) if count == 0 { return nil } - err := s.sketch.AddWithCount(value, count) + err := s.DDSketch.AddWithCount(value, count) if err != nil { return err } @@ -594,7 +641,7 @@ func (s *DDSketchWithExactSummaryStatistics) AddWithCount(value, count float64) } func (s *DDSketchWithExactSummaryStatistics) MergeWith(o *DDSketchWithExactSummaryStatistics) error { - err := s.sketch.MergeWith(o.sketch) + err := s.DDSketch.MergeWith(o.DDSketch) if err != nil { return err } @@ -604,13 +651,13 @@ func (s *DDSketchWithExactSummaryStatistics) MergeWith(o *DDSketchWithExactSumma func (s *DDSketchWithExactSummaryStatistics) Copy() *DDSketchWithExactSummaryStatistics { return &DDSketchWithExactSummaryStatistics{ - sketch: s.sketch.Copy(), + DDSketch: s.DDSketch.Copy(), summaryStatistics: s.summaryStatistics.Copy(), } } func (s *DDSketchWithExactSummaryStatistics) Reweight(factor float64) error { - err := s.sketch.Reweight(factor) + err := s.DDSketch.Reweight(factor) if err != nil { return err } @@ -622,7 +669,7 @@ func (s *DDSketchWithExactSummaryStatistics) ChangeMapping(newMapping mapping.In summaryStatisticsCopy := s.summaryStatistics.Copy() summaryStatisticsCopy.Rescale(scaleFactor) return &DDSketchWithExactSummaryStatistics{ - sketch: s.sketch.ChangeMapping(newMapping, storeProvider(), storeProvider(), scaleFactor), + DDSketch: s.DDSketch.ChangeMapping(newMapping, storeProvider(), storeProvider(), scaleFactor), summaryStatistics: summaryStatisticsCopy, } } @@ -644,7 +691,7 @@ func (s *DDSketchWithExactSummaryStatistics) Encode(b *[]byte, omitIndexMapping enc.EncodeFlag(b, enc.FlagMax) enc.EncodeFloat64LE(b, s.summaryStatistics.Max()) } - s.sketch.Encode(b, omitIndexMapping) + s.DDSketch.Encode(b, omitIndexMapping) } // DecodeDDSketchWithExactSummaryStatistics deserializes a sketch. @@ -664,7 +711,7 @@ func (s *DDSketchWithExactSummaryStatistics) Encode(b *[]byte, omitIndexMapping // it is empty), because it does not track exact summary statistics func DecodeDDSketchWithExactSummaryStatistics(b []byte, storeProvider store.Provider, indexMapping mapping.IndexMapping) (*DDSketchWithExactSummaryStatistics, error) { s := &DDSketchWithExactSummaryStatistics{ - sketch: &DDSketch{ + DDSketch: &DDSketch{ IndexMapping: indexMapping, positiveValueStore: storeProvider(), negativeValueStore: storeProvider(), @@ -677,7 +724,7 @@ func DecodeDDSketchWithExactSummaryStatistics(b []byte, storeProvider store.Prov } func (s *DDSketchWithExactSummaryStatistics) DecodeAndMergeWith(bb []byte) error { - err := s.sketch.decodeAndMergeWith(bb, func(b *[]byte, flag enc.Flag) error { + err := s.DDSketch.decodeAndMergeWith(bb, func(b *[]byte, flag enc.Flag) error { switch flag { case enc.FlagCount: count, err := enc.DecodeVarfloat64(b) @@ -709,7 +756,7 @@ func (s *DDSketchWithExactSummaryStatistics) DecodeAndMergeWith(bb []byte) error } // It is assumed that if the count is encoded, other exact summary // statistics are encoded as well, which is the case if Encode is used. - if s.summaryStatistics.Count() == 0 && !s.sketch.IsEmpty() { + if s.summaryStatistics.Count() == 0 && !s.DDSketch.IsEmpty() { return errors.New("missing exact summary statistics") } return nil diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/cubically_interpolated_mapping.go b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/cubically_interpolated_mapping.go index dbae9bb16..933cdab66 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/cubically_interpolated_mapping.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/cubically_interpolated_mapping.go @@ -21,36 +21,48 @@ const ( C = 10.0 / 7.0 ) -// A fast IndexMapping that approximates the memory-optimal LogarithmicMapping by extracting the floor value -// of the logarithm to the base 2 from the binary representations of floating-point values and cubically -// interpolating the logarithm in-between. -// More detailed documentation of this method can be found in: -// sketches-java +// CubicallyInterpolatedMapping is a fast IndexMapping that approximates the +// memory-optimal LogarithmicMapping by extracting the floor value of the +// logarithm to the base 2 from the binary representations of floating-point +// values and cubically interpolating the logarithm in-between. More detailed +// documentation of this method can be found in: sketches-java type CubicallyInterpolatedMapping struct { - relativeAccuracy float64 - multiplier float64 - normalizedIndexOffset float64 + gamma float64 // base + indexOffset float64 + multiplier float64 // precomputed for performance + minIndexableValue float64 + maxIndexableValue float64 } func NewCubicallyInterpolatedMapping(relativeAccuracy float64) (*CubicallyInterpolatedMapping, error) { if relativeAccuracy <= 0 || relativeAccuracy >= 1 { return nil, errors.New("The relative accuracy must be between 0 and 1.") } - return &CubicallyInterpolatedMapping{ - relativeAccuracy: relativeAccuracy, - multiplier: 7.0 / (10 * math.Log1p(2*relativeAccuracy/(1-relativeAccuracy))), - }, nil + gamma := math.Pow((1+relativeAccuracy)/(1-relativeAccuracy), 10*math.Ln2/7) // > 1 + m, _ := NewCubicallyInterpolatedMappingWithGamma(gamma, 0) + return m, nil } func NewCubicallyInterpolatedMappingWithGamma(gamma, indexOffset float64) (*CubicallyInterpolatedMapping, error) { if gamma <= 1 { return nil, errors.New("Gamma must be greater than 1.") } + multiplier := 1 / math.Log2(gamma) + adjustedGamma := math.Pow(gamma, 7/(10*math.Ln2)) m := CubicallyInterpolatedMapping{ - relativeAccuracy: 1 - 2/(1+math.Exp(7.0/10*math.Log2(gamma))), - multiplier: 1 / math.Log2(gamma), + gamma: gamma, + indexOffset: indexOffset, + multiplier: multiplier, + minIndexableValue: math.Max( + math.Exp2((math.MinInt32-indexOffset)/multiplier+1), // so that index >= MinInt32 + minNormalFloat64*adjustedGamma, + ), + maxIndexableValue: math.Min( + math.Exp2((math.MaxInt32-indexOffset)/multiplier-1), // so that index <= MaxInt32 + math.Exp(expOverflow)/(2*adjustedGamma)*(adjustedGamma+1), // so that math.Exp does not overflow + ), } - m.normalizedIndexOffset = indexOffset - m.approximateLog(1)*m.multiplier return &m, nil } @@ -60,11 +72,11 @@ func (m *CubicallyInterpolatedMapping) Equals(other IndexMapping) bool { return false } tol := 1e-12 - return (withinTolerance(m.multiplier, o.multiplier, tol) && withinTolerance(m.normalizedIndexOffset, o.normalizedIndexOffset, tol)) + return withinTolerance(m.gamma, o.gamma, tol) && withinTolerance(m.indexOffset, o.indexOffset, tol) } func (m *CubicallyInterpolatedMapping) Index(value float64) int { - index := m.approximateLog(value)*m.multiplier + m.normalizedIndexOffset + index := m.approximateLog(value)*m.multiplier + m.indexOffset if index >= 0 { return int(index) } else { @@ -73,14 +85,14 @@ func (m *CubicallyInterpolatedMapping) Index(value float64) int { } func (m *CubicallyInterpolatedMapping) Value(index int) float64 { - return m.LowerBound(index) * (1 + m.relativeAccuracy) + return m.LowerBound(index) * (1 + m.RelativeAccuracy()) } func (m *CubicallyInterpolatedMapping) LowerBound(index int) float64 { - return m.approximateInverseLog((float64(index) - m.normalizedIndexOffset) / m.multiplier) + return m.approximateInverseLog((float64(index) - m.indexOffset) / m.multiplier) } -// Return an approximation of log(1) + Math.log(x) / Math.log(base(2)). +// Return an approximation of Math.log(x) / Math.log(base(2)). func (m *CubicallyInterpolatedMapping) approximateLog(x float64) float64 { bits := math.Float64bits(x) e := getExponent(bits) @@ -100,44 +112,34 @@ func (m *CubicallyInterpolatedMapping) approximateInverseLog(x float64) float64 } func (m *CubicallyInterpolatedMapping) MinIndexableValue() float64 { - return math.Max( - math.Exp2((math.MinInt32-m.normalizedIndexOffset)/m.multiplier-m.approximateLog(1)+1), // so that index >= MinInt32:w - minNormalFloat64*(1+m.relativeAccuracy)/(1-m.relativeAccuracy), - ) + return m.minIndexableValue } func (m *CubicallyInterpolatedMapping) MaxIndexableValue() float64 { - return math.Min( - math.Exp2((math.MaxInt32-m.normalizedIndexOffset)/m.multiplier-m.approximateLog(float64(1))-1), // so that index <= MaxInt32 - math.Exp(expOverflow)/(1+m.relativeAccuracy), // so that math.Exp does not overflow - ) + return m.maxIndexableValue } func (m *CubicallyInterpolatedMapping) RelativeAccuracy() float64 { - return m.relativeAccuracy -} - -func (m *CubicallyInterpolatedMapping) gamma() float64 { - return math.Exp2(1 / m.multiplier) + return 1 - 2/(1+math.Exp(7.0/10*math.Log2(m.gamma))) } func (m *CubicallyInterpolatedMapping) ToProto() *sketchpb.IndexMapping { return &sketchpb.IndexMapping{ - Gamma: m.gamma(), - IndexOffset: m.normalizedIndexOffset + m.approximateLog(1)*m.multiplier, + Gamma: m.gamma, + IndexOffset: m.indexOffset, Interpolation: sketchpb.IndexMapping_CUBIC, } } func (m *CubicallyInterpolatedMapping) Encode(b *[]byte) { enc.EncodeFlag(b, enc.FlagIndexMappingBaseCubic) - enc.EncodeFloat64LE(b, m.gamma()) - enc.EncodeFloat64LE(b, m.normalizedIndexOffset) + enc.EncodeFloat64LE(b, m.gamma) + enc.EncodeFloat64LE(b, m.indexOffset) } func (m *CubicallyInterpolatedMapping) string() string { var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("relativeAccuracy: %v, multiplier: %v, normalizedIndexOffset: %v\n", m.relativeAccuracy, m.multiplier, m.normalizedIndexOffset)) + buffer.WriteString(fmt.Sprintf("gamma: %v, indexOffset: %v\n", m.gamma, m.indexOffset)) return buffer.String() } diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/index_mapping.go b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/index_mapping.go index d8cea7779..f90108eb0 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/index_mapping.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/index_mapping.go @@ -24,7 +24,9 @@ type IndexMapping interface { Value(index int) float64 LowerBound(index int) float64 RelativeAccuracy() float64 + // MinIndexableValue returns the minimum positive value that can be mapped to an index. MinIndexableValue() float64 + // MaxIndexableValue returns the maximum positive value that can be mapped to an index. MaxIndexableValue() float64 ToProto() *sketchpb.IndexMapping // Encode encodes a mapping and appends its content to the provided []byte. diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/linearly_interpolated_mapping.go b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/linearly_interpolated_mapping.go index d17bb4661..d9b0b7408 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/linearly_interpolated_mapping.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/linearly_interpolated_mapping.go @@ -15,34 +15,47 @@ import ( "github.com/DataDog/sketches-go/ddsketch/pb/sketchpb" ) -// A fast IndexMapping that approximates the memory-optimal LogarithmicMapping by extracting the floor value -// of the logarithm to the base 2 from the binary representations of floating-point values and linearly -// interpolating the logarithm in-between. +// LinearlyInterpolatedMapping is a fast IndexMapping that approximates the +// memory-optimal LogarithmicMapping by extracting the floor value of the +// logarithm to the base 2 from the binary representations of floating-point +// values and linearly interpolating the logarithm in-between. type LinearlyInterpolatedMapping struct { - relativeAccuracy float64 - multiplier float64 - normalizedIndexOffset float64 + gamma float64 // base + indexOffset float64 + multiplier float64 // precomputed for performance + minIndexableValue float64 + maxIndexableValue float64 } func NewLinearlyInterpolatedMapping(relativeAccuracy float64) (*LinearlyInterpolatedMapping, error) { if relativeAccuracy <= 0 || relativeAccuracy >= 1 { return nil, errors.New("The relative accuracy must be between 0 and 1.") } - return &LinearlyInterpolatedMapping{ - relativeAccuracy: relativeAccuracy, - multiplier: 1.0 / math.Log1p(2*relativeAccuracy/(1-relativeAccuracy)), - }, nil + gamma := math.Pow((1+relativeAccuracy)/(1-relativeAccuracy), math.Ln2) // > 1 + indexOffset := 1 / math.Log2(gamma) // for backward compatibility + m, _ := NewLinearlyInterpolatedMappingWithGamma(gamma, indexOffset) + return m, nil } func NewLinearlyInterpolatedMappingWithGamma(gamma, indexOffset float64) (*LinearlyInterpolatedMapping, error) { if gamma <= 1 { return nil, errors.New("Gamma must be greater than 1.") } + multiplier := 1 / math.Log2(gamma) + adjustedGamma := math.Pow(gamma, 1/math.Ln2) m := LinearlyInterpolatedMapping{ - relativeAccuracy: 1 - 2/(1+math.Exp(math.Log2(gamma))), - multiplier: 1 / math.Log2(gamma), + gamma: gamma, + indexOffset: indexOffset, + multiplier: multiplier, + minIndexableValue: math.Max( + math.Exp2((math.MinInt32-indexOffset)/multiplier+1), // so that index >= MinInt32 + minNormalFloat64*adjustedGamma, + ), + maxIndexableValue: math.Min( + math.Exp2((math.MaxInt32-indexOffset)/multiplier-1), // so that index <= MaxInt32 + math.Exp(expOverflow)/(2*adjustedGamma)*(adjustedGamma+1), // so that math.Exp does not overflow + ), } - m.normalizedIndexOffset = indexOffset - m.approximateLog(1)*m.multiplier return &m, nil } @@ -52,11 +65,11 @@ func (m *LinearlyInterpolatedMapping) Equals(other IndexMapping) bool { return false } tol := 1e-12 - return (withinTolerance(m.multiplier, o.multiplier, tol) && withinTolerance(m.normalizedIndexOffset, o.normalizedIndexOffset, tol)) + return withinTolerance(m.gamma, o.gamma, tol) && withinTolerance(m.indexOffset, o.indexOffset, tol) } func (m *LinearlyInterpolatedMapping) Index(value float64) int { - index := m.approximateLog(value)*m.multiplier + m.normalizedIndexOffset + index := m.approximateLog(value)*m.multiplier + m.indexOffset if index >= 0 { return int(index) } else { @@ -65,66 +78,56 @@ func (m *LinearlyInterpolatedMapping) Index(value float64) int { } func (m *LinearlyInterpolatedMapping) Value(index int) float64 { - return m.LowerBound(index) * (1 + m.relativeAccuracy) + return m.LowerBound(index) * (1 + m.RelativeAccuracy()) } func (m *LinearlyInterpolatedMapping) LowerBound(index int) float64 { - return m.approximateInverseLog((float64(index) - m.normalizedIndexOffset) / m.multiplier) + return m.approximateInverseLog((float64(index) - m.indexOffset) / m.multiplier) } -// Return an approximation of log(1) + Math.log(x) / Math.log(2)} +// Return an approximation of Math.log(x) / Math.log(2) func (m *LinearlyInterpolatedMapping) approximateLog(x float64) float64 { bits := math.Float64bits(x) - return getExponent(bits) + getSignificandPlusOne(bits) + return getExponent(bits) + getSignificandPlusOne(bits) - 1 } // The exact inverse of approximateLog. func (m *LinearlyInterpolatedMapping) approximateInverseLog(x float64) float64 { - exponent := math.Floor(x - 1) - significandPlusOne := x - exponent + exponent := math.Floor(x) + significandPlusOne := x - exponent + 1 return buildFloat64(int(exponent), significandPlusOne) } func (m *LinearlyInterpolatedMapping) MinIndexableValue() float64 { - return math.Max( - math.Exp2((math.MinInt32-m.normalizedIndexOffset)/m.multiplier-m.approximateLog(1)+1), // so that index >= MinInt32 - minNormalFloat64*(1+m.relativeAccuracy)/(1-m.relativeAccuracy), - ) + return m.minIndexableValue } func (m *LinearlyInterpolatedMapping) MaxIndexableValue() float64 { - return math.Min( - math.Exp2((math.MaxInt32-m.normalizedIndexOffset)/m.multiplier-m.approximateLog(float64(1))-1), // so that index <= MaxInt32 - math.Exp(expOverflow)/(1+m.relativeAccuracy), // so that math.Exp does not overflow - ) + return m.maxIndexableValue } func (m *LinearlyInterpolatedMapping) RelativeAccuracy() float64 { - return m.relativeAccuracy -} - -func (m *LinearlyInterpolatedMapping) gamma() float64 { - return math.Exp2(1 / m.multiplier) + return 1 - 2/(1+math.Exp(math.Log2(m.gamma))) } // Generates a protobuf representation of this LinearlyInterpolatedMapping. func (m *LinearlyInterpolatedMapping) ToProto() *sketchpb.IndexMapping { return &sketchpb.IndexMapping{ - Gamma: m.gamma(), - IndexOffset: m.normalizedIndexOffset + m.approximateLog(1)*m.multiplier, + Gamma: m.gamma, + IndexOffset: m.indexOffset, Interpolation: sketchpb.IndexMapping_LINEAR, } } func (m *LinearlyInterpolatedMapping) Encode(b *[]byte) { enc.EncodeFlag(b, enc.FlagIndexMappingBaseLinear) - enc.EncodeFloat64LE(b, m.gamma()) - enc.EncodeFloat64LE(b, m.normalizedIndexOffset+m.approximateLog(1)*m.multiplier) + enc.EncodeFloat64LE(b, m.gamma) + enc.EncodeFloat64LE(b, m.indexOffset) } func (m *LinearlyInterpolatedMapping) string() string { var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("relativeAccuracy: %v, multiplier: %v, normalizedIndexOffset: %v\n", m.relativeAccuracy, m.multiplier, m.normalizedIndexOffset)) + buffer.WriteString(fmt.Sprintf("gamma: %v, indexOffset: %v\n", m.gamma, m.indexOffset)) return buffer.String() } diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/logarithmic_mapping.go b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/logarithmic_mapping.go index f7c33e435..474e74d93 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/logarithmic_mapping.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/mapping/logarithmic_mapping.go @@ -15,23 +15,24 @@ import ( "github.com/DataDog/sketches-go/ddsketch/pb/sketchpb" ) -// An IndexMapping that is memory-optimal, that is to say that given a targeted relative accuracy, it -// requires the least number of indices to cover a given range of values. This is done by logarithmically +// LogarithmicMapping is an IndexMapping that is memory-optimal, that is to say +// that given a targeted relative accuracy, it requires the least number of +// indices to cover a given range of values. This is done by logarithmically // mapping floating-point values to integers. type LogarithmicMapping struct { - relativeAccuracy float64 - multiplier float64 - normalizedIndexOffset float64 + gamma float64 // base + indexOffset float64 + multiplier float64 // precomputed for performance + minIndexableValue float64 + maxIndexableValue float64 } func NewLogarithmicMapping(relativeAccuracy float64) (*LogarithmicMapping, error) { if relativeAccuracy <= 0 || relativeAccuracy >= 1 { return nil, errors.New("The relative accuracy must be between 0 and 1.") } - m := &LogarithmicMapping{ - relativeAccuracy: relativeAccuracy, - multiplier: 1 / math.Log1p(2*relativeAccuracy/(1-relativeAccuracy)), - } + gamma := (1 + relativeAccuracy) / (1 - relativeAccuracy) // > 1 + m, _ := NewLogarithmicMappingWithGamma(gamma, 0) return m, nil } @@ -39,10 +40,19 @@ func NewLogarithmicMappingWithGamma(gamma, indexOffset float64) (*LogarithmicMap if gamma <= 1 { return nil, errors.New("Gamma must be greater than 1.") } + multiplier := 1 / math.Log(gamma) m := &LogarithmicMapping{ - relativeAccuracy: 1 - 2/(1+gamma), - multiplier: 1 / math.Log(gamma), - normalizedIndexOffset: indexOffset, + gamma: gamma, + indexOffset: indexOffset, + multiplier: multiplier, + minIndexableValue: math.Max( + math.Exp((math.MinInt32-indexOffset)/multiplier+1), // so that index >= MinInt32 + minNormalFloat64*gamma, + ), + maxIndexableValue: math.Min( + math.Exp((math.MaxInt32-indexOffset)/multiplier-1), // so that index <= MaxInt32 + math.Exp(expOverflow)/(2*gamma)*(gamma+1), // so that math.Exp does not overflow + ), } return m, nil } @@ -53,11 +63,11 @@ func (m *LogarithmicMapping) Equals(other IndexMapping) bool { return false } tol := 1e-12 - return (withinTolerance(m.multiplier, o.multiplier, tol) && withinTolerance(m.normalizedIndexOffset, o.normalizedIndexOffset, tol)) + return withinTolerance(m.gamma, o.gamma, tol) && withinTolerance(m.indexOffset, o.indexOffset, tol) } func (m *LogarithmicMapping) Index(value float64) int { - index := math.Log(value)*m.multiplier + m.normalizedIndexOffset + index := math.Log(value)*m.multiplier + m.indexOffset if index >= 0 { return int(index) } else { @@ -66,53 +76,43 @@ func (m *LogarithmicMapping) Index(value float64) int { } func (m *LogarithmicMapping) Value(index int) float64 { - return m.LowerBound(index) * (1 + m.relativeAccuracy) + return m.LowerBound(index) * (1 + m.RelativeAccuracy()) } func (m *LogarithmicMapping) LowerBound(index int) float64 { - return math.Exp((float64(index) - m.normalizedIndexOffset) / m.multiplier) + return math.Exp((float64(index) - m.indexOffset) / m.multiplier) } func (m *LogarithmicMapping) MinIndexableValue() float64 { - return math.Max( - math.Exp((math.MinInt32-m.normalizedIndexOffset)/m.multiplier+1), // so that index >= MinInt32 - minNormalFloat64*(1+m.relativeAccuracy)/(1-m.relativeAccuracy), - ) + return m.minIndexableValue } func (m *LogarithmicMapping) MaxIndexableValue() float64 { - return math.Min( - math.Exp((math.MaxInt32-m.normalizedIndexOffset)/m.multiplier-1), // so that index <= MaxInt32 - math.Exp(expOverflow)/(1+m.relativeAccuracy), // so that math.Exp does not overflow - ) + return m.maxIndexableValue } func (m *LogarithmicMapping) RelativeAccuracy() float64 { - return m.relativeAccuracy -} - -func (m *LogarithmicMapping) gamma() float64 { - return (1 + m.relativeAccuracy) / (1 - m.relativeAccuracy) + return 1 - 2/(1+m.gamma) } // Generates a protobuf representation of this LogarithicMapping. func (m *LogarithmicMapping) ToProto() *sketchpb.IndexMapping { return &sketchpb.IndexMapping{ - Gamma: m.gamma(), - IndexOffset: m.normalizedIndexOffset, + Gamma: m.gamma, + IndexOffset: m.indexOffset, Interpolation: sketchpb.IndexMapping_NONE, } } func (m *LogarithmicMapping) Encode(b *[]byte) { enc.EncodeFlag(b, enc.FlagIndexMappingBaseLogarithmic) - enc.EncodeFloat64LE(b, m.gamma()) - enc.EncodeFloat64LE(b, m.normalizedIndexOffset) + enc.EncodeFloat64LE(b, m.gamma) + enc.EncodeFloat64LE(b, m.indexOffset) } func (m *LogarithmicMapping) string() string { var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("relativeAccuracy: %v, multiplier: %v, normalizedIndexOffset: %v\n", m.relativeAccuracy, m.multiplier, m.normalizedIndexOffset)) + buffer.WriteString(fmt.Sprintf("gamma: %v, indexOffset: %v\n", m.gamma, m.indexOffset)) return buffer.String() } diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/pb/sketchpb/ddsketch.pb.go b/vendor/github.com/DataDog/sketches-go/ddsketch/pb/sketchpb/ddsketch.pb.go index 424dedfed..9dbd6f293 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/pb/sketchpb/ddsketch.pb.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/pb/sketchpb/ddsketch.pb.go @@ -4,8 +4,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.28.0 +// protoc v3.19.4 // source: ddsketch.proto package sketchpb diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/stat/summary.go b/vendor/github.com/DataDog/sketches-go/ddsketch/stat/summary.go index 0381a055b..5d56f3944 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/stat/summary.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/stat/summary.go @@ -5,7 +5,10 @@ package stat -import "math" +import ( + "fmt" + "math" +) // SummaryStatistics keeps track of the count, the sum, the min and the max of // recorded values. We use a compensated sum to avoid accumulating rounding @@ -30,6 +33,27 @@ func NewSummaryStatistics() *SummaryStatistics { } } +// NewSummaryStatisticsFromData constructs SummaryStatistics from the provided data. +func NewSummaryStatisticsFromData(count, sum, min, max float64) (*SummaryStatistics, error) { + if !(count >= 0) { + return nil, fmt.Errorf("count (%g) must be positive or zero", count) + } + if count > 0 && min > max { + return nil, fmt.Errorf("min (%g) cannot be greater than max (%g) if count (%g) is positive", min, max, count) + } + if count == 0 && (min != math.Inf(1) || max != math.Inf(-1)) { + return nil, fmt.Errorf("empty summary statistics must have min (%g) and max (%g) equal to positive and negative infinities respectively", min, max) + } + return &SummaryStatistics{ + count: count, + sum: sum, + sumCompensation: 0, + simpleSum: sum, + min: min, + max: max, + }, nil +} + func (s *SummaryStatistics) Count() float64 { return s.count } diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/store/buffered_paginated.go b/vendor/github.com/DataDog/sketches-go/ddsketch/store/buffered_paginated.go index f08bf555e..11f43107d 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/store/buffered_paginated.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/store/buffered_paginated.go @@ -376,24 +376,29 @@ func (s *BufferedPaginatedStore) minIndexWithCumulCount(predicate func(float64) func (s *BufferedPaginatedStore) MergeWith(other Store) { o, ok := other.(*BufferedPaginatedStore) - if ok && len(o.pages) == 0 { - // Optimized merging if the other store only has buffered data. - oBufferOffset := 0 - for { - bufferCapOverhead := max(cap(s.buffer), s.bufferCompactionTriggerLen) - len(s.buffer) - if bufferCapOverhead >= len(o.buffer)-oBufferOffset { - s.buffer = append(s.buffer, o.buffer[oBufferOffset:]...) - return + if ok && s.pageLenLog2 == o.pageLenLog2 { + // Merge pages. + for oPageOffset, oPage := range o.pages { + if len(oPage) == 0 { + continue + } + oPageIndex := o.minPageIndex + oPageOffset + page := s.page(oPageIndex, true) + for i, oCount := range oPage { + page[i] += oCount } - s.buffer = append(s.buffer, o.buffer[oBufferOffset:oBufferOffset+bufferCapOverhead]...) - oBufferOffset += bufferCapOverhead - s.compact() } - } - // Fallback merging. - for bin := range other.Bins() { - s.AddBin(bin) + // Merge buffers. + for _, index := range o.buffer { + s.Add(index) + } + } else { + // Fallback merging. + other.ForEach(func(index int, count float64) (stop bool) { + s.AddWithCount(index, count) + return false + }) } } @@ -541,9 +546,10 @@ func (s *BufferedPaginatedStore) ToProto() *sketchpb.Store { } // FIXME: add heuristic to use contiguousBinCounts when cheaper. binCounts := make(map[int32]float64) - for bin := range s.Bins() { - binCounts[int32(bin.index)] = bin.count - } + s.ForEach(func(index int, count float64) (stop bool) { + binCounts[int32(index)] = count + return false + }) return &sketchpb.Store{ BinCounts: binCounts, } @@ -570,6 +576,7 @@ func (s *BufferedPaginatedStore) Reweight(w float64) error { } func (s *BufferedPaginatedStore) Encode(b *[]byte, t enc.FlagType) { + s.compact() if len(s.buffer) > 0 { enc.EncodeFlag(b, enc.NewFlag(t, enc.BinEncodingIndexDeltas)) enc.EncodeUvarint64(b, uint64(len(s.buffer))) diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_highest_dense_store.go b/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_highest_dense_store.go index 4cc78324a..2a431a176 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_highest_dense_store.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_highest_dense_store.go @@ -136,9 +136,10 @@ func (s *CollapsingHighestDenseStore) MergeWith(other Store) { } o, ok := other.(*CollapsingHighestDenseStore) if !ok { - for bin := range other.Bins() { - s.AddBin(bin) - } + other.ForEach(func(index int, count float64) (stop bool) { + s.AddWithCount(index, count) + return false + }) return } if o.minIndex < s.minIndex || o.maxIndex > s.maxIndex { diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_lowest_dense_store.go b/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_lowest_dense_store.go index 7549083b6..80ae2a507 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_lowest_dense_store.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/store/collapsing_lowest_dense_store.go @@ -141,9 +141,10 @@ func (s *CollapsingLowestDenseStore) MergeWith(other Store) { } o, ok := other.(*CollapsingLowestDenseStore) if !ok { - for bin := range other.Bins() { - s.AddBin(bin) - } + other.ForEach(func(index int, count float64) (stop bool) { + s.AddWithCount(index, count) + return false + }) return } if o.minIndex < s.minIndex || o.maxIndex > s.maxIndex { diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/store/dense_store.go b/vendor/github.com/DataDog/sketches-go/ddsketch/store/dense_store.go index 5d77e487b..2c4a3d4a0 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/store/dense_store.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/store/dense_store.go @@ -170,9 +170,10 @@ func (s *DenseStore) MergeWith(other Store) { } o, ok := other.(*DenseStore) if !ok { - for bin := range other.Bins() { - s.AddBin(bin) - } + other.ForEach(func(index int, count float64) (stop bool) { + s.AddWithCount(index, count) + return false + }) return } if o.minIndex < s.minIndex || o.maxIndex > s.maxIndex { diff --git a/vendor/github.com/DataDog/sketches-go/ddsketch/store/sparse.go b/vendor/github.com/DataDog/sketches-go/ddsketch/store/sparse.go index 310e4fc25..9a07836e9 100644 --- a/vendor/github.com/DataDog/sketches-go/ddsketch/store/sparse.go +++ b/vendor/github.com/DataDog/sketches-go/ddsketch/store/sparse.go @@ -136,9 +136,10 @@ func (s *SparseStore) KeyAtRank(rank float64) int { } func (s *SparseStore) MergeWith(store Store) { - for bin := range store.Bins() { - s.AddBin(bin) - } + store.ForEach(func(index int, count float64) (stop bool) { + s.AddWithCount(index, count) + return false + }) } func (s *SparseStore) ToProto() *sketchpb.Store { diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes new file mode 100644 index 000000000..94f480de9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore index b883f1fdc..815e20660 100644 --- a/vendor/github.com/Microsoft/go-winio/.gitignore +++ b/vendor/github.com/Microsoft/go-winio/.gitignore @@ -1 +1,10 @@ +.vscode/ + *.exe + +# testing +testdata + +# go workspaces +go.work +go.work.sum diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml new file mode 100644 index 000000000..7b503d26a --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.golangci.yml @@ -0,0 +1,149 @@ +run: + skip-dirs: + - pkg/etw/sample + +linters: + enable: + # style + - containedctx # struct contains a context + - dupl # duplicate code + - errname # erorrs are named correctly + - nolintlint # "//nolint" directives are properly explained + - revive # golint replacement + - unconvert # unnecessary conversions + - wastedassign + + # bugs, performance, unused, etc ... + - contextcheck # function uses a non-inherited context + - errorlint # errors not wrapped for 1.13 + - exhaustive # check exhaustiveness of enum switch statements + - gofmt # files are gofmt'ed + - gosec # security + - nilerr # returns nil even with non-nil error + - unparam # unused function params + +issues: + exclude-rules: + # err is very often shadowed in nested scopes + - linters: + - govet + text: '^shadow: declaration of "err" shadows declaration' + + # ignore long lines for skip autogen directives + - linters: + - revive + text: "^line-length-limit: " + source: "^//(go:generate|sys) " + + #TODO: remove after upgrading to go1.18 + # ignore comment spacing for nolint and sys directives + - linters: + - revive + text: "^comment-spacings: no space between comment delimiter and comment text" + source: "//(cspell:|nolint:|sys |todo)" + + # not on go 1.18 yet, so no any + - linters: + - revive + text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'" + + # allow unjustified ignores of error checks in defer statements + - linters: + - nolintlint + text: "^directive `//nolint:errcheck` should provide explanation" + source: '^\s*defer ' + + # allow unjustified ignores of error lints for io.EOF + - linters: + - nolintlint + text: "^directive `//nolint:errorlint` should provide explanation" + source: '[=|!]= io.EOF' + + +linters-settings: + exhaustive: + default-signifies-exhaustive: true + govet: + enable-all: true + disable: + # struct order is often for Win32 compat + # also, ignore pointer bytes/GC issues for now until performance becomes an issue + - fieldalignment + check-shadowing: true + nolintlint: + allow-leading-space: false + require-explanation: true + require-specific: true + revive: + # revive is more configurable than static check, so likely the preferred alternative to static-check + # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997) + enable-all-rules: + true + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md + rules: + # rules with required arguments + - name: argument-limit + disabled: true + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: file-header + disabled: true + - name: function-length + disabled: true + - name: function-result-limit + disabled: true + - name: max-public-structs + disabled: true + # geneally annoying rules + - name: add-constant # complains about any and all strings and integers + disabled: true + - name: confusing-naming # we frequently use "Foo()" and "foo()" together + disabled: true + - name: flag-parameter # excessive, and a common idiom we use + disabled: true + - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead + disabled: true + # general config + - name: line-length-limit + arguments: + - 140 + - name: var-naming + arguments: + - [] + - - CID + - CRI + - CTRD + - DACL + - DLL + - DOS + - ETW + - FSCTL + - GCS + - GMSA + - HCS + - HV + - IO + - LCOW + - LDAP + - LPAC + - LTSC + - MMIO + - NT + - OCI + - PMEM + - PWSH + - RX + - SACl + - SID + - SMB + - TX + - VHD + - VHDX + - VMID + - VPCI + - WCOW + - WIM diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md index 683be1dcf..7474b4f0b 100644 --- a/vendor/github.com/Microsoft/go-winio/README.md +++ b/vendor/github.com/Microsoft/go-winio/README.md @@ -13,16 +13,60 @@ Please see the LICENSE file for licensing information. ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) -declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. +This project welcomes contributions and suggestions. +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit [Microsoft CLA](https://cla.microsoft.com). -When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR -appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. +When you submit a pull request, a CLA-bot will automatically determine whether you need to +provide a CLA and decorate the PR appropriately (e.g., label, comment). +Simply follow the instructions provided by the bot. +You will only need to do this once across all repos using our CLA. -We also require that contributors sign their commits using git commit -s or git commit --signoff to certify they either authored the work themselves -or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for more info, as well as to make sure that you can -attest to the rules listed. Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. +Additionally, the pull request pipeline requires the following steps to be performed before +mergining. +### Code Sign-Off + +We require that contributors sign their commits using [`git commit --signoff`][git-commit-s] +to certify they either authored the work themselves or otherwise have permission to use it in this project. + +A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s]. + +Please see [the developer certificate](https://developercertificate.org) for more info, +as well as to make sure that you can attest to the rules listed. +Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. + +### Linting + +Code must pass a linting stage, which uses [`golangci-lint`][lint]. +The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run +automatically with VSCode by adding the following to your workspace or folder settings: + +```json + "go.lintTool": "golangci-lint", + "go.lintOnSave": "package", +``` + +Additional editor [integrations options are also available][lint-ide]. + +Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root: + +```shell +# use . or specify a path to only lint a package +# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0" +> golangci-lint run ./... +``` + +### Go Generate + +The pipeline checks that auto-generated code, via `go generate`, are up to date. + +This can be done for the entire repo: + +```shell +> go generate ./... +``` ## Code of Conduct @@ -30,8 +74,16 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Special Thanks +Thanks to [natefinch][natefinch] for the inspiration for this library. +See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation. -## Special Thanks -Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe -for another named pipe implementation. +[lint]: https://golangci-lint.run/ +[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration +[lint-install]: https://golangci-lint.run/usage/install/#local-installation + +[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s +[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff + +[natefinch]: https://github.com/natefinch diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go index 2be34af43..09621c884 100644 --- a/vendor/github.com/Microsoft/go-winio/backup.go +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -7,11 +8,12 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "runtime" "syscall" "unicode/utf16" + + "golang.org/x/sys/windows" ) //sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead @@ -24,7 +26,7 @@ const ( BackupAlternateData BackupLink BackupPropertyData - BackupObjectId + BackupObjectId //revive:disable-line:var-naming ID, not Id BackupReparseData BackupSparseBlock BackupTxfsData @@ -34,14 +36,16 @@ const ( StreamSparseAttributes = uint32(8) ) +//nolint:revive // var-naming: ALL_CAPS const ( - WRITE_DAC = 0x40000 - WRITE_OWNER = 0x80000 - ACCESS_SYSTEM_SECURITY = 0x1000000 + WRITE_DAC = windows.WRITE_DAC + WRITE_OWNER = windows.WRITE_OWNER + ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY ) // BackupHeader represents a backup stream of a file. type BackupHeader struct { + //revive:disable-next-line:var-naming ID, not Id Id uint32 // The backup stream ID Attributes uint32 // Stream attributes Size int64 // The size of the stream in bytes @@ -49,8 +53,8 @@ type BackupHeader struct { Offset int64 // The offset of the stream in the file (for BackupSparseBlock only). } -type win32StreamId struct { - StreamId uint32 +type win32StreamID struct { + StreamID uint32 Attributes uint32 Size uint64 NameSize uint32 @@ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader { // Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if // it was not completely read. func (r *BackupStreamReader) Next() (*BackupHeader, error) { - if r.bytesLeft > 0 { + if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this if s, ok := r.r.(io.Seeker); ok { // Make sure Seek on io.SeekCurrent sometimes succeeds // before trying the actual seek. @@ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { r.bytesLeft = 0 } } - if _, err := io.Copy(ioutil.Discard, r); err != nil { + if _, err := io.Copy(io.Discard, r); err != nil { return nil, err } } - var wsi win32StreamId + var wsi win32StreamID if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { return nil, err } hdr := &BackupHeader{ - Id: wsi.StreamId, + Id: wsi.StreamID, Attributes: wsi.Attributes, Size: int64(wsi.Size), } @@ -102,7 +106,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { } hdr.Name = syscall.UTF16ToString(name) } - if wsi.StreamId == BackupSparseBlock { + if wsi.StreamID == BackupSparseBlock { if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { return nil, err } @@ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { return fmt.Errorf("missing %d bytes", w.bytesLeft) } name := utf16.Encode([]rune(hdr.Name)) - wsi := win32StreamId{ - StreamId: hdr.Id, + wsi := win32StreamID{ + StreamID: hdr.Id, Attributes: hdr.Attributes, Size: uint64(hdr.Size), NameSize: uint32(len(name) * 2), @@ -203,7 +207,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { var bytesRead uint32 err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) if err != nil { - return 0, &os.PathError{"BackupRead", r.f.Name(), err} + return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err} } runtime.KeepAlive(r.f) if bytesRead == 0 { @@ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { // the underlying file. func (r *BackupFileReader) Close() error { if r.ctx != 0 { - backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) + _ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) runtime.KeepAlive(r.f) r.ctx = 0 } @@ -242,7 +246,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { var bytesWritten uint32 err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) if err != nil { - return 0, &os.PathError{"BackupWrite", w.f.Name(), err} + return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err} } runtime.KeepAlive(w.f) if int(bytesWritten) != len(b) { @@ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { // close the underlying file. func (w *BackupFileWriter) Close() error { if w.ctx != 0 { - backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) + _ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) runtime.KeepAlive(w.f) w.ctx = 0 } @@ -271,7 +275,13 @@ func OpenForBackup(path string, access uint32, share uint32, createmode uint32) if err != nil { return nil, err } - h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) + h, err := syscall.CreateFile(&winPath[0], + access, + share, + nil, + createmode, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, + 0) if err != nil { err = &os.PathError{Op: "open", Path: path, Err: err} return nil, err diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go new file mode 100644 index 000000000..1f5bfe2d5 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/doc.go @@ -0,0 +1,22 @@ +// This package provides utilities for efficiently performing Win32 IO operations in Go. +// Currently, this package is provides support for genreal IO and management of +// - named pipes +// - files +// - [Hyper-V sockets] +// +// This code is similar to Go's [net] package, and uses IO completion ports to avoid +// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines. +// +// This limits support to Windows Vista and newer operating systems. +// +// Additionally, this package provides support for: +// - creating and managing GUIDs +// - writing to [ETW] +// - opening and manageing VHDs +// - parsing [Windows Image files] +// - auto-generating Win32 API code +// +// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service +// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw- +// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images +package winio diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go index 4051c1b33..e104dbdfd 100644 --- a/vendor/github.com/Microsoft/go-winio/ea.go +++ b/vendor/github.com/Microsoft/go-winio/ea.go @@ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) if err != nil { err = errInvalidEaBuffer - return + return ea, nb, err } nameOffset := fileFullEaInformationSize @@ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { nextOffset := int(info.NextEntryOffset) if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { err = errInvalidEaBuffer - return + return ea, nb, err } ea.Name = string(b[nameOffset : nameOffset+nameLen]) @@ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { if info.NextEntryOffset != 0 { nb = b[info.NextEntryOffset:] } - return + return ea, nb, err } // DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION @@ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { eas = append(eas, ea) b = nb } - return + return eas, err } func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 0385e4108..175a99d3f 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -10,6 +11,8 @@ import ( "sync/atomic" "syscall" "time" + + "golang.org/x/sys/windows" ) //sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx @@ -23,6 +26,8 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } + +//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg func (b *atomicBool) swap(new bool) bool { var newInt int32 if new { @@ -31,11 +36,6 @@ func (b *atomicBool) swap(new bool) bool { return atomic.SwapInt32((*int32)(b), newInt) == 1 } -const ( - cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 - cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 -) - var ( ErrFileClosed = errors.New("file has already been closed") ErrTimeout = &timeoutError{} @@ -43,28 +43,28 @@ var ( type timeoutError struct{} -func (e *timeoutError) Error() string { return "i/o timeout" } -func (e *timeoutError) Timeout() bool { return true } -func (e *timeoutError) Temporary() bool { return true } +func (*timeoutError) Error() string { return "i/o timeout" } +func (*timeoutError) Timeout() bool { return true } +func (*timeoutError) Temporary() bool { return true } type timeoutChan chan struct{} var ioInitOnce sync.Once var ioCompletionPort syscall.Handle -// ioResult contains the result of an asynchronous IO operation +// ioResult contains the result of an asynchronous IO operation. type ioResult struct { bytes uint32 err error } -// ioOperation represents an outstanding asynchronous Win32 IO +// ioOperation represents an outstanding asynchronous Win32 IO. type ioOperation struct { o syscall.Overlapped ch chan ioResult } -func initIo() { +func initIO() { h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) if err != nil { panic(err) @@ -93,15 +93,15 @@ type deadlineHandler struct { timedout atomicBool } -// makeWin32File makes a new win32File from an existing file handle +// makeWin32File makes a new win32File from an existing file handle. func makeWin32File(h syscall.Handle) (*win32File, error) { f := &win32File{handle: h} - ioInitOnce.Do(initIo) + ioInitOnce.Do(initIO) _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) if err != nil { return nil, err } - err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) + err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE) if err != nil { return nil, err } @@ -120,14 +120,14 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { return f, nil } -// closeHandle closes the resources associated with a Win32 handle +// closeHandle closes the resources associated with a Win32 handle. func (f *win32File) closeHandle() { f.wgLock.Lock() // Atomically set that we are closing, releasing the resources only once. if !f.closing.swap(true) { f.wgLock.Unlock() // cancel all IO and wait for it to complete - cancelIoEx(f.handle, nil) + _ = cancelIoEx(f.handle, nil) f.wg.Wait() // at this point, no new IO can start syscall.Close(f.handle) @@ -143,9 +143,14 @@ func (f *win32File) Close() error { return nil } -// prepareIo prepares for a new IO operation. +// IsClosed checks if the file has been closed. +func (f *win32File) IsClosed() bool { + return f.closing.isSet() +} + +// prepareIO prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. -func (f *win32File) prepareIo() (*ioOperation, error) { +func (f *win32File) prepareIO() (*ioOperation, error) { f.wgLock.RLock() if f.closing.isSet() { f.wgLock.RUnlock() @@ -158,7 +163,7 @@ func (f *win32File) prepareIo() (*ioOperation, error) { return c, nil } -// ioCompletionProcessor processes completed async IOs forever +// ioCompletionProcessor processes completed async IOs forever. func ioCompletionProcessor(h syscall.Handle) { for { var bytes uint32 @@ -172,15 +177,17 @@ func ioCompletionProcessor(h syscall.Handle) { } } -// asyncIo processes the return value from ReadFile or WriteFile, blocking until +// todo: helsaawy - create an asyncIO version that takes a context + +// asyncIO processes the return value from ReadFile or WriteFile, blocking until // the operation has actually completed. -func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { - if err != syscall.ERROR_IO_PENDING { +func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { + if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno return int(bytes), err } if f.closing.isSet() { - cancelIoEx(f.handle, &c.o) + _ = cancelIoEx(f.handle, &c.o) } var timeout timeoutChan @@ -194,7 +201,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er select { case r = <-c.ch: err = r.err - if err == syscall.ERROR_OPERATION_ABORTED { + if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno if f.closing.isSet() { err = ErrFileClosed } @@ -204,10 +211,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) } case <-timeout: - cancelIoEx(f.handle, &c.o) + _ = cancelIoEx(f.handle, &c.o) r = <-c.ch err = r.err - if err == syscall.ERROR_OPERATION_ABORTED { + if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno err = ErrTimeout } } @@ -215,13 +222,14 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er // runtime.KeepAlive is needed, as c is passed via native // code to ioCompletionProcessor, c must remain alive // until the channel read is complete. + // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive? runtime.KeepAlive(c) return int(r.bytes), err } // Read reads from a file handle. func (f *win32File) Read(b []byte) (int, error) { - c, err := f.prepareIo() + c, err := f.prepareIO() if err != nil { return 0, err } @@ -233,13 +241,13 @@ func (f *win32File) Read(b []byte) (int, error) { var bytes uint32 err = syscall.ReadFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, &f.readDeadline, bytes, err) + n, err := f.asyncIO(c, &f.readDeadline, bytes, err) runtime.KeepAlive(b) // Handle EOF conditions. if err == nil && n == 0 && len(b) != 0 { return 0, io.EOF - } else if err == syscall.ERROR_BROKEN_PIPE { + } else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno return 0, io.EOF } else { return n, err @@ -248,7 +256,7 @@ func (f *win32File) Read(b []byte) (int, error) { // Write writes to a file handle. func (f *win32File) Write(b []byte) (int, error) { - c, err := f.prepareIo() + c, err := f.prepareIO() if err != nil { return 0, err } @@ -260,7 +268,7 @@ func (f *win32File) Write(b []byte) (int, error) { var bytes uint32 err = syscall.WriteFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) + n, err := f.asyncIO(c, &f.writeDeadline, bytes, err) runtime.KeepAlive(b) return n, err } diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index 3ab6bff69..702950e72 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -14,13 +15,18 @@ import ( type FileBasicInfo struct { CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime FileAttributes uint32 - pad uint32 // padding + _ uint32 // padding } // GetFileBasicInfo retrieves times and attributes for a file. func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { bi := &FileBasicInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(bi)), + uint32(unsafe.Sizeof(*bi)), + ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -29,7 +35,12 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { // SetFileBasicInfo sets times and attributes for a file. func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { - if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.SetFileInformationByHandle( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(bi)), + uint32(unsafe.Sizeof(*bi)), + ); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -48,7 +59,10 @@ type FileStandardInfo struct { // GetFileStandardInfo retrieves ended information for the file. func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { si := &FileStandardInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), + windows.FileStandardInfo, + (*byte)(unsafe.Pointer(si)), + uint32(unsafe.Sizeof(*si))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -65,7 +79,12 @@ type FileIDInfo struct { // GetFileID retrieves the unique (volume, file ID) pair for a file. func GetFileID(f *os.File) (*FileIDInfo, error) { fileID := &FileIDInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileIdInfo, + (*byte)(unsafe.Pointer(fileID)), + uint32(unsafe.Sizeof(*fileID)), + ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go index b632f8f8b..c88191658 100644 --- a/vendor/github.com/Microsoft/go-winio/hvsock.go +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -1,8 +1,11 @@ +//go:build windows // +build windows package winio import ( + "context" + "errors" "fmt" "io" "net" @@ -11,16 +14,87 @@ import ( "time" "unsafe" + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/socket" "github.com/Microsoft/go-winio/pkg/guid" ) -//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind +const afHVSock = 34 // AF_HYPERV -const ( - afHvSock = 34 // AF_HYPERV +// Well known Service and VM IDs +// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards - socketError = ^uintptr(0) -) +// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions. +func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000 + return guid.GUID{} +} + +// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions. +func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff + return guid.GUID{ + Data1: 0xffffffff, + Data2: 0xffff, + Data3: 0xffff, + Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + } +} + +// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector. +func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838 + return guid.GUID{ + Data1: 0xe0e16197, + Data2: 0xdd56, + Data3: 0x4a10, + Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38}, + } +} + +// HvsockGUIDSiloHost is the address of a silo's host partition: +// - The silo host of a hosted silo is the utility VM. +// - The silo host of a silo on a physical host is the physical host. +func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568 + return guid.GUID{ + Data1: 0x36bd0c5c, + Data2: 0x7276, + Data3: 0x4223, + Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68}, + } +} + +// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions. +func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd + return guid.GUID{ + Data1: 0x90db8b89, + Data2: 0xd35, + Data3: 0x4f79, + Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd}, + } +} + +// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition. +// Listening on this VmId accepts connection from: +// - Inside silos: silo host partition. +// - Inside hosted silo: host of the VM. +// - Inside VM: VM host. +// - Physical host: Not supported. +func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878 + return guid.GUID{ + Data1: 0xa42e7cda, + Data2: 0xd03f, + Data3: 0x480c, + Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78}, + } +} + +// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol. +func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3 + return guid.GUID{ + Data2: 0xfacb, + Data3: 0x11e6, + Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3}, + } +} // An HvsockAddr is an address for a AF_HYPERV socket. type HvsockAddr struct { @@ -35,8 +109,10 @@ type rawHvsockAddr struct { ServiceID guid.GUID } +var _ socket.RawSockaddr = &rawHvsockAddr{} + // Network returns the address's network name, "hvsock". -func (addr *HvsockAddr) Network() string { +func (*HvsockAddr) Network() string { return "hvsock" } @@ -46,14 +122,14 @@ func (addr *HvsockAddr) String() string { // VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port. func VsockServiceID(port uint32) guid.GUID { - g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3") + g := hvsockVsockServiceTemplate() // make a copy g.Data1 = port return g } func (addr *HvsockAddr) raw() rawHvsockAddr { return rawHvsockAddr{ - Family: afHvSock, + Family: afHVSock, VMID: addr.VMID, ServiceID: addr.ServiceID, } @@ -64,20 +140,48 @@ func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { addr.ServiceID = raw.ServiceID } +// Sockaddr returns a pointer to and the size of this struct. +// +// Implements the [socket.RawSockaddr] interface, and allows use in +// [socket.Bind] and [socket.ConnectEx]. +func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) { + return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil +} + +// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`. +func (r *rawHvsockAddr) FromBytes(b []byte) error { + n := int(unsafe.Sizeof(rawHvsockAddr{})) + + if len(b) < n { + return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize) + } + + copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n]) + if r.Family != afHVSock { + return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily) + } + + return nil +} + // HvsockListener is a socket listener for the AF_HYPERV address family. type HvsockListener struct { sock *win32File addr HvsockAddr } +var _ net.Listener = &HvsockListener{} + // HvsockConn is a connected socket of the AF_HYPERV address family. type HvsockConn struct { sock *win32File local, remote HvsockAddr } -func newHvSocket() (*win32File, error) { - fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1) +var _ net.Conn = &HvsockConn{} + +func newHVSocket() (*win32File, error) { + fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1) if err != nil { return nil, os.NewSyscallError("socket", err) } @@ -93,12 +197,12 @@ func newHvSocket() (*win32File, error) { // ListenHvsock listens for connections on the specified hvsock address. func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { l := &HvsockListener{addr: *addr} - sock, err := newHvSocket() + sock, err := newHVSocket() if err != nil { return nil, l.opErr("listen", err) } sa := addr.raw() - err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa))) + err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { return nil, l.opErr("listen", os.NewSyscallError("socket", err)) } @@ -120,7 +224,7 @@ func (l *HvsockListener) Addr() net.Addr { // Accept waits for the next connection and returns it. func (l *HvsockListener) Accept() (_ net.Conn, err error) { - sock, err := newHvSocket() + sock, err := newHVSocket() if err != nil { return nil, l.opErr("accept", err) } @@ -129,27 +233,42 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) { sock.Close() } }() - c, err := l.sock.prepareIo() + c, err := l.sock.prepareIO() if err != nil { return nil, l.opErr("accept", err) } defer l.sock.wg.Done() // AcceptEx, per documentation, requires an extra 16 bytes per address. + // + // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) var addrbuf [addrlen * 2]byte var bytes uint32 - err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o) - _, err = l.sock.asyncIo(c, nil, bytes, err) - if err != nil { + err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o) + if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil { return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) } + conn := &HvsockConn{ sock: sock, } + // The local address returned in the AcceptEx buffer is the same as the Listener socket's + // address. However, the service GUID reported by GetSockName is different from the Listeners + // socket, and is sometimes the same as the local address of the socket that dialed the + // address, with the service GUID.Data1 incremented, but othertimes is different. + // todo: does the local address matter? is the listener's address or the actual address appropriate? conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) + + // initialize the accepted socket and update its properties with those of the listening socket + if err = windows.Setsockopt(windows.Handle(sock.handle), + windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT, + (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil { + return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err)) + } + sock = nil return conn, nil } @@ -159,43 +278,171 @@ func (l *HvsockListener) Close() error { return l.sock.Close() } -/* Need to finish ConnectEx handling -func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) { - sock, err := newHvSocket() +// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]). +type HvsockDialer struct { + // Deadline is the time the Dial operation must connect before erroring. + Deadline time.Time + + // Retries is the number of additional connects to try if the connection times out, is refused, + // or the host is unreachable + Retries uint + + // RetryWait is the time to wait after a connection error to retry + RetryWait time.Duration + + rt *time.Timer // redial wait timer +} + +// Dial the Hyper-V socket at addr. +// +// See [HvsockDialer.Dial] for more information. +func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + return (&HvsockDialer{}).Dial(ctx, addr) +} + +// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful. +// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between +// retries. +// +// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx. +func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + op := "dial" + // create the conn early to use opErr() + conn = &HvsockConn{ + remote: *addr, + } + + if !d.Deadline.IsZero() { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, d.Deadline) + defer cancel() + } + + // preemptive timeout/cancellation check + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) + } + + sock, err := newHVSocket() if err != nil { - return nil, err + return nil, conn.opErr(op, err) } defer func() { if sock != nil { sock.Close() } }() - c, err := sock.prepareIo() + + sa := addr.raw() + err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { - return nil, err + return nil, conn.opErr(op, os.NewSyscallError("bind", err)) + } + + c, err := sock.prepareIO() + if err != nil { + return nil, conn.opErr(op, err) } defer sock.wg.Done() var bytes uint32 - err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o) - _, err = sock.asyncIo(ctx, c, nil, bytes, err) + for i := uint(0); i <= d.Retries; i++ { + err = socket.ConnectEx( + windows.Handle(sock.handle), + &sa, + nil, // sendBuf + 0, // sendDataLen + &bytes, + (*windows.Overlapped)(unsafe.Pointer(&c.o))) + _, err = sock.asyncIO(c, nil, bytes, err) + if i < d.Retries && canRedial(err) { + if err = d.redialWait(ctx); err == nil { + continue + } + } + break + } if err != nil { - return nil, err + return nil, conn.opErr(op, os.NewSyscallError("connectex", err)) } - conn := &HvsockConn{ - sock: sock, - remote: *addr, + + // update the connection properties, so shutdown can be used + if err = windows.Setsockopt( + windows.Handle(sock.handle), + windows.SOL_SOCKET, + windows.SO_UPDATE_CONNECT_CONTEXT, + nil, // optvalue + 0, // optlen + ); err != nil { + return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err)) + } + + // get the local name + var sal rawHvsockAddr + err = socket.GetSockName(windows.Handle(sock.handle), &sal) + if err != nil { + return nil, conn.opErr(op, os.NewSyscallError("getsockname", err)) + } + conn.local.fromRaw(&sal) + + // one last check for timeout, since asyncIO doesn't check the context + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) } + + conn.sock = sock sock = nil + return conn, nil } -*/ + +// redialWait waits before attempting to redial, resetting the timer as appropriate. +func (d *HvsockDialer) redialWait(ctx context.Context) (err error) { + if d.RetryWait == 0 { + return nil + } + + if d.rt == nil { + d.rt = time.NewTimer(d.RetryWait) + } else { + // should already be stopped and drained + d.rt.Reset(d.RetryWait) + } + + select { + case <-ctx.Done(): + case <-d.rt.C: + return nil + } + + // stop and drain the timer + if !d.rt.Stop() { + <-d.rt.C + } + return ctx.Err() +} + +// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall. +func canRedial(err error) bool { + //nolint:errorlint // guaranteed to be an Errno + switch err { + case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT, + windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL: + return true + default: + return false + } +} func (conn *HvsockConn) opErr(op string, err error) error { + // translate from "file closed" to "socket closed" + if errors.Is(err, ErrFileClosed) { + err = socket.ErrSocketClosed + } return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} } func (conn *HvsockConn) Read(b []byte) (int, error) { - c, err := conn.sock.prepareIo() + c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("read", err) } @@ -203,10 +450,11 @@ func (conn *HvsockConn) Read(b []byte) (int, error) { buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var flags, bytes uint32 err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) - n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err) + n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err) if err != nil { - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsarecv", err) + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsarecv", eno) } return 0, conn.opErr("read", err) } else if n == 0 { @@ -229,7 +477,7 @@ func (conn *HvsockConn) Write(b []byte) (int, error) { } func (conn *HvsockConn) write(b []byte) (int, error) { - c, err := conn.sock.prepareIo() + c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("write", err) } @@ -237,10 +485,11 @@ func (conn *HvsockConn) write(b []byte) (int, error) { buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var bytes uint32 err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) - n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err) + n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err) if err != nil { - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsasend", err) + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsasend", eno) } return 0, conn.opErr("write", err) } @@ -252,29 +501,43 @@ func (conn *HvsockConn) Close() error { return conn.sock.Close() } +func (conn *HvsockConn) IsClosed() bool { + return conn.sock.IsClosed() +} + +// shutdown disables sending or receiving on a socket. func (conn *HvsockConn) shutdown(how int) error { - err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD) + if conn.IsClosed() { + return socket.ErrSocketClosed + } + + err := syscall.Shutdown(conn.sock.handle, how) if err != nil { + // If the connection was closed, shutdowns fail with "not connected" + if errors.Is(err, windows.WSAENOTCONN) || + errors.Is(err, windows.WSAESHUTDOWN) { + err = socket.ErrSocketClosed + } return os.NewSyscallError("shutdown", err) } return nil } -// CloseRead shuts down the read end of the socket. +// CloseRead shuts down the read end of the socket, preventing future read operations. func (conn *HvsockConn) CloseRead() error { err := conn.shutdown(syscall.SHUT_RD) if err != nil { - return conn.opErr("close", err) + return conn.opErr("closeread", err) } return nil } -// CloseWrite shuts down the write end of the socket, notifying the other endpoint that -// no more data will be written. +// CloseWrite shuts down the write end of the socket, preventing future write operations and +// notifying the other endpoint that no more data will be written. func (conn *HvsockConn) CloseWrite() error { err := conn.shutdown(syscall.SHUT_WR) if err != nil { - return conn.opErr("close", err) + return conn.opErr("closewrite", err) } return nil } @@ -291,8 +554,13 @@ func (conn *HvsockConn) RemoteAddr() net.Addr { // SetDeadline implements the net.Conn SetDeadline method. func (conn *HvsockConn) SetDeadline(t time.Time) error { - conn.SetReadDeadline(t) - conn.SetWriteDeadline(t) + // todo: implement `SetDeadline` for `win32File` + if err := conn.SetReadDeadline(t); err != nil { + return fmt.Errorf("set read deadline: %w", err) + } + if err := conn.SetWriteDeadline(t); err != nil { + return fmt.Errorf("set write deadline: %w", err) + } return nil } diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go new file mode 100644 index 000000000..1f6538817 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go @@ -0,0 +1,2 @@ +// This package contains Win32 filesystem functionality. +package fs diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go new file mode 100644 index 000000000..509b3ec64 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go @@ -0,0 +1,202 @@ +//go:build windows + +package fs + +import ( + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/stringbuffer" +) + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go + +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew +//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW + +const NullHandle windows.Handle = 0 + +// AccessMask defines standard, specific, and generic rights. +// +// Bitmask: +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---------------+---------------+-------------------------------+ +// |G|G|G|G|Resvd|A| StandardRights| SpecificRights | +// |R|W|E|A| |S| | | +// +-+-------------+---------------+-------------------------------+ +// +// GR Generic Read +// GW Generic Write +// GE Generic Exectue +// GA Generic All +// Resvd Reserved +// AS Access Security System +// +// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask +// +// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights +// +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants +type AccessMask = windows.ACCESS_MASK + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // Not actually any. + // + // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device" + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters + FILE_ANY_ACCESS AccessMask = 0 + + // Specific Object Access + // from ntioapi.h + + FILE_READ_DATA AccessMask = (0x0001) // file & pipe + FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory + + FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe + FILE_ADD_FILE AccessMask = (0x0002) // directory + + FILE_APPEND_DATA AccessMask = (0x0004) // file + FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory + FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe + + FILE_READ_EA AccessMask = (0x0008) // file & directory + FILE_READ_PROPERTIES AccessMask = FILE_READ_EA + + FILE_WRITE_EA AccessMask = (0x0010) // file & directory + FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA + + FILE_EXECUTE AccessMask = (0x0020) // file + FILE_TRAVERSE AccessMask = (0x0020) // directory + + FILE_DELETE_CHILD AccessMask = (0x0040) // directory + + FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all + + FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all + + FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) + FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) + FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE) + FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) + + SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF + + // Standard Access + // from ntseapi.h + + DELETE AccessMask = 0x0001_0000 + READ_CONTROL AccessMask = 0x0002_0000 + WRITE_DAC AccessMask = 0x0004_0000 + WRITE_OWNER AccessMask = 0x0008_0000 + SYNCHRONIZE AccessMask = 0x0010_0000 + + STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000 + + STANDARD_RIGHTS_READ AccessMask = READ_CONTROL + STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL + STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL + + STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000 +) + +type FileShareMode uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + FILE_SHARE_NONE FileShareMode = 0x00 + FILE_SHARE_READ FileShareMode = 0x01 + FILE_SHARE_WRITE FileShareMode = 0x02 + FILE_SHARE_DELETE FileShareMode = 0x04 + FILE_SHARE_VALID_FLAGS FileShareMode = 0x07 +) + +type FileCreationDisposition uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // from winbase.h + + CREATE_NEW FileCreationDisposition = 0x01 + CREATE_ALWAYS FileCreationDisposition = 0x02 + OPEN_EXISTING FileCreationDisposition = 0x03 + OPEN_ALWAYS FileCreationDisposition = 0x04 + TRUNCATE_EXISTING FileCreationDisposition = 0x05 +) + +// CreateFile and co. take flags or attributes together as one parameter. +// Define alias until we can use generics to allow both + +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants +type FileFlagOrAttribute uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( // from winnt.h + FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000 + FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000 + FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000 + FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000 + FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000 + FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000 + FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000 + FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000 + FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000 + FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000 + FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000 +) + +type FileSQSFlag = FileFlagOrAttribute + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( // from winbase.h + SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16) + SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16) + SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16) + SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16) + + SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000 + SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000 +) + +// GetFinalPathNameByHandle flags +// +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters +type GetFinalPathFlag uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + GetFinalPathDefaultFlag GetFinalPathFlag = 0x0 + + FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0 + FILE_NAME_OPENED GetFinalPathFlag = 0x8 + + VOLUME_NAME_DOS GetFinalPathFlag = 0x0 + VOLUME_NAME_GUID GetFinalPathFlag = 0x1 + VOLUME_NAME_NT GetFinalPathFlag = 0x2 + VOLUME_NAME_NONE GetFinalPathFlag = 0x4 +) + +// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle +// with the given handle and flags. It transparently takes care of creating a buffer of the +// correct size for the call. +// +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew +func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) { + b := stringbuffer.NewWString() + //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n? + for { + n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags)) + if err != nil { + return "", err + } + // If the buffer wasn't large enough, n will be the total size needed (including null terminator). + // Resize and try again. + if n > b.Cap() { + b.ResizeTo(n) + continue + } + // If the buffer is large enough, n will be the size not including the null terminator. + // Convert to a Go string and return. + return b.String(), nil + } +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go new file mode 100644 index 000000000..81760ac67 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go @@ -0,0 +1,12 @@ +package fs + +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level +type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32` + +// Impersonation levels +const ( + SecurityAnonymous SecurityImpersonationLevel = 0 + SecurityIdentification SecurityImpersonationLevel = 1 + SecurityImpersonation SecurityImpersonationLevel = 2 + SecurityDelegation SecurityImpersonationLevel = 3 +) diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go new file mode 100644 index 000000000..e2f7bb24e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go @@ -0,0 +1,64 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package fs + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procCreateFileW = modkernel32.NewProc("CreateFileW") +) + +func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile) +} + +func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) + handle = windows.Handle(r0) + if handle == windows.InvalidHandle { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go new file mode 100644 index 000000000..7e82f9afa --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go @@ -0,0 +1,20 @@ +package socket + +import ( + "unsafe" +) + +// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The +// struct must meet the Win32 sockaddr requirements specified here: +// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 +// +// Specifically, the struct size must be least larger than an int16 (unsigned short) +// for the address family. +type RawSockaddr interface { + // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing + // for the RawSockaddr's data to be overwritten by syscalls (if necessary). + // + // It is the callers responsibility to validate that the values are valid; invalid + // pointers or size can cause a panic. + Sockaddr() (unsafe.Pointer, int32, error) +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go new file mode 100644 index 000000000..aeb7b7250 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go @@ -0,0 +1,179 @@ +//go:build windows + +package socket + +import ( + "errors" + "fmt" + "net" + "sync" + "syscall" + "unsafe" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go + +//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname +//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername +//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind + +const socketError = uintptr(^uint32(0)) + +var ( + // todo(helsaawy): create custom error types to store the desired vs actual size and addr family? + + ErrBufferSize = errors.New("buffer size") + ErrAddrFamily = errors.New("address family") + ErrInvalidPointer = errors.New("invalid pointer") + ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed) +) + +// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error) + +// GetSockName writes the local address of socket s to the [RawSockaddr] rsa. +// If rsa is not large enough, the [windows.WSAEFAULT] is returned. +func GetSockName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + // although getsockname returns WSAEFAULT if the buffer is too small, it does not set + // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy + return getsockname(s, ptr, &l) +} + +// GetPeerName returns the remote address the socket is connected to. +// +// See [GetSockName] for more information. +func GetPeerName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return getpeername(s, ptr, &l) +} + +func Bind(s windows.Handle, rsa RawSockaddr) (err error) { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return bind(s, ptr, l) +} + +// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the +// their sockaddr interface, so they cannot be used with HvsockAddr +// Replicate functionality here from +// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go + +// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at +// runtime via a WSAIoctl call: +// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks + +type runtimeFunc struct { + id guid.GUID + once sync.Once + addr uintptr + err error +} + +func (f *runtimeFunc) Load() error { + f.once.Do(func() { + var s windows.Handle + s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP) + if f.err != nil { + return + } + defer windows.CloseHandle(s) //nolint:errcheck + + var n uint32 + f.err = windows.WSAIoctl(s, + windows.SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&f.id)), + uint32(unsafe.Sizeof(f.id)), + (*byte)(unsafe.Pointer(&f.addr)), + uint32(unsafe.Sizeof(f.addr)), + &n, + nil, // overlapped + 0, // completionRoutine + ) + }) + return f.err +} + +var ( + // todo: add `AcceptEx` and `GetAcceptExSockaddrs` + WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS + Data1: 0x25a207b9, + Data2: 0xddf3, + Data3: 0x4660, + Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, + } + + connectExFunc = runtimeFunc{id: WSAID_CONNECTEX} +) + +func ConnectEx( + fd windows.Handle, + rsa RawSockaddr, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) error { + if err := connectExFunc.Load(); err != nil { + return fmt.Errorf("failed to load ConnectEx function pointer: %w", err) + } + ptr, n, err := rsa.Sockaddr() + if err != nil { + return err + } + return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) +} + +// BOOL LpfnConnectex( +// [in] SOCKET s, +// [in] const sockaddr *name, +// [in] int namelen, +// [in, optional] PVOID lpSendBuffer, +// [in] DWORD dwSendDataLength, +// [out] LPDWORD lpdwBytesSent, +// [in] LPOVERLAPPED lpOverlapped +// ) + +func connectEx( + s windows.Handle, + name unsafe.Pointer, + namelen int32, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) (err error) { + // todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN + r1, _, e1 := syscall.Syscall9(connectExFunc.addr, + 7, + uintptr(s), + uintptr(name), + uintptr(namelen), + uintptr(unsafe.Pointer(sendBuf)), + uintptr(sendDataLen), + uintptr(unsafe.Pointer(bytesSent)), + uintptr(unsafe.Pointer(overlapped)), + 0, + 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return err +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go new file mode 100644 index 000000000..6d2e1a9e4 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go @@ -0,0 +1,72 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package socket + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") + + procbind = modws2_32.NewProc("bind") + procgetpeername = modws2_32.NewProc("getpeername") + procgetsockname = modws2_32.NewProc("getsockname") +) + +func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go new file mode 100644 index 000000000..7ad505702 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go @@ -0,0 +1,132 @@ +package stringbuffer + +import ( + "sync" + "unicode/utf16" +) + +// TODO: worth exporting and using in mkwinsyscall? + +// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate +// large path strings: +// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310. +const MinWStringCap = 310 + +// use *[]uint16 since []uint16 creates an extra allocation where the slice header +// is copied to heap and then referenced via pointer in the interface header that sync.Pool +// stores. +var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly + New: func() interface{} { + b := make([]uint16, MinWStringCap) + return &b + }, +} + +func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) } + +// freeBuffer copies the slice header data, and puts a pointer to that in the pool. +// This avoids taking a pointer to the slice header in WString, which can be set to nil. +func freeBuffer(b []uint16) { pathPool.Put(&b) } + +// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings +// for interacting with Win32 APIs. +// Sizes are specified as uint32 and not int. +// +// It is not thread safe. +type WString struct { + // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future. + + // raw buffer + b []uint16 +} + +// NewWString returns a [WString] allocated from a shared pool with an +// initial capacity of at least [MinWStringCap]. +// Since the buffer may have been previously used, its contents are not guaranteed to be empty. +// +// The buffer should be freed via [WString.Free] +func NewWString() *WString { + return &WString{ + b: newBuffer(), + } +} + +func (b *WString) Free() { + if b.empty() { + return + } + freeBuffer(b.b) + b.b = nil +} + +// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the +// previous buffer back into pool. +func (b *WString) ResizeTo(c uint32) uint32 { + // allready sufficient (or n is 0) + if c <= b.Cap() { + return b.Cap() + } + + if c <= MinWStringCap { + c = MinWStringCap + } + // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places + if c <= 2*b.Cap() { + c = 2 * b.Cap() + } + + b2 := make([]uint16, c) + if !b.empty() { + copy(b2, b.b) + freeBuffer(b.b) + } + b.b = b2 + return c +} + +// Buffer returns the underlying []uint16 buffer. +func (b *WString) Buffer() []uint16 { + if b.empty() { + return nil + } + return b.b +} + +// Pointer returns a pointer to the first uint16 in the buffer. +// If the [WString.Free] has already been called, the pointer will be nil. +func (b *WString) Pointer() *uint16 { + if b.empty() { + return nil + } + return &b.b[0] +} + +// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer. +// +// It assumes that the data is null-terminated. +func (b *WString) String() string { + // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows" + // and would make this code Windows-only, which makes no sense. + // So copy UTF16ToString code into here. + // If other windows-specific code is added, switch to [windows.UTF16ToString] + + s := b.b + for i, v := range s { + if v == 0 { + s = s[:i] + break + } + } + return string(utf16.Decode(s)) +} + +// Cap returns the underlying buffer capacity. +func (b *WString) Cap() uint32 { + if b.empty() { + return 0 + } + return b.cap() +} + +func (b *WString) cap() uint32 { return uint32(cap(b.b)) } +func (b *WString) empty() bool { return b == nil || b.cap() == 0 } diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index 96700a73d..25cc81103 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -13,18 +14,21 @@ import ( "syscall" "time" "unsafe" + + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/fs" ) //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe //sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW -//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc -//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile -//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb -//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U -//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl +//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile +//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb +//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U +//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl type ioStatusBlock struct { Status, Information uintptr @@ -51,45 +55,22 @@ type securityDescriptor struct { Control uint16 Owner uintptr Group uintptr - Sacl uintptr - Dacl uintptr + Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl + Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl } -type ntstatus int32 +type ntStatus int32 -func (status ntstatus) Err() error { +func (status ntStatus) Err() error { if status >= 0 { return nil } return rtlNtStatusToDosError(status) } -const ( - cERROR_PIPE_BUSY = syscall.Errno(231) - cERROR_NO_DATA = syscall.Errno(232) - cERROR_PIPE_CONNECTED = syscall.Errno(535) - cERROR_SEM_TIMEOUT = syscall.Errno(121) - - cSECURITY_SQOS_PRESENT = 0x100000 - cSECURITY_ANONYMOUS = 0 - - cPIPE_TYPE_MESSAGE = 4 - - cPIPE_READMODE_MESSAGE = 2 - - cFILE_OPEN = 1 - cFILE_CREATE = 2 - - cFILE_PIPE_MESSAGE_TYPE = 1 - cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 - - cSE_DACL_PRESENT = 4 -) - var ( // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed. - // This error should match net.errClosing since docker takes a dependency on its text. - ErrPipeListenerClosed = errors.New("use of closed network connection") + ErrPipeListenerClosed = net.ErrClosed errPipeWriteClosed = errors.New("pipe has been closed for write") ) @@ -116,9 +97,10 @@ func (f *win32Pipe) RemoteAddr() net.Addr { } func (f *win32Pipe) SetDeadline(t time.Time) error { - f.SetReadDeadline(t) - f.SetWriteDeadline(t) - return nil + if err := f.SetReadDeadline(t); err != nil { + return err + } + return f.SetWriteDeadline(t) } // CloseWrite closes the write side of a message pipe in byte mode. @@ -157,14 +139,14 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { return 0, io.EOF } n, err := f.win32File.Read(b) - if err == io.EOF { + if err == io.EOF { //nolint:errorlint // If this was the result of a zero-byte read, then // it is possible that the read was due to a zero-size // message. Since we are simulating CloseWrite with a // zero-byte message, ensure that all future Read() calls // also return EOF. f.readEOF = true - } else if err == syscall.ERROR_MORE_DATA { + } else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno // ERROR_MORE_DATA indicates that the pipe's read mode is message mode // and the message still has more bytes. Treat this as a success, since // this package presents all named pipes as byte streams. @@ -173,7 +155,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { return n, err } -func (s pipeAddress) Network() string { +func (pipeAddress) Network() string { return "pipe" } @@ -182,18 +164,25 @@ func (s pipeAddress) String() string { } // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. -func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) { +func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) { for { - select { case <-ctx.Done(): return syscall.Handle(0), ctx.Err() default: - h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) + wh, err := fs.CreateFile(*path, + access, + 0, // mode + nil, // security attributes + fs.OPEN_EXISTING, + fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS, + 0, // template file handle + ) + h := syscall.Handle(wh) if err == nil { return h, nil } - if err != cERROR_PIPE_BUSY { + if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno return h, &os.PathError{Err: err, Op: "open", Path: *path} } // Wait 10 msec and try again. This is a rather simplistic @@ -213,9 +202,10 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { } else { absTimeout = time.Now().Add(2 * time.Second) } - ctx, _ := context.WithDeadline(context.Background(), absTimeout) + ctx, cancel := context.WithDeadline(context.Background(), absTimeout) + defer cancel() conn, err := DialPipeContext(ctx, path) - if err == context.DeadlineExceeded { + if errors.Is(err, context.DeadlineExceeded) { return nil, ErrTimeout } return conn, err @@ -232,7 +222,7 @@ func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) { var err error var h syscall.Handle - h, err = tryDialPipe(ctx, &path, access) + h, err = tryDialPipe(ctx, &path, fs.AccessMask(access)) if err != nil { return nil, err } @@ -251,7 +241,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, // If the pipe is in message mode, return a message byte pipe, which // supports CloseWrite(). - if flags&cPIPE_TYPE_MESSAGE != 0 { + if flags&windows.PIPE_TYPE_MESSAGE != 0 { return &win32MessageBytePipe{ win32Pipe: win32Pipe{win32File: f, path: path}, }, nil @@ -283,17 +273,22 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy oa.Length = unsafe.Sizeof(oa) var ntPath unicodeString - if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { + if err := rtlDosPathNameToNtPathName(&path16[0], + &ntPath, + 0, + 0, + ).Err(); err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } defer localFree(ntPath.Buffer) oa.ObjectName = &ntPath + oa.Attributes = windows.OBJ_CASE_INSENSITIVE // The security descriptor is only needed for the first pipe. if first { if sd != nil { - len := uint32(len(sd)) - sdb := localAlloc(0, len) + l := uint32(len(sd)) + sdb := localAlloc(0, l) defer localFree(sdb) copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) @@ -301,28 +296,28 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy // Construct the default named pipe security descriptor. var dacl uintptr if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { - return 0, fmt.Errorf("getting default named pipe ACL: %s", err) + return 0, fmt.Errorf("getting default named pipe ACL: %w", err) } defer localFree(dacl) sdb := &securityDescriptor{ Revision: 1, - Control: cSE_DACL_PRESENT, + Control: windows.SE_DACL_PRESENT, Dacl: dacl, } oa.SecurityDescriptor = sdb } } - typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) + typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS) if c.MessageMode { - typ |= cFILE_PIPE_MESSAGE_TYPE + typ |= windows.FILE_PIPE_MESSAGE_TYPE } - disposition := uint32(cFILE_OPEN) + disposition := uint32(windows.FILE_OPEN) access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) if first { - disposition = cFILE_CREATE + disposition = windows.FILE_CREATE // By not asking for read or write access, the named pipe file system // will put this pipe into an initially disconnected state, blocking // client connections until the next call with first == false. @@ -335,7 +330,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy h syscall.Handle iosb ioStatusBlock ) - err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() + err = ntCreateNamedPipeFile(&h, + access, + &oa, + &iosb, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, + disposition, + 0, + typ, + 0, + 0, + 0xffffffff, + uint32(c.InputBufferSize), + uint32(c.OutputBufferSize), + &timeout).Err() if err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } @@ -380,7 +388,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { p.Close() p = nil err = <-ch - if err == nil || err == ErrFileClosed { + if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno err = ErrPipeListenerClosed } } @@ -402,12 +410,12 @@ func (l *win32PipeListener) listenerRoutine() { p, err = l.makeConnectedServerPipe() // If the connection was immediately closed by the client, try // again. - if err != cERROR_NO_DATA { + if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno break } } responseCh <- acceptResponse{p, err} - closed = err == ErrPipeListenerClosed + closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno } } syscall.Close(l.firstHandle) @@ -469,15 +477,15 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { } func connectPipe(p *win32File) error { - c, err := p.prepareIo() + c, err := p.prepareIO() if err != nil { return err } defer p.wg.Done() err = connectNamedPipe(p.handle, &c.o) - _, err = p.asyncIo(c, nil, 0, err) - if err != nil && err != cERROR_PIPE_CONNECTED { + _, err = p.asyncIO(c, nil, 0, err) + if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno return err } return nil diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go index f497c0e39..48ce4e924 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go @@ -1,5 +1,3 @@ -// +build windows - // Package guid provides a GUID type. The backing structure for a GUID is // identical to that used by the golang.org/x/sys/windows GUID type. // There are two main binary encodings used for a GUID, the big-endian encoding, @@ -9,26 +7,26 @@ package guid import ( "crypto/rand" - "crypto/sha1" + "crypto/sha1" //nolint:gosec // not used for secure application "encoding" "encoding/binary" "fmt" "strconv" - - "golang.org/x/sys/windows" ) +//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment + // Variant specifies which GUID variant (or "type") of the GUID. It determines // how the entirety of the rest of the GUID is interpreted. type Variant uint8 -// The variants specified by RFC 4122. +// The variants specified by RFC 4122 section 4.1.1. const ( // VariantUnknown specifies a GUID variant which does not conform to one of // the variant encodings specified in RFC 4122. VariantUnknown Variant = iota VariantNCS - VariantRFC4122 + VariantRFC4122 // RFC 4122 VariantMicrosoft VariantFuture ) @@ -38,16 +36,13 @@ const ( // hash of an input string. type Version uint8 +func (v Version) String() string { + return strconv.FormatUint(uint64(v), 10) +} + var _ = (encoding.TextMarshaler)(GUID{}) var _ = (encoding.TextUnmarshaler)(&GUID{}) -// GUID represents a GUID/UUID. It has the same structure as -// golang.org/x/sys/windows.GUID so that it can be used with functions expecting -// that type. It is defined as its own type so that stringification and -// marshaling can be supported. The representation matches that used by native -// Windows code. -type GUID windows.GUID - // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122. func NewV4() (GUID, error) { var b [16]byte @@ -70,7 +65,7 @@ func NewV4() (GUID, error) { // big-endian UTF16 stream of bytes. If that is desired, the string can be // encoded as such before being passed to this function. func NewV5(namespace GUID, name []byte) (GUID, error) { - b := sha1.New() + b := sha1.New() //nolint:gosec // not used for secure application namespaceBytes := namespace.ToArray() b.Write(namespaceBytes[:]) b.Write(name) diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go new file mode 100644 index 000000000..805bd3548 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go @@ -0,0 +1,16 @@ +//go:build !windows +// +build !windows + +package guid + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type as that is only available to builds +// targeted at `windows`. The representation matches that used by native Windows +// code. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go new file mode 100644 index 000000000..27e45ee5c --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go @@ -0,0 +1,13 @@ +//go:build windows +// +build windows + +package guid + +import "golang.org/x/sys/windows" + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type so that stringification and +// marshaling can be supported. The representation matches that used by native +// Windows code. +type GUID windows.GUID diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go new file mode 100644 index 000000000..4076d3132 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT. + +package guid + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[VariantUnknown-0] + _ = x[VariantNCS-1] + _ = x[VariantRFC4122-2] + _ = x[VariantMicrosoft-3] + _ = x[VariantFuture-4] +} + +const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture" + +var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33} + +func (i Variant) String() string { + if i >= Variant(len(_Variant_index)-1) { + return "Variant(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Variant_name[_Variant_index[i]:_Variant_index[i+1]] +} diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go index c3dd7c217..0ff9dac90 100644 --- a/vendor/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -24,22 +25,17 @@ import ( //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW const ( - SE_PRIVILEGE_ENABLED = 2 + //revive:disable-next-line:var-naming ALL_CAPS + SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED - ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 + //revive:disable-next-line:var-naming ALL_CAPS + ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED SeBackupPrivilege = "SeBackupPrivilege" SeRestorePrivilege = "SeRestorePrivilege" SeSecurityPrivilege = "SeSecurityPrivilege" ) -const ( - securityAnonymous = iota - securityIdentification - securityImpersonation - securityDelegation -) - var ( privNames = make(map[string]uint64) privNameMutex sync.Mutex @@ -51,11 +47,9 @@ type PrivilegeError struct { } func (e *PrivilegeError) Error() string { - s := "" + s := "Could not enable privilege " if len(e.privileges) > 1 { s = "Could not enable privileges " - } else { - s = "Could not enable privilege " } for i, p := range e.privileges { if i != 0 { @@ -94,7 +88,7 @@ func RunWithPrivileges(names []string, fn func() error) error { } func mapPrivileges(names []string) ([]uint64, error) { - var privileges []uint64 + privileges := make([]uint64, 0, len(names)) privNameMutex.Lock() defer privNameMutex.Unlock() for _, name := range names { @@ -127,7 +121,7 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { return err } - p, _ := windows.GetCurrentProcess() + p := windows.CurrentProcess() var token windows.Token err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) if err != nil { @@ -140,10 +134,10 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { var b bytes.Buffer - binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) + _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) for _, p := range privileges { - binary.Write(&b, binary.LittleEndian, p) - binary.Write(&b, binary.LittleEndian, action) + _ = binary.Write(&b, binary.LittleEndian, p) + _ = binary.Write(&b, binary.LittleEndian, action) } prevState := make([]byte, b.Len()) reqSize := uint32(0) @@ -151,7 +145,7 @@ func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) e if !success { return err } - if err == ERROR_NOT_ALL_ASSIGNED { + if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno return &PrivilegeError{privileges} } return nil @@ -177,7 +171,7 @@ func getPrivilegeName(luid uint64) string { } func newThreadToken() (windows.Token, error) { - err := impersonateSelf(securityImpersonation) + err := impersonateSelf(windows.SecurityImpersonation) if err != nil { return 0, err } diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go index fc1ee4d3a..67d1a104a 100644 --- a/vendor/github.com/Microsoft/go-winio/reparse.go +++ b/vendor/github.com/Microsoft/go-winio/reparse.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package winio import ( @@ -113,16 +116,16 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte { } var b bytes.Buffer - binary.Write(&b, binary.LittleEndian, &data) + _ = binary.Write(&b, binary.LittleEndian, &data) if !rp.IsMountPoint { flags := uint32(0) if relative { flags |= 1 } - binary.Write(&b, binary.LittleEndian, flags) + _ = binary.Write(&b, binary.LittleEndian, flags) } - binary.Write(&b, binary.LittleEndian, ntTarget16) - binary.Write(&b, binary.LittleEndian, target16) + _ = binary.Write(&b, binary.LittleEndian, ntTarget16) + _ = binary.Write(&b, binary.LittleEndian, target16) return b.Bytes() } diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go index db1b370a1..5550ef6b6 100644 --- a/vendor/github.com/Microsoft/go-winio/sd.go +++ b/vendor/github.com/Microsoft/go-winio/sd.go @@ -1,23 +1,25 @@ +//go:build windows // +build windows package winio import ( + "errors" "syscall" "unsafe" + + "golang.org/x/sys/windows" ) //sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW +//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW //sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW +//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW //sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW //sys localFree(mem uintptr) = LocalFree //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength -const ( - cERROR_NONE_MAPPED = syscall.Errno(1332) -) - type AccountLookupError struct { Name string Err error @@ -28,8 +30,10 @@ func (e *AccountLookupError) Error() string { return "lookup account: empty account name specified" } var s string - switch e.Err { - case cERROR_NONE_MAPPED: + switch { + case errors.Is(e.Err, windows.ERROR_INVALID_SID): + s = "the security ID structure is invalid" + case errors.Is(e.Err, windows.ERROR_NONE_MAPPED): s = "not found" default: s = e.Err.Error() @@ -37,6 +41,8 @@ func (e *AccountLookupError) Error() string { return "lookup account " + e.Name + ": " + s } +func (e *AccountLookupError) Unwrap() error { return e.Err } + type SddlConversionError struct { Sddl string Err error @@ -46,15 +52,19 @@ func (e *SddlConversionError) Error() string { return "convert " + e.Sddl + ": " + e.Err.Error() } +func (e *SddlConversionError) Unwrap() error { return e.Err } + // LookupSidByName looks up the SID of an account by name +// +//revive:disable-next-line:var-naming SID, not Sid func LookupSidByName(name string) (sid string, err error) { if name == "" { - return "", &AccountLookupError{name, cERROR_NONE_MAPPED} + return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED} } var sidSize, sidNameUse, refDomainSize uint32 err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) - if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { + if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno return "", &AccountLookupError{name, err} } sidBuffer := make([]byte, sidSize) @@ -73,6 +83,42 @@ func LookupSidByName(name string) (sid string, err error) { return sid, nil } +// LookupNameBySid looks up the name of an account by SID +// +//revive:disable-next-line:var-naming SID, not Sid +func LookupNameBySid(sid string) (name string, err error) { + if sid == "" { + return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED} + } + + sidBuffer, err := windows.UTF16PtrFromString(sid) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + var sidPtr *byte + if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil { + return "", &AccountLookupError{sid, err} + } + defer localFree(uintptr(unsafe.Pointer(sidPtr))) + + var nameSize, refDomainSize, sidNameUse uint32 + err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno + return "", &AccountLookupError{sid, err} + } + + nameBuffer := make([]uint16, nameSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + name = windows.UTF16ToString(nameBuffer) + return name, nil +} + func SddlToSecurityDescriptor(sddl string) ([]byte, error) { var sdBuffer uintptr err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) @@ -87,7 +133,7 @@ func SddlToSecurityDescriptor(sddl string) ([]byte, error) { func SecurityDescriptorToSddl(sd []byte) (string, error) { var sddl *uint16 - // The returned string length seems to including an aribtrary number of terminating NULs. + // The returned string length seems to include an arbitrary number of terminating NULs. // Don't use it. err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) if err != nil { diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go index 5955c99fd..a6ca111b3 100644 --- a/vendor/github.com/Microsoft/go-winio/syscall.go +++ b/vendor/github.com/Microsoft/go-winio/syscall.go @@ -1,3 +1,5 @@ +//go:build windows + package winio -//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go diff --git a/vendor/github.com/Microsoft/go-winio/tools.go b/vendor/github.com/Microsoft/go-winio/tools.go new file mode 100644 index 000000000..2aa045843 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/tools.go @@ -0,0 +1,5 @@ +//go:build tools + +package winio + +import _ "golang.org/x/tools/cmd/stringer" diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go index 176ff75e3..469b16f63 100644 --- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -1,4 +1,6 @@ -// Code generated by 'go generate'; DO NOT EDIT. +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package winio @@ -47,9 +49,11 @@ var ( procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") + procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") @@ -59,7 +63,6 @@ var ( procBackupWrite = modkernel32.NewProc("BackupWrite") procCancelIoEx = modkernel32.NewProc("CancelIoEx") procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procCreateFileW = modkernel32.NewProc("CreateFileW") procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") @@ -74,7 +77,6 @@ var ( procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") - procbind = modws2_32.NewProc("bind") ) func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { @@ -123,6 +125,14 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision return } +func convertStringSidToSid(str *uint16, sid **byte) (err error) { + r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func getSecurityDescriptorLength(sd uintptr) (len uint32) { r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) len = uint32(r0) @@ -154,6 +164,14 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS return } +func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(systemName) @@ -286,24 +304,6 @@ func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { return } -func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(name) - if err != nil { - return - } - return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) -} - -func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - err = errnoErr(e1) - } - return -} - func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) newport = syscall.Handle(r0) @@ -380,25 +380,25 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro return } -func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { +func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) { r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { +func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { +func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlNtStatusToDosError(status ntstatus) (winerr error) { +func rtlNtStatusToDosError(status ntStatus) (winerr error) { r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) if r0 != 0 { winerr = syscall.Errno(r0) @@ -417,11 +417,3 @@ func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint } return } - -func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { - r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if r1 == socketError { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/config.go b/vendor/github.com/coredns/coredns/core/dnsserver/config.go index 3da86271e..9e1116650 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/config.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/config.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "fmt" "net/http" + "time" "github.com/coredns/caddy" "github.com/coredns/coredns/plugin" @@ -53,6 +54,11 @@ type Config struct { // TLSConfig when listening for encrypted connections (gRPC, DNS-over-TLS). TLSConfig *tls.Config + // Timeouts for TCP, TLS and HTTPS servers. + ReadTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration + // TSIG secrets, [name]key. TsigSecret map[string]string diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/https.go b/vendor/github.com/coredns/coredns/core/dnsserver/https.go index 382e06efe..015c52ec5 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/https.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/https.go @@ -4,13 +4,11 @@ import ( "net" "net/http" - "github.com/coredns/coredns/plugin/pkg/nonwriter" + "github.com/miekg/dns" ) -// DoHWriter is a nonwriter.Writer that adds more specific LocalAddr and RemoteAddr methods. +// DoHWriter is a dns.ResponseWriter that adds more specific LocalAddr and RemoteAddr methods. type DoHWriter struct { - nonwriter.Writer - // raddr is the remote's address. This can be optionally set. raddr net.Addr // laddr is our address. This can be optionally set. @@ -18,13 +16,50 @@ type DoHWriter struct { // request is the HTTP request we're currently handling. request *http.Request + + // Msg is a response to be written to the client. + Msg *dns.Msg +} + +// WriteMsg stores the message to be written to the client. +func (d *DoHWriter) WriteMsg(m *dns.Msg) error { + d.Msg = m + return nil +} + +// Write stores the message to be written to the client. +func (d *DoHWriter) Write(b []byte) (int, error) { + d.Msg = new(dns.Msg) + return len(b), d.Msg.Unpack(b) } // RemoteAddr returns the remote address. -func (d *DoHWriter) RemoteAddr() net.Addr { return d.raddr } +func (d *DoHWriter) RemoteAddr() net.Addr { + return d.raddr +} // LocalAddr returns the local address. -func (d *DoHWriter) LocalAddr() net.Addr { return d.laddr } +func (d *DoHWriter) LocalAddr() net.Addr { + return d.laddr +} + +// Request returns the HTTP request. +func (d *DoHWriter) Request() *http.Request { + return d.request +} + +// Close no-op implementation. +func (d *DoHWriter) Close() error { + return nil +} + +// TsigStatus no-op implementation. +func (d *DoHWriter) TsigStatus() error { + return nil +} + +// TsigTimersOnly no-op implementation. +func (d *DoHWriter) TsigTimersOnly(_ bool) {} -// Request returns the HTTP request -func (d *DoHWriter) Request() *http.Request { return d.request } +// Hijack no-op implementation. +func (d *DoHWriter) Hijack() {} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/quic.go b/vendor/github.com/coredns/coredns/core/dnsserver/quic.go new file mode 100644 index 000000000..5c2890a72 --- /dev/null +++ b/vendor/github.com/coredns/coredns/core/dnsserver/quic.go @@ -0,0 +1,60 @@ +package dnsserver + +import ( + "encoding/binary" + "net" + + "github.com/miekg/dns" + "github.com/quic-go/quic-go" +) + +type DoQWriter struct { + localAddr net.Addr + remoteAddr net.Addr + stream quic.Stream + Msg *dns.Msg +} + +func (w *DoQWriter) Write(b []byte) (int, error) { + b = AddPrefix(b) + return w.stream.Write(b) +} + +func (w *DoQWriter) WriteMsg(m *dns.Msg) error { + bytes, err := m.Pack() + if err != nil { + return err + } + + _, err = w.Write(bytes) + if err != nil { + return err + } + + return w.Close() +} + +// Close sends the STREAM FIN signal. +// The server MUST send the response(s) on the same stream and MUST +// indicate, after the last response, through the STREAM FIN +// mechanism that no further data will be sent on that stream. +// See https://www.rfc-editor.org/rfc/rfc9250#section-4.2-7 +func (w *DoQWriter) Close() error { + return w.stream.Close() +} + +// AddPrefix adds a 2-byte prefix with the DNS message length. +func AddPrefix(b []byte) (m []byte) { + m = make([]byte, 2+len(b)) + binary.BigEndian.PutUint16(m, uint16(len(b))) + copy(m[2:], b) + + return m +} + +// These methods implement the dns.ResponseWriter interface from Go DNS. +func (w *DoQWriter) TsigStatus() error { return nil } +func (w *DoQWriter) TsigTimersOnly(b bool) {} +func (w *DoQWriter) Hijack() {} +func (w *DoQWriter) LocalAddr() net.Addr { return w.localAddr } +func (w *DoQWriter) RemoteAddr() net.Addr { return w.remoteAddr } diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/register.go b/vendor/github.com/coredns/coredns/core/dnsserver/register.go index e94accc22..ae001b9f2 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/register.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/register.go @@ -1,7 +1,6 @@ package dnsserver import ( - "flag" "fmt" "net" "time" @@ -17,12 +16,7 @@ import ( const serverType = "dns" -// Any flags defined here, need to be namespaced to the serverType other -// wise they potentially clash with other server types. func init() { - flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port") - flag.StringVar(&Port, "p", DefaultPort, "Default port") - caddy.RegisterServerType(serverType, caddy.ServerType{ Directives: func() []string { return Directives }, DefaultInput: func() caddy.Input { @@ -88,6 +82,8 @@ func (h *dnsContext) InspectServerBlocks(sourceFile string, serverBlocks []caddy port = Port case transport.TLS: port = transport.TLSPort + case transport.QUIC: + port = transport.QUICPort case transport.GRPC: port = transport.GRPCPort case transport.HTTPS: @@ -147,7 +143,12 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) { c.ListenHosts = c.firstConfigInBlock.ListenHosts c.Debug = c.firstConfigInBlock.Debug c.Stacktrace = c.firstConfigInBlock.Stacktrace - c.TLSConfig = c.firstConfigInBlock.TLSConfig + + // Fork TLSConfig for each encrypted connection + c.TLSConfig = c.firstConfigInBlock.TLSConfig.Clone() + c.ReadTimeout = c.firstConfigInBlock.ReadTimeout + c.WriteTimeout = c.firstConfigInBlock.WriteTimeout + c.IdleTimeout = c.firstConfigInBlock.IdleTimeout c.TsigSecret = c.firstConfigInBlock.TsigSecret } @@ -175,6 +176,13 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) { } servers = append(servers, s) + case transport.QUIC: + s, err := NewServerQUIC(addr, group) + if err != nil { + return nil, err + } + servers = append(servers, s) + case transport.GRPC: s, err := NewServergRPC(addr, group) if err != nil { @@ -221,7 +229,8 @@ func (c *Config) AddPlugin(m plugin.Plugin) { } // registerHandler adds a handler to a site's handler registration. Handlers -// use this to announce that they exist to other plugin. +// +// use this to announce that they exist to other plugin. func (c *Config) registerHandler(h plugin.Handler) { if c.registry == nil { c.registry = make(map[string]plugin.Handler) @@ -287,7 +296,7 @@ func (h *dnsContext) validateZonesAndListeningAddresses() error { return nil } -// groupSiteConfigsByListenAddr groups site configs by their listen +// groupConfigsByListenAddr groups site configs by their listen // (bind) address, so sites that use the same listener can be served // on the same server instance. The return value maps the listen // address (what you pass into net.Listen) to the list of site configs. diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server.go b/vendor/github.com/coredns/coredns/core/dnsserver/server.go index 478287bf8..2107e8d01 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server.go @@ -44,6 +44,9 @@ type Server struct { debug bool // disable recover() stacktrace bool // enable stacktrace in recover error log classChaos bool // allow non-INET class queries + idleTimeout time.Duration // Idle timeout for TCP + readTimeout time.Duration // Read timeout for TCP + writeTimeout time.Duration // Write timeout for TCP tsigSecret map[string]string } @@ -60,6 +63,9 @@ func NewServer(addr string, group []*Config) (*Server, error) { Addr: addr, zones: make(map[string][]*Config), graceTimeout: 5 * time.Second, + idleTimeout: 10 * time.Second, + readTimeout: 3 * time.Second, + writeTimeout: 5 * time.Second, tsigSecret: make(map[string]string), } @@ -81,6 +87,17 @@ func NewServer(addr string, group []*Config) (*Server, error) { // append the config to the zone's configs s.zones[site.Zone] = append(s.zones[site.Zone], site) + // set timeouts + if site.ReadTimeout != 0 { + s.readTimeout = site.ReadTimeout + } + if site.WriteTimeout != 0 { + s.writeTimeout = site.WriteTimeout + } + if site.IdleTimeout != 0 { + s.idleTimeout = site.IdleTimeout + } + // copy tsig secrets for key, secret := range site.TsigSecret { s.tsigSecret[key] = secret @@ -130,11 +147,22 @@ var _ caddy.GracefulServer = &Server{} // This implements caddy.TCPServer interface. func (s *Server) Serve(l net.Listener) error { s.m.Lock() - s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { - ctx := context.WithValue(context.Background(), Key{}, s) - ctx = context.WithValue(ctx, LoopKey{}, 0) - s.ServeDNS(ctx, w, r) - }), TsigSecret: s.tsigSecret} + + s.server[tcp] = &dns.Server{Listener: l, + Net: "tcp", + TsigSecret: s.tsigSecret, + MaxTCPQueries: tcpMaxQueries, + ReadTimeout: s.readTimeout, + WriteTimeout: s.writeTimeout, + IdleTimeout: func() time.Duration { + return s.idleTimeout + }, + Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { + ctx := context.WithValue(context.Background(), Key{}, s) + ctx = context.WithValue(ctx, LoopKey{}, 0) + s.ServeDNS(ctx, w, r) + })} + s.m.Unlock() return s.server[tcp].ActivateAndServe() @@ -404,6 +432,8 @@ func errorAndMetricsFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int const ( tcp = 0 udp = 1 + + tcpMaxQueries = -1 ) type ( diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go index eda39c140..cddf59890 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go @@ -75,9 +75,9 @@ func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { } srv := &http.Server{ - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 120 * time.Second, + ReadTimeout: s.readTimeout, + WriteTimeout: s.writeTimeout, + IdleTimeout: s.idleTimeout, ErrorLog: stdlog.New(&loggerAdapter{}, "", 0), } sh := &ServerHTTPS{ diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_quic.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_quic.go new file mode 100644 index 000000000..ba7867cfb --- /dev/null +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_quic.go @@ -0,0 +1,346 @@ +package dnsserver + +import ( + "context" + "crypto/tls" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "net" + + "github.com/coredns/coredns/plugin/metrics/vars" + clog "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/reuseport" + "github.com/coredns/coredns/plugin/pkg/transport" + + "github.com/miekg/dns" + "github.com/quic-go/quic-go" +) + +const ( + // DoQCodeNoError is used when the connection or stream needs to be + // closed, but there is no error to signal. + DoQCodeNoError quic.ApplicationErrorCode = 0 + + // DoQCodeInternalError signals that the DoQ implementation encountered + // an internal error and is incapable of pursuing the transaction or the + // connection. + DoQCodeInternalError quic.ApplicationErrorCode = 1 + + // DoQCodeProtocolError signals that the DoQ implementation encountered + // a protocol error and is forcibly aborting the connection. + DoQCodeProtocolError quic.ApplicationErrorCode = 2 +) + +// ServerQUIC represents an instance of a DNS-over-QUIC server. +type ServerQUIC struct { + *Server + listenAddr net.Addr + tlsConfig *tls.Config + quicConfig *quic.Config + quicListener *quic.Listener +} + +// NewServerQUIC returns a new CoreDNS QUIC server and compiles all plugin in to it. +func NewServerQUIC(addr string, group []*Config) (*ServerQUIC, error) { + s, err := NewServer(addr, group) + if err != nil { + return nil, err + } + // The *tls* plugin must make sure that multiple conflicting + // TLS configuration returns an error: it can only be specified once. + var tlsConfig *tls.Config + for _, z := range s.zones { + for _, conf := range z { + // Should we error if some configs *don't* have TLS? + tlsConfig = conf.TLSConfig + } + } + + if tlsConfig != nil { + tlsConfig.NextProtos = []string{"doq"} + } + + var quicConfig *quic.Config + quicConfig = &quic.Config{ + MaxIdleTimeout: s.idleTimeout, + MaxIncomingStreams: math.MaxUint16, + MaxIncomingUniStreams: math.MaxUint16, + // Enable 0-RTT by default for all connections on the server-side. + Allow0RTT: true, + } + + return &ServerQUIC{Server: s, tlsConfig: tlsConfig, quicConfig: quicConfig}, nil +} + +// ServePacket implements caddy.UDPServer interface. +func (s *ServerQUIC) ServePacket(p net.PacketConn) error { + s.m.Lock() + s.listenAddr = s.quicListener.Addr() + s.m.Unlock() + + return s.ServeQUIC() +} + +// ServeQUIC listens for incoming QUIC packets. +func (s *ServerQUIC) ServeQUIC() error { + for { + conn, err := s.quicListener.Accept(context.Background()) + if err != nil { + if s.isExpectedErr(err) { + s.closeQUICConn(conn, DoQCodeNoError) + return err + } + + s.closeQUICConn(conn, DoQCodeInternalError) + return err + } + + go s.serveQUICConnection(conn) + } +} + +// serveQUICConnection handles a new QUIC connection. It waits for new streams +// and passes them to serveQUICStream. +func (s *ServerQUIC) serveQUICConnection(conn quic.Connection) { + for { + // In DoQ, one query consumes one stream. + // The client MUST select the next available client-initiated bidirectional + // stream for each subsequent query on a QUIC connection. + stream, err := conn.AcceptStream(context.Background()) + if err != nil { + if s.isExpectedErr(err) { + s.closeQUICConn(conn, DoQCodeNoError) + return + } + + s.closeQUICConn(conn, DoQCodeInternalError) + return + } + + go s.serveQUICStream(stream, conn) + } +} + +func (s *ServerQUIC) serveQUICStream(stream quic.Stream, conn quic.Connection) { + buf, err := readDOQMessage(stream) + + // io.EOF does not really mean that there's any error, it is just + // the STREAM FIN indicating that there will be no data to read + // anymore from this stream. + if err != nil && err != io.EOF { + s.closeQUICConn(conn, DoQCodeProtocolError) + + return + } + + req := &dns.Msg{} + err = req.Unpack(buf) + if err != nil { + clog.Debugf("unpacking quic packet: %s", err) + s.closeQUICConn(conn, DoQCodeProtocolError) + + return + } + + if !validRequest(req) { + // If a peer encounters such an error condition, it is considered a + // fatal error. It SHOULD forcibly abort the connection using QUIC's + // CONNECTION_CLOSE mechanism and SHOULD use the DoQ error code + // DOQ_PROTOCOL_ERROR. + // See https://www.rfc-editor.org/rfc/rfc9250#section-4.3.3-3 + s.closeQUICConn(conn, DoQCodeProtocolError) + + return + } + + w := &DoQWriter{ + localAddr: conn.LocalAddr(), + remoteAddr: conn.RemoteAddr(), + stream: stream, + Msg: req, + } + + dnsCtx := context.WithValue(stream.Context(), Key{}, s.Server) + dnsCtx = context.WithValue(dnsCtx, LoopKey{}, 0) + s.ServeDNS(dnsCtx, w, req) + s.countResponse(DoQCodeNoError) +} + +// ListenPacket implements caddy.UDPServer interface. +func (s *ServerQUIC) ListenPacket() (net.PacketConn, error) { + p, err := reuseport.ListenPacket("udp", s.Addr[len(transport.QUIC+"://"):]) + if err != nil { + return nil, err + } + + s.m.Lock() + defer s.m.Unlock() + + s.quicListener, err = quic.Listen(p, s.tlsConfig, s.quicConfig) + if err != nil { + return nil, err + } + + return p, nil +} + +// OnStartupComplete lists the sites served by this server +// and any relevant information, assuming Quiet is false. +func (s *ServerQUIC) OnStartupComplete() { + if Quiet { + return + } + + out := startUpZones(transport.QUIC+"://", s.Addr, s.zones) + if out != "" { + fmt.Print(out) + } +} + +// Stop stops the server non-gracefully. It blocks until the server is totally stopped. +func (s *ServerQUIC) Stop() error { + s.m.Lock() + defer s.m.Unlock() + + if s.quicListener != nil { + return s.quicListener.Close() + } + + return nil +} + +// Serve implements caddy.TCPServer interface. +func (s *ServerQUIC) Serve(l net.Listener) error { return nil } + +// Listen implements caddy.TCPServer interface. +func (s *ServerQUIC) Listen() (net.Listener, error) { return nil, nil } + +// closeQUICConn quietly closes the QUIC connection. +func (s *ServerQUIC) closeQUICConn(conn quic.Connection, code quic.ApplicationErrorCode) { + if conn == nil { + return + } + + clog.Debugf("closing quic conn %s with code %d", conn.LocalAddr(), code) + err := conn.CloseWithError(code, "") + if err != nil { + clog.Debugf("failed to close quic connection with code %d: %s", code, err) + } + + // DoQCodeNoError metrics are already registered after s.ServeDNS() + if code != DoQCodeNoError { + s.countResponse(code) + } +} + +// validRequest checks for protocol errors in the unpacked DNS message. +// See https://www.rfc-editor.org/rfc/rfc9250.html#name-protocol-errors +func validRequest(req *dns.Msg) (ok bool) { + // 1. a client or server receives a message with a non-zero Message ID. + if req.Id != 0 { + return false + } + + // 2. an implementation receives a message containing the edns-tcp-keepalive + // EDNS(0) Option [RFC7828]. + if opt := req.IsEdns0(); opt != nil { + for _, option := range opt.Option { + if option.Option() == dns.EDNS0TCPKEEPALIVE { + clog.Debug("client sent EDNS0 TCP keepalive option") + + return false + } + } + } + + // 3. the client or server does not indicate the expected STREAM FIN after + // sending requests or responses. + // + // This is quite problematic to validate this case since this would imply + // we have to wait until STREAM FIN is arrived before we start processing + // the message. So we're consciously ignoring this case in this + // implementation. + + // 4. a server receives a "replayable" transaction in 0-RTT data + // + // The information necessary to validate this is not exposed by quic-go. + + return true +} + +// readDOQMessage reads a DNS over QUIC (DOQ) message from the given stream +// and returns the message bytes. +// Drafts of the RFC9250 did not require the 2-byte prefixed message length. +// Thus, we are only supporting the official version (DoQ v1). +func readDOQMessage(r io.Reader) ([]byte, error) { + // All DNS messages (queries and responses) sent over DoQ connections MUST + // be encoded as a 2-octet length field followed by the message content as + // specified in [RFC1035]. + // See https://www.rfc-editor.org/rfc/rfc9250.html#section-4.2-4 + sizeBuf := make([]byte, 2) + _, err := io.ReadFull(r, sizeBuf) + if err != nil { + return nil, err + } + + size := binary.BigEndian.Uint16(sizeBuf) + + if size == 0 { + return nil, fmt.Errorf("message size is 0: probably unsupported DoQ version") + } + + buf := make([]byte, size) + _, err = io.ReadFull(r, buf) + + // A client or server receives a STREAM FIN before receiving all the bytes + // for a message indicated in the 2-octet length field. + // See https://www.rfc-editor.org/rfc/rfc9250#section-4.3.3-2.2 + if size != uint16(len(buf)) { + return nil, fmt.Errorf("message size does not match 2-byte prefix") + } + + return buf, err +} + +// isExpectedErr returns true if err is an expected error, likely related to +// the current implementation. +func (s *ServerQUIC) isExpectedErr(err error) bool { + if err == nil { + return false + } + + // This error is returned when the QUIC listener was closed by us. As + // graceful shutdown is not implemented, the connection will be abruptly + // closed but there is no error to signal. + if errors.Is(err, quic.ErrServerClosed) { + return true + } + + // This error happens when the connection was closed due to a DoQ + // protocol error but there's still something to read in the closed stream. + // For example, when the message was sent without the prefixed length. + var qAppErr *quic.ApplicationError + if errors.As(err, &qAppErr) && qAppErr.ErrorCode == 2 { + return true + } + + // When a connection hits the idle timeout, quic.AcceptStream() returns + // an IdleTimeoutError. In this, case, we should just drop the connection + // with DoQCodeNoError. + var qIdleErr *quic.IdleTimeoutError + return errors.As(err, &qIdleErr) +} + +func (s *ServerQUIC) countResponse(code quic.ApplicationErrorCode) { + switch code { + case DoQCodeNoError: + vars.QUICResponsesCount.WithLabelValues(s.Addr, "0x0").Inc() + case DoQCodeInternalError: + vars.QUICResponsesCount.WithLabelValues(s.Addr, "0x1").Inc() + case DoQCodeProtocolError: + vars.QUICResponsesCount.WithLabelValues(s.Addr, "0x2").Inc() + } +} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go index 6fff61d5c..f2251efb4 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "fmt" "net" + "time" "github.com/coredns/caddy" "github.com/coredns/coredns/plugin/pkg/reuseport" @@ -50,11 +51,20 @@ func (s *ServerTLS) Serve(l net.Listener) error { } // Only fill out the TCP server for this one. - s.server[tcp] = &dns.Server{Listener: l, Net: "tcp-tls", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { - ctx := context.WithValue(context.Background(), Key{}, s.Server) - ctx = context.WithValue(ctx, LoopKey{}, 0) - s.ServeDNS(ctx, w, r) - })} + s.server[tcp] = &dns.Server{Listener: l, + Net: "tcp-tls", + MaxTCPQueries: tlsMaxQueries, + ReadTimeout: s.readTimeout, + WriteTimeout: s.writeTimeout, + IdleTimeout: func() time.Duration { + return s.idleTimeout + }, + Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { + ctx := context.WithValue(context.Background(), Key{}, s.Server) + ctx = context.WithValue(ctx, LoopKey{}, 0) + s.ServeDNS(ctx, w, r) + })} + s.m.Unlock() return s.server[tcp].ActivateAndServe() @@ -87,3 +97,7 @@ func (s *ServerTLS) OnStartupComplete() { fmt.Print(out) } } + +const ( + tlsMaxQueries = -1 +) diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go index 38425fb06..83743ac21 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go @@ -10,14 +10,15 @@ package dnsserver // (after) them during a request, but they must not // care what plugin above them are doing. var Directives = []string{ + "root", "metadata", "geoip", "cancel", "tls", + "timeouts", "reload", "nsid", "bufsize", - "root", "bind", "debug", "trace", diff --git a/vendor/github.com/coredns/coredns/coremain/run.go b/vendor/github.com/coredns/coredns/coremain/run.go index fa7657886..31b791c8e 100644 --- a/vendor/github.com/coredns/coredns/coremain/run.go +++ b/vendor/github.com/coredns/coredns/coremain/run.go @@ -28,6 +28,9 @@ func init() { caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader)) caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader)) + flag.StringVar(&dnsserver.Port, serverType+".port", dnsserver.DefaultPort, "Default port") + flag.StringVar(&dnsserver.Port, "p", dnsserver.DefaultPort, "Default port") + caddy.AppName = coreName caddy.AppVersion = CoreVersion } @@ -42,7 +45,7 @@ func Run() { } log.SetOutput(os.Stdout) - log.SetFlags(0) // Set to 0 because we're doing our own time, with timezone + log.SetFlags(LogFlags) if version { showVersion() @@ -166,10 +169,14 @@ var ( conf string version bool plugins bool + + // LogFlags are initially set to 0 for no extra output + LogFlags int ) // Build information obtained with the help of -ldflags var ( + // nolint appVersion = "(untracked dev build)" // inferred at startup devBuild = true // inferred at startup diff --git a/vendor/github.com/coredns/coredns/coremain/version.go b/vendor/github.com/coredns/coredns/coremain/version.go index 7578079c9..232941fba 100644 --- a/vendor/github.com/coredns/coredns/coremain/version.go +++ b/vendor/github.com/coredns/coredns/coremain/version.go @@ -2,7 +2,7 @@ package coremain // Various CoreDNS constants. const ( - CoreVersion = "1.10.0" + CoreVersion = "1.11.3" coreName = "CoreDNS" serverType = "dns" ) diff --git a/vendor/github.com/coredns/coredns/plugin/bind/README.md b/vendor/github.com/coredns/coredns/plugin/bind/README.md index a911218f6..1c0f0c5f9 100644 --- a/vendor/github.com/coredns/coredns/plugin/bind/README.md +++ b/vendor/github.com/coredns/coredns/plugin/bind/README.md @@ -13,7 +13,7 @@ If several addresses are provided, a listener will be open on each of the IP pro Each address has to be an IP or name of one of the interfaces of the host. Bind by interface name, binds to the IPs on that interface at the time of startup or reload (reload will happen with a SIGHUP or if the config file changes). -If the given argument is an interface name, and that interface has serveral IP addresses, CoreDNS will listen on all of the interface IP addresses (including IPv4 and IPv6), except for IPv6 link-local addresses on that interface. +If the given argument is an interface name, and that interface has several IP addresses, CoreDNS will listen on all of the interface IP addresses (including IPv4 and IPv6), except for IPv6 link-local addresses on that interface. ## Syntax diff --git a/vendor/github.com/coredns/coredns/plugin/bind/setup.go b/vendor/github.com/coredns/coredns/plugin/bind/setup.go index 10fe4a955..1bd397585 100644 --- a/vendor/github.com/coredns/coredns/plugin/bind/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/bind/setup.go @@ -9,6 +9,8 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/log" + + "k8s.io/utils/strings/slices" ) func setup(c *caddy.Controller) error { @@ -37,7 +39,7 @@ func setup(c *caddy.Controller) error { } for _, ip := range ips { - if !isIn(ip, except) { + if !slices.Contains(except, ip) { all = append(all, ip) } } @@ -98,14 +100,3 @@ func listIP(args []string, ifaces []net.Interface) ([]string, error) { } return all, nil } - -// isIn checks if a string array contains an element -func isIn(s string, list []string) bool { - is := false - for _, l := range list { - if s == l { - is = true - } - } - return is -} diff --git a/vendor/github.com/coredns/coredns/plugin/bufsize/README.md b/vendor/github.com/coredns/coredns/plugin/bufsize/README.md index 56a9dddfc..0dc96235c 100644 --- a/vendor/github.com/coredns/coredns/plugin/bufsize/README.md +++ b/vendor/github.com/coredns/coredns/plugin/bufsize/README.md @@ -1,11 +1,15 @@ # bufsize ## Name -*bufsize* - sizes EDNS0 buffer size to prevent IP fragmentation. +*bufsize* - limits EDNS0 buffer size to prevent IP fragmentation. ## Description -*bufsize* limits a requester's UDP payload size. +*bufsize* limits a requester's UDP payload size to within a maximum value. +If a request with an OPT RR has a bufsize greater than the limit, the bufsize +of the request will be reduced. Otherwise the request is unaffected. It prevents IP fragmentation, mitigating certain DNS vulnerabilities. -This will only affect queries that have an OPT RR. +It cannot increase UDP size requested by the client, it can be reduced only. +This will only affect queries that have +an OPT RR ([EDNS(0)](https://www.rfc-editor.org/rfc/rfc6891)). ## Syntax ```txt @@ -13,14 +17,14 @@ bufsize [SIZE] ``` **[SIZE]** is an int value for setting the buffer size. -The default value is 512, and the value must be within 512 - 4096. +The default value is 1232, and the value must be within 512 - 4096. Only one argument is acceptable, and it covers both IPv4 and IPv6. ## Examples Enable limiting the buffer size of outgoing query to the resolver (172.31.0.10): ```corefile . { - bufsize 512 + bufsize 1100 forward . 172.31.0.10 log } @@ -29,7 +33,7 @@ Enable limiting the buffer size of outgoing query to the resolver (172.31.0.10): Enable limiting the buffer size as an authoritative nameserver: ```corefile . { - bufsize 512 + bufsize 1220 file db.example.org log } diff --git a/vendor/github.com/coredns/coredns/plugin/bufsize/setup.go b/vendor/github.com/coredns/coredns/plugin/bufsize/setup.go index 7ac602d5d..56113e633 100644 --- a/vendor/github.com/coredns/coredns/plugin/bufsize/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/bufsize/setup.go @@ -24,12 +24,13 @@ func setup(c *caddy.Controller) error { } func parse(c *caddy.Controller) (int, error) { - const defaultBufSize = 512 + // value from http://www.dnsflagday.net/2020/ + const defaultBufSize = 1232 for c.Next() { args := c.RemainingArgs() switch len(args) { case 0: - // Nothing specified; use 512 as default + // Nothing specified; use defaultBufSize return defaultBufSize, nil case 1: // Specified value is needed to verify diff --git a/vendor/github.com/coredns/coredns/plugin/cache/README.md b/vendor/github.com/coredns/coredns/plugin/cache/README.md index 562f5bd9a..d516a91db 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/README.md +++ b/vendor/github.com/coredns/coredns/plugin/cache/README.md @@ -10,8 +10,7 @@ With *cache* enabled, all records except zone transfers and metadata records wil 3600s. Caching is mostly useful in a scenario when fetching data from the backend (upstream, database, etc.) is expensive. -*Cache* will change the query to enable DNSSEC (DNSSEC OK; DO) if it passes through the plugin. If -the client didn't request any DNSSEC (records), these are filtered out when replying. +*Cache* will pass DNSSEC (DNSSEC OK; DO) options through the plugin for upstream queries. This plugin can only be used once per Server Block. @@ -40,6 +39,7 @@ cache [TTL] [ZONES...] { serve_stale [DURATION] [REFRESH_MODE] servfail DURATION disable success|denial [ZONES...] + keepttl } ~~~ @@ -70,6 +70,11 @@ cache [TTL] [ZONES...] { greater than 5 minutes. * `disable` disable the success or denial cache for the listed **ZONES**. If no **ZONES** are given, the specified cache will be disabled for all zones. +* `keepttl` do not age TTL when serving responses from cache. The entry will still be removed from cache + when the TTL expires as normal, but until it expires responses will include the original TTL instead + of the remaining TTL. This can be useful if CoreDNS is used as an authoritative server and you want + to serve a consistent TTL to downstream clients. This is **NOT** recommended when CoreDNS is caching + records it is not authoritative for because it could result in downstream clients using stale answers. ## Capacity and Eviction @@ -136,4 +141,4 @@ example.org { disable denial sub.example.org } } -~~~ \ No newline at end of file +~~~ diff --git a/vendor/github.com/coredns/coredns/plugin/cache/cache.go b/vendor/github.com/coredns/coredns/plugin/cache/cache.go index b4767937d..1378263b8 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/cache.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/cache.go @@ -48,6 +48,9 @@ type Cache struct { pexcept []string nexcept []string + // Keep ttl option + keepttl bool + // Testing. now func() time.Time } @@ -76,7 +79,7 @@ func New() *Cache { // key returns key under which we store the item, -1 will be returned if we don't store the message. // Currently we do not cache Truncated, errors zone transfers or dynamic update messages. // qname holds the already lowercased qname. -func key(qname string, m *dns.Msg, t response.Type) (bool, uint64) { +func key(qname string, m *dns.Msg, t response.Type, do, cd bool) (bool, uint64) { // We don't store truncated responses. if m.Truncated { return false, 0 @@ -86,11 +89,27 @@ func key(qname string, m *dns.Msg, t response.Type) (bool, uint64) { return false, 0 } - return true, hash(qname, m.Question[0].Qtype) + return true, hash(qname, m.Question[0].Qtype, do, cd) } -func hash(qname string, qtype uint16) uint64 { +var one = []byte("1") +var zero = []byte("0") + +func hash(qname string, qtype uint16, do, cd bool) uint64 { h := fnv.New64() + + if do { + h.Write(one) + } else { + h.Write(zero) + } + + if cd { + h.Write(one) + } else { + h.Write(zero) + } + h.Write([]byte{byte(qtype >> 8)}) h.Write([]byte{byte(qtype)}) h.Write([]byte(qname)) @@ -116,6 +135,7 @@ type ResponseWriter struct { server string // Server handling the request. do bool // When true the original request had the DO bit set. + cd bool // When true the original request had the CD bit set. ad bool // When true the original request had the AD bit set. prefetch bool // When true write nothing back to the client. remoteAddr net.Addr @@ -145,6 +165,8 @@ func newPrefetchResponseWriter(server string, state request.Request, c *Cache) * Cache: c, state: state, server: server, + do: state.Do(), + cd: state.Req.CheckingDisabled, prefetch: true, remoteAddr: addr, } @@ -163,7 +185,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { mt, _ := response.Typify(res, w.now().UTC()) // key returns empty string for anything we don't want to cache. - hasKey, key := key(w.state.Name(), res, mt) + hasKey, key := key(w.state.Name(), res, mt, w.do, w.cd) msgTTL := dnsutil.MinimalTTL(res, mt) var duration time.Duration @@ -191,11 +213,10 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { } // Apply capped TTL to this reply to avoid jarring TTL experience 1799 -> 8 (e.g.) - // We also may need to filter out DNSSEC records, see toMsg() for similar code. ttl := uint32(duration.Seconds()) - res.Answer = filterRRSlice(res.Answer, ttl, w.do, false) - res.Ns = filterRRSlice(res.Ns, ttl, w.do, false) - res.Extra = filterRRSlice(res.Extra, ttl, w.do, false) + res.Answer = filterRRSlice(res.Answer, ttl, false) + res.Ns = filterRRSlice(res.Ns, ttl, false) + res.Extra = filterRRSlice(res.Extra, ttl, false) if !w.do && !w.ad { // unset AD bit if requester is not OK with DNSSEC diff --git a/vendor/github.com/coredns/coredns/plugin/cache/dnssec.go b/vendor/github.com/coredns/coredns/plugin/cache/dnssec.go index cf908037e..ec5ff41cb 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/dnssec.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/dnssec.go @@ -2,35 +2,13 @@ package cache import "github.com/miekg/dns" -// isDNSSEC returns true if r is a DNSSEC record. NSEC,NSEC3,DS and RRSIG/SIG -// are DNSSEC records. DNSKEYs is not in this list on the assumption that the -// client explicitly asked for it. -func isDNSSEC(r dns.RR) bool { - switch r.Header().Rrtype { - case dns.TypeNSEC: - return true - case dns.TypeNSEC3: - return true - case dns.TypeDS: - return true - case dns.TypeRRSIG: - return true - case dns.TypeSIG: - return true - } - return false -} - -// filterRRSlice filters rrs and removes DNSSEC RRs when do is false. In the returned slice -// the TTLs are set to ttl. If dup is true the RRs in rrs are _copied_ into the slice that is +// filterRRSlice filters out OPT RRs, and sets all RR TTLs to ttl. +// If dup is true the RRs in rrs are _copied_ into the slice that is // returned. -func filterRRSlice(rrs []dns.RR, ttl uint32, do, dup bool) []dns.RR { +func filterRRSlice(rrs []dns.RR, ttl uint32, dup bool) []dns.RR { j := 0 rs := make([]dns.RR, len(rrs)) for _, r := range rrs { - if !do && isDNSSEC(r) { - continue - } if r.Header().Rrtype == dns.TypeOPT { continue } diff --git a/vendor/github.com/coredns/coredns/plugin/cache/handler.go b/vendor/github.com/coredns/coredns/plugin/cache/handler.go index ec2135e8c..38a8bfebc 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/handler.go @@ -18,6 +18,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) rc := r.Copy() // We potentially modify r, to prevent other plugins from seeing this (r is a pointer), copy r into rc. state := request.Request{W: w, Req: rc} do := state.Do() + cd := r.CheckingDisabled ad := r.AuthenticatedData zone := plugin.Zones(c.Zones).Matches(state.Name()) @@ -28,17 +29,15 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) now := c.now().UTC() server := metrics.WithServer(ctx) - // On cache miss, if the request has the OPT record and the DO bit set we leave the message as-is. If there isn't a DO bit - // set we will modify the request to _add_ one. This means we will always do DNSSEC lookups on cache misses. - // When writing to cache, any DNSSEC RRs in the response are written to cache with the response. - // When sending a response to a non-DNSSEC client, we remove DNSSEC RRs from the response. We use a 2048 buffer size, which is - // less than 4096 (and older default) and more than 1024 which may be too small. We might need to tweaks this - // value to be smaller still to prevent UDP fragmentation? + // On cache refresh, we will just use the DO bit from the incoming query for the refresh since we key our cache + // with the query DO bit. That means two separate cache items for the query DO bit true or false. In the situation + // in which upstream doesn't support DNSSEC, the two cache items will effectively be the same. Regardless, any + // DNSSEC RRs in the response are written to cache with the response. ttl := 0 i := c.getIgnoreTTL(now, state, server) if i == nil { - crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad, + crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad, cd: cd, nexcept: c.nexcept, pexcept: c.pexcept, wildcardFunc: wildcardFunc(ctx)} return c.doRefresh(ctx, state, crr) } @@ -46,7 +45,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) if ttl < 0 { // serve stale behavior if c.verifyStale { - crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do} + crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, cd: cd} cw := newVerifyStaleResponseWriter(crr) ret, err := c.doRefresh(ctx, state, cw) if cw.refreshed { @@ -73,6 +72,11 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) }) } + if c.keepttl { + // If keepttl is enabled we fake the current time to the stored + // one so that we always get the original TTL + now = i.stored + } resp := i.toMsg(r, now, do, ad) w.WriteMsg(resp) return dns.RcodeSuccess, nil @@ -101,9 +105,6 @@ func (c *Cache) doPrefetch(ctx context.Context, state request.Request, cw *Respo } func (c *Cache) doRefresh(ctx context.Context, state request.Request, cw dns.ResponseWriter) (int, error) { - if !state.Do() { - setDo(state.Req) - } return plugin.NextOrFailure(c.Name(), c.Next, ctx, cw, state.Req) } @@ -121,7 +122,7 @@ func (c *Cache) Name() string { return "cache" } // getIgnoreTTL unconditionally returns an item if it exists in the cache. func (c *Cache) getIgnoreTTL(now time.Time, state request.Request, server string) *item { - k := hash(state.Name(), state.QType()) + k := hash(state.Name(), state.QType(), state.Do(), state.Req.CheckingDisabled) cacheRequests.WithLabelValues(server, c.zonesMetricLabel, c.viewMetricLabel).Inc() if i, ok := c.ncache.Get(k); ok { @@ -145,7 +146,7 @@ func (c *Cache) getIgnoreTTL(now time.Time, state request.Request, server string } func (c *Cache) exists(state request.Request) *item { - k := hash(state.Name(), state.QType()) + k := hash(state.Name(), state.QType(), state.Do(), state.Req.CheckingDisabled) if i, ok := c.ncache.Get(k); ok { return i.(*item) } @@ -154,22 +155,3 @@ func (c *Cache) exists(state request.Request) *item { } return nil } - -// setDo sets the DO bit and UDP buffer size in the message m. -func setDo(m *dns.Msg) { - o := m.IsEdns0() - if o != nil { - o.SetDo() - o.SetUDPSize(defaultUDPBufSize) - return - } - - o = &dns.OPT{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT}} - o.SetDo() - o.SetUDPSize(defaultUDPBufSize) - m.Extra = append(m.Extra, o) -} - -// defaultUDPBufsize is the bufsize the cache plugin uses on outgoing requests that don't -// have an OPT RR. -const defaultUDPBufSize = 2048 diff --git a/vendor/github.com/coredns/coredns/plugin/cache/item.go b/vendor/github.com/coredns/coredns/plugin/cache/item.go index 6b51a5bad..c5aeccdcc 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/item.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/item.go @@ -87,9 +87,9 @@ func (i *item) toMsg(m *dns.Msg, now time.Time, do bool, ad bool) *dns.Msg { m1.Extra = make([]dns.RR, len(i.Extra)) ttl := uint32(i.ttl(now)) - m1.Answer = filterRRSlice(i.Answer, ttl, do, true) - m1.Ns = filterRRSlice(i.Ns, ttl, do, true) - m1.Extra = filterRRSlice(i.Extra, ttl, do, true) + m1.Answer = filterRRSlice(i.Answer, ttl, true) + m1.Ns = filterRRSlice(i.Ns, ttl, true) + m1.Extra = filterRRSlice(i.Extra, ttl, true) return m1 } diff --git a/vendor/github.com/coredns/coredns/plugin/cache/setup.go b/vendor/github.com/coredns/coredns/plugin/cache/setup.go index 6a537d986..f8278b872 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/setup.go @@ -240,6 +240,12 @@ func cacheParse(c *caddy.Controller) (*Cache, error) { default: return nil, fmt.Errorf("cache type for disable must be %q or %q", Success, Denial) } + case "keepttl": + args := c.RemainingArgs() + if len(args) != 0 { + return nil, c.ArgErr() + } + ca.keepttl = true default: return nil, c.ArgErr() } diff --git a/vendor/github.com/coredns/coredns/plugin/dns64/dns64.go b/vendor/github.com/coredns/coredns/plugin/dns64/dns64.go index 9f426eb87..0a6176fcf 100644 --- a/vendor/github.com/coredns/coredns/plugin/dns64/dns64.go +++ b/vendor/github.com/coredns/coredns/plugin/dns64/dns64.go @@ -36,7 +36,7 @@ type DNS64 struct { func (d *DNS64) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { // Don't proxy if we don't need to. if !d.requestShouldIntercept(&request.Request{W: w, Req: r}) { - return d.Next.ServeDNS(ctx, w, r) + return plugin.NextOrFailure(d.Name(), d.Next, ctx, w, r) } // Pass the request to the next plugin in the chain, but intercept the response. diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/README.md b/vendor/github.com/coredns/coredns/plugin/dnstap/README.md index 8e5615772..b90c45fc7 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/README.md +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/README.md @@ -18,6 +18,8 @@ Every message is sent to the socket as soon as it comes in, the *dnstap* plugin dnstap SOCKET [full] { [identity IDENTITY] [version VERSION] + [extra EXTRA] + [skipverify] } ~~~ @@ -25,6 +27,8 @@ dnstap SOCKET [full] { * `full` to include the wire-format DNS message. * **IDENTITY** to override the identity of the server. Defaults to the hostname. * **VERSION** to override the version field. Defaults to the CoreDNS version. +* **EXTRA** to define "extra" field in dnstap payload, [metadata](../metadata/) replacement available here. +* `skipverify` to skip tls verification during connection. Default to be secure ## Examples @@ -61,6 +65,33 @@ dnstap /tmp/dnstap.sock { } ~~~ +Log to a socket, customize the "extra" field in dnstap payload. You may use metadata provided by other plugins in the extra field. + +~~~ txt +forward . 8.8.8.8 +metadata +dnstap /tmp/dnstap.sock { + extra "upstream: {/forward/upstream}" +} +~~~ + +Log to a remote TLS endpoint. + +~~~ txt +dnstap tls://127.0.0.1:6000 full { + skipverify +} +~~~ + +You can use _dnstap_ more than once to define multiple taps. The following logs information including the +wire-format DNS message about client requests and responses to */tmp/dnstap.sock*, +and also sends client requests and responses without wire-format DNS messages to a remote FQDN. + +~~~ txt +dnstap /tmp/dnstap.sock full +dnstap tcp://example.com:6000 +~~~ + ## Command Line Tool Dnstap has a command line tool that can be used to inspect the logging. The tool can be found @@ -86,13 +117,15 @@ $ dnstap -l 127.0.0.1:6000 ## Using Dnstap in your plugin -In your setup function, check to see if the *dnstap* plugin is loaded: +In your setup function, collect and store a list of all *dnstap* plugins loaded in the config: ~~~ go +x := &ExamplePlugin{} + c.OnStartup(func() error { if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { - if tapPlugin, ok := taph.(dnstap.Dnstap); ok { - f.tapPlugin = &tapPlugin + for tapPlugin, ok := taph.(*dnstap.Dnstap); ok; tapPlugin, ok = tapPlugin.Next.(*dnstap.Dnstap) { + x.tapPlugins = append(x.tapPlugins, tapPlugin) } } return nil @@ -102,8 +135,15 @@ c.OnStartup(func() error { And then in your plugin: ~~~ go -func (x RandomPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - if tapPlugin != nil { +import ( + "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang-dnstap" +) + +func (x ExamplePlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + for _, tapPlugin := range x.tapPlugins { q := new(msg.Msg) msg.SetQueryTime(q, time.Now()) msg.SetQueryAddress(q, w.RemoteAddr()) @@ -112,7 +152,12 @@ func (x RandomPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns q.QueryMessage = buf } msg.SetType(q, tap.Message_CLIENT_QUERY) + + // if no metadata interpretation is needed, just send the message tapPlugin.TapMessage(q) + + // OR: to interpret the metadata in "extra" field, give more context info + tapPlugin.TapMessageWithMetadata(ctx, q, request.Request{W: w, Req: query}) } // ... } diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go b/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go index a4b1acff0..1d5bd98e5 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go @@ -6,6 +6,8 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/plugin/pkg/replacer" + "github.com/coredns/coredns/request" tap "github.com/dnstap/golang-dnstap" "github.com/miekg/dns" @@ -15,20 +17,40 @@ import ( type Dnstap struct { Next plugin.Handler io tapper + repl replacer.Replacer // IncludeRawMessage will include the raw DNS message into the dnstap messages if true. IncludeRawMessage bool Identity []byte Version []byte + ExtraFormat string } -// TapMessage sends the message m to the dnstap interface. -func (h Dnstap) TapMessage(m *tap.Message) { +// TapMessage sends the message m to the dnstap interface, without populating "Extra" field. +func (h *Dnstap) TapMessage(m *tap.Message) { + if h.ExtraFormat == "" { + h.tapWithExtra(m, nil) + } else { + h.tapWithExtra(m, []byte(h.ExtraFormat)) + } +} + +// TapMessageWithMetadata sends the message m to the dnstap interface, with "Extra" field being populated. +func (h *Dnstap) TapMessageWithMetadata(ctx context.Context, m *tap.Message, state request.Request) { + if h.ExtraFormat == "" { + h.tapWithExtra(m, nil) + return + } + extraStr := h.repl.Replace(ctx, state, nil, h.ExtraFormat) + h.tapWithExtra(m, []byte(extraStr)) +} + +func (h *Dnstap) tapWithExtra(m *tap.Message, extra []byte) { t := tap.Dnstap_MESSAGE - h.io.Dnstap(&tap.Dnstap{Type: &t, Message: m, Identity: h.Identity, Version: h.Version}) + h.io.Dnstap(&tap.Dnstap{Type: &t, Message: m, Identity: h.Identity, Version: h.Version, Extra: extra}) } -func (h Dnstap) tapQuery(w dns.ResponseWriter, query *dns.Msg, queryTime time.Time) { +func (h *Dnstap) tapQuery(ctx context.Context, w dns.ResponseWriter, query *dns.Msg, queryTime time.Time) { q := new(tap.Message) msg.SetQueryTime(q, queryTime) msg.SetQueryAddress(q, w.RemoteAddr()) @@ -38,24 +60,26 @@ func (h Dnstap) tapQuery(w dns.ResponseWriter, query *dns.Msg, queryTime time.Ti q.QueryMessage = buf } msg.SetType(q, tap.Message_CLIENT_QUERY) - h.TapMessage(q) + state := request.Request{W: w, Req: query} + h.TapMessageWithMetadata(ctx, q, state) } // ServeDNS logs the client query and response to dnstap and passes the dnstap Context. -func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { +func (h *Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { rw := &ResponseWriter{ ResponseWriter: w, Dnstap: h, query: r, + ctx: ctx, queryTime: time.Now(), } // The query tap message should be sent before sending the query to the // forwarder. Otherwise, the tap messages will come out out of order. - h.tapQuery(w, r, rw.queryTime) + h.tapQuery(ctx, w, r, rw.queryTime) return plugin.NextOrFailure(h.Name(), h.Next, ctx, rw, r) } // Name implements the plugin.Plugin interface. -func (h Dnstap) Name() string { return "dnstap" } +func (h *Dnstap) Name() string { return "dnstap" } diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/io.go b/vendor/github.com/coredns/coredns/plugin/dnstap/io.go index d15d5669b..f95e4b5e8 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/io.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/io.go @@ -1,6 +1,7 @@ package dnstap import ( + "crypto/tls" "net" "sync/atomic" "time" @@ -14,6 +15,8 @@ const ( tcpTimeout = 4 * time.Second flushTimeout = 1 * time.Second + + skipVerify = false // by default, every tls connection is verified to be secure ) // tapper interface is used in testing to mock the Dnstap method. @@ -31,6 +34,7 @@ type dio struct { quit chan struct{} flushTimeout time.Duration tcpTimeout time.Duration + skipVerify bool } // newIO returns a new and initialized pointer to a dio. @@ -42,14 +46,32 @@ func newIO(proto, endpoint string) *dio { quit: make(chan struct{}), flushTimeout: flushTimeout, tcpTimeout: tcpTimeout, + skipVerify: skipVerify, } } func (d *dio) dial() error { - conn, err := net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout) - if err != nil { - return err + var conn net.Conn + var err error + + if d.proto == "tls" { + config := &tls.Config{ + InsecureSkipVerify: d.skipVerify, + } + dialer := &net.Dialer{ + Timeout: d.tcpTimeout, + } + conn, err = tls.DialWithDialer(dialer, "tcp", d.endpoint, config) + if err != nil { + return err + } + } else { + conn, err = net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout) + if err != nil { + return err + } } + if tcpConn, ok := conn.(*net.TCPConn); ok { tcpConn.SetWriteBuffer(tcpWriteBufSize) tcpConn.SetNoDelay(false) diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go b/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go index d7d1cdc1b..0186f4d51 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go @@ -9,95 +9,132 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" clog "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/replacer" ) var log = clog.NewWithPlugin("dnstap") func init() { plugin.Register("dnstap", setup) } -func parseConfig(c *caddy.Controller) (Dnstap, error) { - c.Next() // directive name - d := Dnstap{} - endpoint := "" +func parseConfig(c *caddy.Controller) ([]*Dnstap, error) { + dnstaps := []*Dnstap{} - args := c.RemainingArgs() + for c.Next() { // directive name + d := Dnstap{} + endpoint := "" + d.repl = replacer.New() - if len(args) == 0 { - return d, c.ArgErr() - } + args := c.RemainingArgs() + + if len(args) == 0 { + return nil, c.ArgErr() + } - endpoint = args[0] + endpoint = args[0] - if strings.HasPrefix(endpoint, "tcp://") { - // remote network endpoint - endpointURL, err := url.Parse(endpoint) - if err != nil { - return d, c.ArgErr() + var dio *dio + if strings.HasPrefix(endpoint, "tls://") { + // remote network endpoint + endpointURL, err := url.Parse(endpoint) + if err != nil { + return nil, c.ArgErr() + } + dio = newIO("tls", endpointURL.Host) + d = Dnstap{io: dio} + } else if strings.HasPrefix(endpoint, "tcp://") { + // remote network endpoint + endpointURL, err := url.Parse(endpoint) + if err != nil { + return nil, c.ArgErr() + } + dio = newIO("tcp", endpointURL.Host) + d = Dnstap{io: dio} + } else { + endpoint = strings.TrimPrefix(endpoint, "unix://") + dio = newIO("unix", endpoint) + d = Dnstap{io: dio} } - dio := newIO("tcp", endpointURL.Host) - d = Dnstap{io: dio} - } else { - endpoint = strings.TrimPrefix(endpoint, "unix://") - dio := newIO("unix", endpoint) - d = Dnstap{io: dio} - } - d.IncludeRawMessage = len(args) == 2 && args[1] == "full" + d.IncludeRawMessage = len(args) == 2 && args[1] == "full" - hostname, _ := os.Hostname() - d.Identity = []byte(hostname) - d.Version = []byte(caddy.AppName + "-" + caddy.AppVersion) + hostname, _ := os.Hostname() + d.Identity = []byte(hostname) + d.Version = []byte(caddy.AppName + "-" + caddy.AppVersion) - for c.NextBlock() { - switch c.Val() { - case "identity": - { - if !c.NextArg() { - return d, c.ArgErr() + for c.NextBlock() { + switch c.Val() { + case "skipverify": + { + dio.skipVerify = true } - d.Identity = []byte(c.Val()) - } - case "version": - { - if !c.NextArg() { - return d, c.ArgErr() + case "identity": + { + if !c.NextArg() { + return nil, c.ArgErr() + } + d.Identity = []byte(c.Val()) + } + case "version": + { + if !c.NextArg() { + return nil, c.ArgErr() + } + d.Version = []byte(c.Val()) + } + case "extra": + { + if !c.NextArg() { + return nil, c.ArgErr() + } + d.ExtraFormat = c.Val() } - d.Version = []byte(c.Val()) } } + dnstaps = append(dnstaps, &d) } - - return d, nil + return dnstaps, nil } func setup(c *caddy.Controller) error { - dnstap, err := parseConfig(c) + dnstaps, err := parseConfig(c) if err != nil { return plugin.Error("dnstap", err) } - c.OnStartup(func() error { - if err := dnstap.io.(*dio).connect(); err != nil { - log.Errorf("No connection to dnstap endpoint: %s", err) - } - return nil - }) - - c.OnRestart(func() error { - dnstap.io.(*dio).close() - return nil - }) - - c.OnFinalShutdown(func() error { - dnstap.io.(*dio).close() - return nil - }) - - dnsserver.GetConfig(c).AddPlugin( - func(next plugin.Handler) plugin.Handler { - dnstap.Next = next - return dnstap + for i := range dnstaps { + dnstap := dnstaps[i] + c.OnStartup(func() error { + if err := dnstap.io.(*dio).connect(); err != nil { + log.Errorf("No connection to dnstap endpoint: %s", err) + } + return nil }) + c.OnRestart(func() error { + dnstap.io.(*dio).close() + return nil + }) + + c.OnFinalShutdown(func() error { + dnstap.io.(*dio).close() + return nil + }) + + if i == len(dnstaps)-1 { + // last dnstap plugin in block: point next to next plugin + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + dnstap.Next = next + return dnstap + }) + } else { + // not last dnstap plugin in block: point next to next dnstap + nextDnstap := dnstaps[i+1] + dnsserver.GetConfig(c).AddPlugin(func(plugin.Handler) plugin.Handler { + dnstap.Next = nextDnstap + return dnstap + }) + } + } + return nil } diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/writer.go b/vendor/github.com/coredns/coredns/plugin/dnstap/writer.go index 177263496..9ef6e620c 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/writer.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/writer.go @@ -1,9 +1,11 @@ package dnstap import ( + "context" "time" "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/request" tap "github.com/dnstap/golang-dnstap" "github.com/miekg/dns" @@ -13,8 +15,9 @@ import ( type ResponseWriter struct { queryTime time.Time query *dns.Msg + ctx context.Context dns.ResponseWriter - Dnstap + *Dnstap } // WriteMsg writes back the response to the client and THEN works on logging the request and response to dnstap. @@ -35,6 +38,7 @@ func (w *ResponseWriter) WriteMsg(resp *dns.Msg) error { } msg.SetType(r, tap.Message_CLIENT_RESPONSE) - w.TapMessage(r) + state := request.Request{W: w.ResponseWriter, Req: w.query} + w.TapMessageWithMetadata(w.ctx, r, state) return nil } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/README.md b/vendor/github.com/coredns/coredns/plugin/forward/README.md index 0088c9c7c..7dd66f768 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/README.md +++ b/vendor/github.com/coredns/coredns/plugin/forward/README.md @@ -97,7 +97,7 @@ forward FROM TO... { As an upper bound for **MAX**, consider that each concurrent query will use about 2kb of memory. Also note the TLS config is "global" for the whole forwarding proxy if you need a different -`tls-name` for different upstreams you're out of luck. +`tls_servername` for different upstreams you're out of luck. On each endpoint, the timeouts for communication are set as follows: @@ -115,20 +115,28 @@ plugin is also enabled: If monitoring is enabled (via the *prometheus* plugin) then the following metric are exported: -* `coredns_forward_requests_total{to}` - query count per upstream. -* `coredns_forward_responses_total{to}` - Counter of responses received per upstream. -* `coredns_forward_request_duration_seconds{to, rcode, type}` - duration per upstream, RCODE, type -* `coredns_forward_responses_total{to, rcode}` - count of RCODEs per upstream. -* `coredns_forward_healthcheck_failures_total{to}` - number of failed health checks per upstream. -* `coredns_forward_healthcheck_broken_total{}` - counter of when all upstreams are unhealthy, +* `coredns_forward_healthcheck_broken_total{}` - count of when all upstreams are unhealthy, and we are randomly (this always uses the `random` policy) spraying to an upstream. -* `coredns_forward_max_concurrent_rejects_total{}` - counter of the number of queries rejected because the +* `coredns_forward_max_concurrent_rejects_total{}` - count of queries rejected because the number of concurrent queries were at maximum. -* `coredns_forward_conn_cache_hits_total{to, proto}` - counter of connection cache hits per upstream and protocol. -* `coredns_forward_conn_cache_misses_total{to, proto}` - counter of connection cache misses per upstream and protocol. +* `coredns_proxy_request_duration_seconds{proxy_name="forward", to, rcode}` - histogram per upstream, RCODE +* `coredns_proxy_healthcheck_failures_total{proxy_name="forward", to, rcode}`- count of failed health checks per upstream. +* `coredns_proxy_conn_cache_hits_total{proxy_name="forward", to, proto}`- count of connection cache hits per upstream and protocol. +* `coredns_proxy_conn_cache_misses_total{proxy_name="forward", to, proto}` - count of connection cache misses per upstream and protocol. + Where `to` is one of the upstream servers (**TO** from the config), `rcode` is the returned RCODE from the upstream, `proto` is the transport protocol like `udp`, `tcp`, `tcp-tls`. +The following metrics have recently been deprecated: +* `coredns_forward_healthcheck_failures_total{to, rcode}` + * Can be replaced with `coredns_proxy_healthcheck_failures_total{proxy_name="forward", to, rcode}` +* `coredns_forward_requests_total{to}` + * Can be replaced with `sum(coredns_proxy_request_duration_seconds_count{proxy_name="forward", to})` +* `coredns_forward_responses_total{to, rcode}` + * Can be replaced with `coredns_proxy_request_duration_seconds_count{proxy_name="forward", to, rcode}` +* `coredns_forward_request_duration_seconds{to, rcode}` + * Can be replaced with `coredns_proxy_request_duration_seconds{proxy_name="forward", to, rcode}` + ## Examples Proxy all requests within `example.org.` to a nameserver running on a different port: @@ -248,13 +256,13 @@ Or when you have multiple DoT upstreams with different `tls_servername`s, you ca } .:5301 { - forward . 8.8.8.8 8.8.4.4 { + forward . tls://8.8.8.8 tls://8.8.4.4 { tls_servername dns.google } } .:5302 { - forward . 1.1.1.1 1.0.0.1 { + forward . tls://1.1.1.1 tls://1.0.0.1 { tls_servername cloudflare-dns.com } } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go b/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go index 4e06ac1ff..8195bb49d 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go @@ -1,11 +1,13 @@ package forward import ( + "context" "net" "strconv" "time" "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/coredns/coredns/request" tap "github.com/dnstap/golang-dnstap" @@ -13,10 +15,7 @@ import ( ) // toDnstap will send the forward and received message to the dnstap plugin. -func toDnstap(f *Forward, host string, state request.Request, opts options, reply *dns.Msg, start time.Time) { - // Query - q := new(tap.Message) - msg.SetQueryTime(q, start) +func toDnstap(ctx context.Context, f *Forward, host string, state request.Request, opts proxy.Options, reply *dns.Msg, start time.Time) { h, p, _ := net.SplitHostPort(host) // this is preparsed and can't err here port, _ := strconv.ParseUint(p, 10, 32) // same here ip := net.ParseIP(h) @@ -24,9 +23,9 @@ func toDnstap(f *Forward, host string, state request.Request, opts options, repl var ta net.Addr = &net.UDPAddr{IP: ip, Port: int(port)} t := state.Proto() switch { - case opts.forceTCP: + case opts.ForceTCP: t = "tcp" - case opts.preferUDP: + case opts.PreferUDP: t = "udp" } @@ -34,30 +33,34 @@ func toDnstap(f *Forward, host string, state request.Request, opts options, repl ta = &net.TCPAddr{IP: ip, Port: int(port)} } - // Forwarder dnstap messages are from the perspective of the downstream server - // (upstream is the forward server) - msg.SetQueryAddress(q, state.W.RemoteAddr()) - msg.SetResponseAddress(q, ta) + for _, t := range f.tapPlugins { + // Query + q := new(tap.Message) + msg.SetQueryTime(q, start) + // Forwarder dnstap messages are from the perspective of the downstream server + // (upstream is the forward server) + msg.SetQueryAddress(q, state.W.RemoteAddr()) + msg.SetResponseAddress(q, ta) + if t.IncludeRawMessage { + buf, _ := state.Req.Pack() + q.QueryMessage = buf + } + msg.SetType(q, tap.Message_FORWARDER_QUERY) + t.TapMessageWithMetadata(ctx, q, state) - if f.tapPlugin.IncludeRawMessage { - buf, _ := state.Req.Pack() - q.QueryMessage = buf - } - msg.SetType(q, tap.Message_FORWARDER_QUERY) - f.tapPlugin.TapMessage(q) - - // Response - if reply != nil { - r := new(tap.Message) - if f.tapPlugin.IncludeRawMessage { - buf, _ := reply.Pack() - r.ResponseMessage = buf + // Response + if reply != nil { + r := new(tap.Message) + if t.IncludeRawMessage { + buf, _ := reply.Pack() + r.ResponseMessage = buf + } + msg.SetQueryTime(r, start) + msg.SetQueryAddress(r, state.W.RemoteAddr()) + msg.SetResponseAddress(r, ta) + msg.SetResponseTime(r, time.Now()) + msg.SetType(r, tap.Message_FORWARDER_RESPONSE) + t.TapMessageWithMetadata(ctx, r, state) } - msg.SetQueryTime(r, start) - msg.SetQueryAddress(r, state.W.RemoteAddr()) - msg.SetResponseAddress(r, ta) - msg.SetResponseTime(r, time.Now()) - msg.SetType(r, tap.Message_FORWARDER_RESPONSE) - f.tapPlugin.TapMessage(r) } } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/forward.go b/vendor/github.com/coredns/coredns/plugin/forward/forward.go index 90ae1aef8..d8bbe7ab9 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/forward.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/forward.go @@ -16,6 +16,7 @@ import ( "github.com/coredns/coredns/plugin/dnstap" "github.com/coredns/coredns/plugin/metadata" clog "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -25,12 +26,17 @@ import ( var log = clog.NewWithPlugin("forward") +const ( + defaultExpire = 10 * time.Second + hcInterval = 500 * time.Millisecond +) + // Forward represents a plugin instance that can proxy requests to another (DNS) server. It has a list // of proxies each representing one upstream proxy. type Forward struct { concurrent int64 // atomic counters need to be first in struct for proper alignment - proxies []*Proxy + proxies []*proxy.Proxy p Policy hcInterval time.Duration @@ -43,27 +49,35 @@ type Forward struct { expire time.Duration maxConcurrent int64 - opts options // also here for testing + opts proxy.Options // also here for testing // ErrLimitExceeded indicates that a query was rejected because the number of concurrent queries has exceeded // the maximum allowed (maxConcurrent) ErrLimitExceeded error - tapPlugin *dnstap.Dnstap // when the dnstap plugin is loaded, we use to this to send messages out. + tapPlugins []*dnstap.Dnstap // when dnstap plugins are loaded, we use to this to send messages out. Next plugin.Handler } // New returns a new Forward. func New() *Forward { - f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(random), from: ".", hcInterval: hcInterval, opts: options{forceTCP: false, preferUDP: false, hcRecursionDesired: true, hcDomain: "."}} + f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(random), from: ".", hcInterval: hcInterval, opts: proxy.Options{ForceTCP: false, PreferUDP: false, HCRecursionDesired: true, HCDomain: "."}} return f } // SetProxy appends p to the proxy list and starts healthchecking. -func (f *Forward) SetProxy(p *Proxy) { +func (f *Forward) SetProxy(p *proxy.Proxy) { f.proxies = append(f.proxies, p) - p.start(f.hcInterval) + p.Start(f.hcInterval) +} + +// SetTapPlugin appends one or more dnstap plugins to the tap plugin list. +func (f *Forward) SetTapPlugin(tapPlugin *dnstap.Dnstap) { + f.tapPlugins = append(f.tapPlugins, tapPlugin) + if nextPlugin, ok := tapPlugin.Next.(*dnstap.Dnstap); ok { + f.SetTapPlugin(nextPlugin) + } } // Len returns the number of configured proxies. @@ -83,7 +97,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg count := atomic.AddInt64(&(f.concurrent), 1) defer atomic.AddInt64(&(f.concurrent), -1) if count > f.maxConcurrent { - MaxConcurrentRejectCount.Add(1) + maxConcurrentRejectCount.Add(1) return dns.RcodeRefused, f.ErrLimitExceeded } } @@ -96,7 +110,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg list := f.List() deadline := time.Now().Add(defaultTimeout) start := time.Now() - for time.Now().Before(deadline) { + for time.Now().Before(deadline) && ctx.Err() == nil { if i >= len(list) { // reached the end of list, reset to begin i = 0 @@ -115,17 +129,17 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg r := new(random) proxy = r.List(f.proxies)[0] - HealthcheckBrokenCount.Add(1) + healthcheckBrokenCount.Add(1) } if span != nil { child = span.Tracer().StartSpan("connect", ot.ChildOf(span.Context())) - otext.PeerAddress.Set(child, proxy.addr) + otext.PeerAddress.Set(child, proxy.Addr()) ctx = ot.ContextWithSpan(ctx, child) } metadata.SetValueFunc(ctx, "forward/upstream", func() string { - return proxy.addr + return proxy.Addr() }) var ( @@ -133,14 +147,16 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg err error ) opts := f.opts + for { ret, err = proxy.Connect(ctx, state, opts) + if err == ErrCachedClosed { // Remote side closed conn, can only happen with TCP. continue } // Retry with TCP if truncated and prefer_udp configured. - if ret != nil && ret.Truncated && !opts.forceTCP && opts.preferUDP { - opts.forceTCP = true + if ret != nil && ret.Truncated && !opts.ForceTCP && opts.PreferUDP { + opts.ForceTCP = true continue } break @@ -150,8 +166,8 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg child.Finish() } - if f.tapPlugin != nil { - toDnstap(f, proxy.addr, state, opts, ret, start) + if len(f.tapPlugins) != 0 { + toDnstap(ctx, f, proxy.Addr(), state, opts, ret, start) } upstreamErr = err @@ -211,13 +227,13 @@ func (f *Forward) isAllowedDomain(name string) bool { } // ForceTCP returns if TCP is forced to be used even when the request comes in over UDP. -func (f *Forward) ForceTCP() bool { return f.opts.forceTCP } +func (f *Forward) ForceTCP() bool { return f.opts.ForceTCP } // PreferUDP returns if UDP is preferred to be used even when the request comes in over TCP. -func (f *Forward) PreferUDP() bool { return f.opts.preferUDP } +func (f *Forward) PreferUDP() bool { return f.opts.PreferUDP } // List returns a set of proxies to be used for this client depending on the policy in f. -func (f *Forward) List() []*Proxy { return f.p.List(f.proxies) } +func (f *Forward) List() []*proxy.Proxy { return f.p.List(f.proxies) } var ( // ErrNoHealthy means no healthy proxies left. @@ -228,12 +244,16 @@ var ( ErrCachedClosed = errors.New("cached connection was closed by peer") ) -// options holds various options that can be set. -type options struct { - forceTCP bool - preferUDP bool - hcRecursionDesired bool - hcDomain string +// Options holds various Options that can be set. +type Options struct { + // ForceTCP use TCP protocol for upstream DNS request. Has precedence over PreferUDP flag + ForceTCP bool + // PreferUDP use UDP protocol for upstream DNS request. + PreferUDP bool + // HCRecursionDesired sets recursion desired flag for Proxy healthcheck requests + HCRecursionDesired bool + // HCDomain sets domain for Proxy healthcheck requests + HCDomain string } var defaultTimeout = 5 * time.Second diff --git a/vendor/github.com/coredns/coredns/plugin/forward/fuzz.go b/vendor/github.com/coredns/coredns/plugin/forward/fuzz.go index bec573e47..ba6e9155d 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/fuzz.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/fuzz.go @@ -5,6 +5,7 @@ package forward import ( "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/pkg/fuzz" + "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/miekg/dns" ) @@ -16,8 +17,8 @@ var f *Forward func init() { f = New() s := dnstest.NewServer(r{}.reflectHandler) - f.SetProxy(NewProxy(s.Addr, "tcp")) - f.SetProxy(NewProxy(s.Addr, "udp")) + f.SetProxy(proxy.NewProxy(s.Addr, "tcp")) + f.SetProxy(proxy.NewProxy(s.Addr, "udp")) } // Fuzz fuzzes forward. diff --git a/vendor/github.com/coredns/coredns/plugin/forward/metrics.go b/vendor/github.com/coredns/coredns/plugin/forward/metrics.go index f1f0c48d6..246dc6500 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/metrics.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/metrics.go @@ -9,53 +9,17 @@ import ( // Variables declared for monitoring. var ( - RequestCount = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "requests_total", - Help: "Counter of requests made per upstream.", - }, []string{"to"}) - RcodeCount = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "responses_total", - Help: "Counter of responses received per upstream.", - }, []string{"rcode", "to"}) - RequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "request_duration_seconds", - Buckets: plugin.TimeBuckets, - Help: "Histogram of the time each request took.", - }, []string{"to", "rcode"}) - HealthcheckFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "healthcheck_failures_total", - Help: "Counter of the number of failed healthchecks.", - }, []string{"to"}) - HealthcheckBrokenCount = promauto.NewCounter(prometheus.CounterOpts{ + healthcheckBrokenCount = promauto.NewCounter(prometheus.CounterOpts{ Namespace: plugin.Namespace, Subsystem: "forward", Name: "healthcheck_broken_total", Help: "Counter of the number of complete failures of the healthchecks.", }) - MaxConcurrentRejectCount = promauto.NewCounter(prometheus.CounterOpts{ + + maxConcurrentRejectCount = promauto.NewCounter(prometheus.CounterOpts{ Namespace: plugin.Namespace, Subsystem: "forward", Name: "max_concurrent_rejects_total", Help: "Counter of the number of queries rejected because the concurrent queries were at maximum.", }) - ConnCacheHitsCount = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "conn_cache_hits_total", - Help: "Counter of connection cache hits per upstream and protocol.", - }, []string{"to", "proto"}) - ConnCacheMissesCount = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "conn_cache_misses_total", - Help: "Counter of connection cache misses per upstream and protocol.", - }, []string{"to", "proto"}) ) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/policy.go b/vendor/github.com/coredns/coredns/plugin/forward/policy.go index e81e4ab91..7bd1f316a 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/policy.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/policy.go @@ -4,12 +4,13 @@ import ( "sync/atomic" "time" + "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/coredns/coredns/plugin/pkg/rand" ) // Policy defines a policy we use for selecting upstreams. type Policy interface { - List([]*Proxy) []*Proxy + List([]*proxy.Proxy) []*proxy.Proxy String() string } @@ -18,19 +19,19 @@ type random struct{} func (r *random) String() string { return "random" } -func (r *random) List(p []*Proxy) []*Proxy { +func (r *random) List(p []*proxy.Proxy) []*proxy.Proxy { switch len(p) { case 1: return p case 2: if rn.Int()%2 == 0 { - return []*Proxy{p[1], p[0]} // swap + return []*proxy.Proxy{p[1], p[0]} // swap } return p } perms := rn.Perm(len(p)) - rnd := make([]*Proxy, len(p)) + rnd := make([]*proxy.Proxy, len(p)) for i, p1 := range perms { rnd[i] = p[p1] @@ -45,11 +46,11 @@ type roundRobin struct { func (r *roundRobin) String() string { return "round_robin" } -func (r *roundRobin) List(p []*Proxy) []*Proxy { +func (r *roundRobin) List(p []*proxy.Proxy) []*proxy.Proxy { poolLen := uint32(len(p)) i := atomic.AddUint32(&r.robin, 1) % poolLen - robin := []*Proxy{p[i]} + robin := []*proxy.Proxy{p[i]} robin = append(robin, p[:i]...) robin = append(robin, p[i+1:]...) @@ -61,7 +62,7 @@ type sequential struct{} func (r *sequential) String() string { return "sequential" } -func (r *sequential) List(p []*Proxy) []*Proxy { +func (r *sequential) List(p []*proxy.Proxy) []*proxy.Proxy { return p } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/setup.go b/vendor/github.com/coredns/coredns/plugin/forward/setup.go index dfae70d37..5341b7e60 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/setup.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "errors" "fmt" + "path/filepath" "strconv" "time" @@ -12,13 +13,16 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/dnstap" "github.com/coredns/coredns/plugin/pkg/parse" + "github.com/coredns/coredns/plugin/pkg/proxy" pkgtls "github.com/coredns/coredns/plugin/pkg/tls" "github.com/coredns/coredns/plugin/pkg/transport" "github.com/miekg/dns" ) -func init() { plugin.Register("forward", setup) } +func init() { + plugin.Register("forward", setup) +} func setup(c *caddy.Controller) error { fs, err := parseForward(c) @@ -51,9 +55,7 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { - if tapPlugin, ok := taph.(dnstap.Dnstap); ok { - f.tapPlugin = &tapPlugin - } + f.SetTapPlugin(taph.(*dnstap.Dnstap)) } return nil }) @@ -69,7 +71,7 @@ func setup(c *caddy.Controller) error { // OnStartup starts a goroutines for all proxies. func (f *Forward) OnStartup() (err error) { for _, p := range f.proxies { - p.start(f.hcInterval) + p.Start(f.hcInterval) } return nil } @@ -77,7 +79,7 @@ func (f *Forward) OnStartup() (err error) { // OnShutdown stops all configured proxies. func (f *Forward) OnShutdown() error { for _, p := range f.proxies { - p.stop() + p.Stop() } return nil } @@ -129,7 +131,7 @@ func parseStanza(c *caddy.Controller) (*Forward, error) { if !allowedTrans[trans] { return f, fmt.Errorf("'%s' is not supported as a destination protocol in forward: %s", trans, host) } - p := NewProxy(h, trans) + p := proxy.NewProxy("forward", h, trans) f.proxies = append(f.proxies, p) transports[i] = trans } @@ -154,18 +156,19 @@ func parseStanza(c *caddy.Controller) (*Forward, error) { f.proxies[i].SetTLSConfig(f.tlsConfig) } f.proxies[i].SetExpire(f.expire) - f.proxies[i].health.SetRecursionDesired(f.opts.hcRecursionDesired) + f.proxies[i].GetHealthchecker().SetRecursionDesired(f.opts.HCRecursionDesired) // when TLS is used, checks are set to tcp-tls - if f.opts.forceTCP && transports[i] != transport.TLS { - f.proxies[i].health.SetTCPTransport() + if f.opts.ForceTCP && transports[i] != transport.TLS { + f.proxies[i].GetHealthchecker().SetTCPTransport() } - f.proxies[i].health.SetDomain(f.opts.hcDomain) + f.proxies[i].GetHealthchecker().SetDomain(f.opts.HCDomain) } return f, nil } func parseBlock(c *caddy.Controller, f *Forward) error { + config := dnsserver.GetConfig(c) switch c.Val() { case "except": ignore := c.RemainingArgs() @@ -196,12 +199,12 @@ func parseBlock(c *caddy.Controller, f *Forward) error { return fmt.Errorf("health_check can't be negative: %d", dur) } f.hcInterval = dur - f.opts.hcDomain = "." + f.opts.HCDomain = "." for c.NextArg() { switch hcOpts := c.Val(); hcOpts { case "no_rec": - f.opts.hcRecursionDesired = false + f.opts.HCRecursionDesired = false case "domain": if !c.NextArg() { return c.ArgErr() @@ -210,7 +213,7 @@ func parseBlock(c *caddy.Controller, f *Forward) error { if _, ok := dns.IsDomainName(hcDomain); !ok { return fmt.Errorf("health_check: invalid domain name %s", hcDomain) } - f.opts.hcDomain = plugin.Name(hcDomain).Normalize() + f.opts.HCDomain = plugin.Name(hcDomain).Normalize() default: return fmt.Errorf("health_check: unknown option %s", hcOpts) } @@ -220,18 +223,23 @@ func parseBlock(c *caddy.Controller, f *Forward) error { if c.NextArg() { return c.ArgErr() } - f.opts.forceTCP = true + f.opts.ForceTCP = true case "prefer_udp": if c.NextArg() { return c.ArgErr() } - f.opts.preferUDP = true + f.opts.PreferUDP = true case "tls": args := c.RemainingArgs() if len(args) > 3 { return c.ArgErr() } + for i := range args { + if !filepath.IsAbs(args[i]) && config.Root != "" { + args[i] = filepath.Join(config.Root, args[i]) + } + } tlsConfig, err := pkgtls.NewTLSConfigFromArgs(args...) if err != nil { return err diff --git a/vendor/github.com/coredns/coredns/plugin/health/health.go b/vendor/github.com/coredns/coredns/plugin/health/health.go index c69b221ec..980cf2bc8 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/health.go +++ b/vendor/github.com/coredns/coredns/plugin/health/health.go @@ -6,6 +6,7 @@ import ( "io" "net" "net/http" + "net/url" "time" clog "github.com/coredns/coredns/plugin/pkg/log" @@ -16,8 +17,9 @@ var log = clog.NewWithPlugin("health") // Health implements healthchecks by exporting a HTTP endpoint. type health struct { - Addr string - lameduck time.Duration + Addr string + lameduck time.Duration + healthURI *url.URL ln net.Listener nlSetup bool @@ -30,6 +32,19 @@ func (h *health) OnStartup() error { if h.Addr == "" { h.Addr = ":8080" } + + var err error + h.healthURI, err = url.Parse("http://" + h.Addr) + if err != nil { + return err + } + + h.healthURI.Path = "/health" + if h.healthURI.Host == "" { + // while we can listen on multiple network interfaces, we need to pick one to poll + h.healthURI.Host = "localhost" + } + ln, err := reuseport.Listen("tcp", h.Addr) if err != nil { return err @@ -39,7 +54,7 @@ func (h *health) OnStartup() error { h.mux = http.NewServeMux() h.nlSetup = true - h.mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + h.mux.HandleFunc(h.healthURI.Path, func(w http.ResponseWriter, r *http.Request) { // We're always healthy. w.WriteHeader(http.StatusOK) io.WriteString(w, http.StatusText(http.StatusOK)) diff --git a/vendor/github.com/coredns/coredns/plugin/health/overloaded.go b/vendor/github.com/coredns/coredns/plugin/health/overloaded.go index 57b9ca2d0..f8b3256bf 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/overloaded.go +++ b/vendor/github.com/coredns/coredns/plugin/health/overloaded.go @@ -32,8 +32,7 @@ func (h *health) overloaded(ctx context.Context) { Transport: bypassProxy, } - url := "http://" + h.Addr + "/health" - req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, h.healthURI.String(), nil) tick := time.NewTicker(1 * time.Second) defer tick.Stop() @@ -49,14 +48,14 @@ func (h *health) overloaded(ctx context.Context) { if err != nil { HealthDuration.Observe(time.Since(start).Seconds()) HealthFailures.Inc() - log.Warningf("Local health request to %q failed: %s", url, err) + log.Warningf("Local health request to %q failed: %s", req.URL.String(), err) continue } resp.Body.Close() elapsed := time.Since(start) HealthDuration.Observe(elapsed.Seconds()) if elapsed > time.Second { // 1s is pretty random, but a *local* scrape taking that long isn't good - log.Warningf("Local health request to %q took more than 1s: %s", url, elapsed) + log.Warningf("Local health request to %q took more than 1s: %s", req.URL.String(), elapsed) } case <-ctx.Done(): @@ -68,11 +67,12 @@ func (h *health) overloaded(ctx context.Context) { var ( // HealthDuration is the metric used for exporting how fast we can retrieve the /health endpoint. HealthDuration = promauto.NewHistogram(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: "health", - Name: "request_duration_seconds", - Buckets: plugin.SlimTimeBuckets, - Help: "Histogram of the time (in seconds) each request took.", + Namespace: plugin.Namespace, + Subsystem: "health", + Name: "request_duration_seconds", + Buckets: plugin.SlimTimeBuckets, + NativeHistogramBucketFactor: plugin.NativeHistogramBucketFactor, + Help: "Histogram of the time (in seconds) each request took.", }) // HealthFailures is the metric used to count how many times the health request failed HealthFailures = promauto.NewCounter(prometheus.CounterOpts{ diff --git a/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go b/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go index cf3c43cc8..e5aff0d6d 100644 --- a/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go +++ b/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go @@ -45,7 +45,7 @@ func newOptions() *options { return &options{ autoReverse: true, ttl: 3600, - reload: time.Duration(5 * time.Second), + reload: 5 * time.Second, } } diff --git a/vendor/github.com/coredns/coredns/plugin/hosts/setup.go b/vendor/github.com/coredns/coredns/plugin/hosts/setup.go index c256dc19a..128a365e1 100644 --- a/vendor/github.com/coredns/coredns/plugin/hosts/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/hosts/setup.go @@ -26,6 +26,7 @@ func periodicHostsUpdate(h *Hosts) chan bool { go func() { ticker := time.NewTicker(h.options.reload) + defer ticker.Stop() for { select { case <-parseChan: diff --git a/vendor/github.com/coredns/coredns/plugin/loadbalance/README.md b/vendor/github.com/coredns/coredns/plugin/loadbalance/README.md index 81a6580c4..fe29b19fc 100644 --- a/vendor/github.com/coredns/coredns/plugin/loadbalance/README.md +++ b/vendor/github.com/coredns/coredns/plugin/loadbalance/README.md @@ -16,10 +16,43 @@ implementations (like glibc) are particular about that. ## Syntax ~~~ -loadbalance [POLICY] +loadbalance [round_robin | weighted WEIGHTFILE] { + reload DURATION +} +~~~ +* `round_robin` policy randomizes the order of A, AAAA, and MX records applying a uniform probability distribution. This is the default load balancing policy. + +* `weighted` policy assigns weight values to IPs to control the relative likelihood of particular IPs to be returned as the first +(top) A/AAAA record in the answer. Note that it does not shuffle all the records in the answer, it is only concerned about the first A/AAAA record +returned in the answer. + + * **WEIGHTFILE** is the file containing the weight values assigned to IPs for various domain names. If the path is relative, the path from the **root** plugin will be prepended to it. The format is explained below in the *Weightfile* section. + + * **DURATION** interval to reload `WEIGHTFILE` and update weight assignments if there are changes in the file. The default value is `30s`. A value of `0s` means to not scan for changes and reload. + + +## Weightfile + +The generic weight file syntax: + +~~~ +# Comment lines are ignored + +domain-name1 +ip11 weight11 +ip12 weight12 +ip13 weight13 + +domain-name2 +ip21 weight21 +ip22 weight22 +# ... etc. ~~~ -* **POLICY** is how to balance. The default, and only option, is "round_robin". +where `ipXY` is an IP address for `domain-nameX` and `weightXY` is the weight value associated with that IP. The weight values are in the range of [1,255]. + +The `weighted` policy selects one of the address record in the result list and moves it to the top (first) position in the list. The random selection takes into account the weight values assigned to the addresses in the weight file. If an address in the result list is associated with no weight value in the weight file then the default weight value "1" is assumed for it when the selection is performed. + ## Examples @@ -31,3 +64,27 @@ Load balance replies coming back from Google Public DNS: forward . 8.8.8.8 8.8.4.4 } ~~~ + +Use the `weighted` strategy to load balance replies supplied by the **file** plugin. We assign weight vales `3`, `1` and `2` to the IPs `100.64.1.1`, `100.64.1.2` and `100.64.1.3`, respectively. These IPs are addresses in A records for the domain name `www.example.com` defined in the `./db.example.com` zone file. The ratio between the number of answers in which `100.64.1.1`, `100.64.1.2` or `100.64.1.3` is in the top (first) A record should converge to `3 : 1 : 2`. (E.g. there should be twice as many answers with `100.64.1.3` in the top A record than with `100.64.1.2`). +Corefile: + +~~~ corefile +example.com { + file ./db.example.com { + reload 10s + } + loadbalance weighted ./db.example.com.weights { + reload 10s + } +} +~~~ + +weight file `./db.example.com.weights`: + +~~~ +www.example.com +100.64.1.1 3 +100.64.1.2 1 +100.64.1.3 2 +~~~ + diff --git a/vendor/github.com/coredns/coredns/plugin/loadbalance/handler.go b/vendor/github.com/coredns/coredns/plugin/loadbalance/handler.go index ac046c8d0..8b84e1c5c 100644 --- a/vendor/github.com/coredns/coredns/plugin/loadbalance/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/loadbalance/handler.go @@ -10,15 +10,16 @@ import ( ) // RoundRobin is a plugin to rewrite responses for "load balancing". -type RoundRobin struct { - Next plugin.Handler +type LoadBalance struct { + Next plugin.Handler + shuffle func(*dns.Msg) *dns.Msg } // ServeDNS implements the plugin.Handler interface. -func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - wrr := &RoundRobinResponseWriter{w} - return plugin.NextOrFailure(rr.Name(), rr.Next, ctx, wrr, r) +func (lb LoadBalance) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + rw := &LoadBalanceResponseWriter{ResponseWriter: w, shuffle: lb.shuffle} + return plugin.NextOrFailure(lb.Name(), lb.Next, ctx, rw, r) } // Name implements the Handler interface. -func (rr RoundRobin) Name() string { return "loadbalance" } +func (lb LoadBalance) Name() string { return "loadbalance" } diff --git a/vendor/github.com/coredns/coredns/plugin/loadbalance/loadbalance.go b/vendor/github.com/coredns/coredns/plugin/loadbalance/loadbalance.go index 966121d6b..f2a1caed0 100644 --- a/vendor/github.com/coredns/coredns/plugin/loadbalance/loadbalance.go +++ b/vendor/github.com/coredns/coredns/plugin/loadbalance/loadbalance.go @@ -5,11 +5,19 @@ import ( "github.com/miekg/dns" ) -// RoundRobinResponseWriter is a response writer that shuffles A, AAAA and MX records. -type RoundRobinResponseWriter struct{ dns.ResponseWriter } +const ( + ramdomShufflePolicy = "round_robin" + weightedRoundRobinPolicy = "weighted" +) + +// LoadBalanceResponseWriter is a response writer that shuffles A, AAAA and MX records. +type LoadBalanceResponseWriter struct { + dns.ResponseWriter + shuffle func(*dns.Msg) *dns.Msg +} // WriteMsg implements the dns.ResponseWriter interface. -func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error { +func (r *LoadBalanceResponseWriter) WriteMsg(res *dns.Msg) error { if res.Rcode != dns.RcodeSuccess { return r.ResponseWriter.WriteMsg(res) } @@ -18,11 +26,14 @@ func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error { return r.ResponseWriter.WriteMsg(res) } + return r.ResponseWriter.WriteMsg(r.shuffle(res)) +} + +func randomShuffle(res *dns.Msg) *dns.Msg { res.Answer = roundRobin(res.Answer) res.Ns = roundRobin(res.Ns) res.Extra = roundRobin(res.Extra) - - return r.ResponseWriter.WriteMsg(res) + return res } func roundRobin(in []dns.RR) []dns.RR { @@ -72,9 +83,9 @@ func roundRobinShuffle(records []dns.RR) { } // Write implements the dns.ResponseWriter interface. -func (r *RoundRobinResponseWriter) Write(buf []byte) (int, error) { +func (r *LoadBalanceResponseWriter) Write(buf []byte) (int, error) { // Should we pack and unpack here to fiddle with the packet... Not likely. - log.Warning("RoundRobin called with Write: not shuffling records") + log.Warning("LoadBalance called with Write: not shuffling records") n, err := r.ResponseWriter.Write(buf) return n, err } diff --git a/vendor/github.com/coredns/coredns/plugin/loadbalance/setup.go b/vendor/github.com/coredns/coredns/plugin/loadbalance/setup.go index d8f273aaa..5706aeb41 100644 --- a/vendor/github.com/coredns/coredns/plugin/loadbalance/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/loadbalance/setup.go @@ -1,43 +1,103 @@ package loadbalance import ( + "errors" "fmt" + "path/filepath" + "time" "github.com/coredns/caddy" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" clog "github.com/coredns/coredns/plugin/pkg/log" + + "github.com/miekg/dns" ) var log = clog.NewWithPlugin("loadbalance") +var errOpen = errors.New("Weight file open error") func init() { plugin.Register("loadbalance", setup) } +type lbFuncs struct { + shuffleFunc func(*dns.Msg) *dns.Msg + onStartUpFunc func() error + onShutdownFunc func() error + weighted *weightedRR // used in unit tests only +} + func setup(c *caddy.Controller) error { - err := parse(c) + //shuffleFunc, startUpFunc, shutdownFunc, err := parse(c) + lb, err := parse(c) if err != nil { return plugin.Error("loadbalance", err) } + if lb.onStartUpFunc != nil { + c.OnStartup(lb.onStartUpFunc) + } + if lb.onShutdownFunc != nil { + c.OnShutdown(lb.onShutdownFunc) + } dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - return RoundRobin{Next: next} + return LoadBalance{Next: next, shuffle: lb.shuffleFunc} }) return nil } -func parse(c *caddy.Controller) error { +// func parse(c *caddy.Controller) (string, *weightedRR, error) { +func parse(c *caddy.Controller) (*lbFuncs, error) { + config := dnsserver.GetConfig(c) + for c.Next() { args := c.RemainingArgs() - switch len(args) { - case 0: - return nil - case 1: - if args[0] != "round_robin" { - return fmt.Errorf("unknown policy: %s", args[0]) + if len(args) == 0 { + return &lbFuncs{shuffleFunc: randomShuffle}, nil + } + switch args[0] { + case ramdomShufflePolicy: + if len(args) > 1 { + return nil, c.Errf("unknown property for %s", args[0]) + } + return &lbFuncs{shuffleFunc: randomShuffle}, nil + case weightedRoundRobinPolicy: + if len(args) < 2 { + return nil, c.Err("missing weight file argument") + } + + if len(args) > 2 { + return nil, c.Err("unexpected argument(s)") + } + + weightFileName := args[1] + if !filepath.IsAbs(weightFileName) && config.Root != "" { + weightFileName = filepath.Join(config.Root, weightFileName) + } + reload := 30 * time.Second // default reload period + for c.NextBlock() { + switch c.Val() { + case "reload": + t := c.RemainingArgs() + if len(t) < 1 { + return nil, c.Err("reload duration value is missing") + } + if len(t) > 1 { + return nil, c.Err("unexpected argument") + } + var err error + reload, err = time.ParseDuration(t[0]) + if err != nil { + return nil, c.Errf("invalid reload duration '%s'", t[0]) + } + default: + return nil, c.Errf("unknown property '%s'", c.Val()) + } } - return nil + return createWeightedFuncs(weightFileName, reload), nil + default: + return nil, fmt.Errorf("unknown policy: %s", args[0]) } } - return c.ArgErr() + return nil, c.ArgErr() } diff --git a/vendor/github.com/coredns/coredns/plugin/loadbalance/weighted.go b/vendor/github.com/coredns/coredns/plugin/loadbalance/weighted.go new file mode 100644 index 000000000..39b178017 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/loadbalance/weighted.go @@ -0,0 +1,329 @@ +package loadbalance + +import ( + "bufio" + "bytes" + "crypto/md5" + "errors" + "fmt" + "io" + "math/rand" + "net" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/coredns/coredns/plugin" + + "github.com/miekg/dns" +) + +type ( + // "weighted-round-robin" policy specific data + weightedRR struct { + fileName string + reload time.Duration + md5sum [md5.Size]byte + domains map[string]weights + randomGen + mutex sync.Mutex + } + // Per domain weights + weights []*weightItem + // Weight assigned to an address + weightItem struct { + address net.IP + value uint8 + } + // Random uint generator + randomGen interface { + randInit() + randUint(limit uint) uint + } +) + +// Random uint generator +type randomUint struct { + rn *rand.Rand +} + +func (r *randomUint) randInit() { + r.rn = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +func (r *randomUint) randUint(limit uint) uint { + return uint(r.rn.Intn(int(limit))) +} + +func weightedShuffle(res *dns.Msg, w *weightedRR) *dns.Msg { + switch res.Question[0].Qtype { + case dns.TypeA, dns.TypeAAAA, dns.TypeSRV: + res.Answer = w.weightedRoundRobin(res.Answer) + res.Extra = w.weightedRoundRobin(res.Extra) + } + return res +} + +func weightedOnStartUp(w *weightedRR, stopReloadChan chan bool) error { + err := w.updateWeights() + if errors.Is(err, errOpen) && w.reload != 0 { + log.Warningf("Failed to open weight file:%v. Will try again in %v", + err, w.reload) + } else if err != nil { + return plugin.Error("loadbalance", err) + } + // start periodic weight file reload go routine + w.periodicWeightUpdate(stopReloadChan) + return nil +} + +func createWeightedFuncs(weightFileName string, + reload time.Duration) *lbFuncs { + lb := &lbFuncs{ + weighted: &weightedRR{ + fileName: weightFileName, + reload: reload, + randomGen: &randomUint{}, + }, + } + lb.weighted.randomGen.randInit() + + lb.shuffleFunc = func(res *dns.Msg) *dns.Msg { + return weightedShuffle(res, lb.weighted) + } + + stopReloadChan := make(chan bool) + + lb.onStartUpFunc = func() error { + return weightedOnStartUp(lb.weighted, stopReloadChan) + } + + lb.onShutdownFunc = func() error { + // stop periodic weigh reload go routine + close(stopReloadChan) + return nil + } + return lb +} + +// Apply weighted round robin policy to the answer +func (w *weightedRR) weightedRoundRobin(in []dns.RR) []dns.RR { + cname := []dns.RR{} + address := []dns.RR{} + mx := []dns.RR{} + rest := []dns.RR{} + for _, r := range in { + switch r.Header().Rrtype { + case dns.TypeCNAME: + cname = append(cname, r) + case dns.TypeA, dns.TypeAAAA: + address = append(address, r) + case dns.TypeMX: + mx = append(mx, r) + default: + rest = append(rest, r) + } + } + + if len(address) == 0 { + // no change + return in + } + + w.setTopRecord(address) + + out := append(cname, rest...) + out = append(out, address...) + out = append(out, mx...) + return out +} + +// Move the next expected address to the first position in the result list +func (w *weightedRR) setTopRecord(address []dns.RR) { + itop := w.topAddressIndex(address) + + if itop < 0 { + // internal error + return + } + + if itop != 0 { + // swap the selected top entry with the actual one + address[0], address[itop] = address[itop], address[0] + } +} + +// Compute the top (first) address index +func (w *weightedRR) topAddressIndex(address []dns.RR) int { + w.mutex.Lock() + defer w.mutex.Unlock() + + // Determine the weight value for each address in the answer + var wsum uint + type waddress struct { + index int + weight uint8 + } + weightedAddr := make([]waddress, len(address)) + for i, ar := range address { + wa := &weightedAddr[i] + wa.index = i + wa.weight = 1 // default weight + var ip net.IP + switch ar.Header().Rrtype { + case dns.TypeA: + ip = ar.(*dns.A).A + case dns.TypeAAAA: + ip = ar.(*dns.AAAA).AAAA + } + ws := w.domains[ar.Header().Name] + for _, w := range ws { + if w.address.Equal(ip) { + wa.weight = w.value + break + } + } + wsum += uint(wa.weight) + } + + // Select the first (top) IP + sort.Slice(weightedAddr, func(i, j int) bool { + return weightedAddr[i].weight > weightedAddr[j].weight + }) + v := w.randUint(wsum) + var psum uint + for _, wa := range weightedAddr { + psum += uint(wa.weight) + if v < psum { + return wa.index + } + } + + // we should never reach this + log.Errorf("Internal error: cannot find top address (randv:%v wsum:%v)", v, wsum) + return -1 +} + +// Start go routine to update weights from the weight file periodically +func (w *weightedRR) periodicWeightUpdate(stopReload <-chan bool) { + if w.reload == 0 { + return + } + + go func() { + ticker := time.NewTicker(w.reload) + for { + select { + case <-stopReload: + return + case <-ticker.C: + err := w.updateWeights() + if err != nil { + log.Error(err) + } + } + } + }() +} + +// Update weights from weight file +func (w *weightedRR) updateWeights() error { + reader, err := os.Open(filepath.Clean(w.fileName)) + if err != nil { + return errOpen + } + defer reader.Close() + + // check if the contents has changed + var buf bytes.Buffer + tee := io.TeeReader(reader, &buf) + bytes, err := io.ReadAll(tee) + if err != nil { + return err + } + md5sum := md5.Sum(bytes) + if md5sum == w.md5sum { + // file contents has not changed + return nil + } + w.md5sum = md5sum + scanner := bufio.NewScanner(&buf) + + // Parse the weight file contents + domains, err := w.parseWeights(scanner) + if err != nil { + return err + } + + // access to weights must be protected + w.mutex.Lock() + w.domains = domains + w.mutex.Unlock() + + log.Infof("Successfully reloaded weight file %s", w.fileName) + return nil +} + +// Parse the weight file contents +func (w *weightedRR) parseWeights(scanner *bufio.Scanner) (map[string]weights, error) { + var dname string + var ws weights + domains := make(map[string]weights) + + for scanner.Scan() { + nextLine := strings.TrimSpace(scanner.Text()) + if len(nextLine) == 0 || nextLine[0:1] == "#" { + // Empty and comment lines are ignored + continue + } + fields := strings.Fields(nextLine) + switch len(fields) { + case 1: + // (domain) name sanity check + if net.ParseIP(fields[0]) != nil { + return nil, fmt.Errorf("Wrong domain name:\"%s\" in weight file %s. (Maybe a missing weight value?)", + fields[0], w.fileName) + } + dname = fields[0] + + // add the root domain if it is missing + if dname[len(dname)-1] != '.' { + dname += "." + } + var ok bool + ws, ok = domains[dname] + if !ok { + ws = make(weights, 0) + domains[dname] = ws + } + case 2: + // IP address and weight value + ip := net.ParseIP(fields[0]) + if ip == nil { + return nil, fmt.Errorf("Wrong IP address:\"%s\" in weight file %s", fields[0], w.fileName) + } + weight, err := strconv.ParseUint(fields[1], 10, 8) + if err != nil || weight == 0 { + return nil, fmt.Errorf("Wrong weight value:\"%s\" in weight file %s", fields[1], w.fileName) + } + witem := &weightItem{address: ip, value: uint8(weight)} + if dname == "" { + return nil, fmt.Errorf("Missing domain name in weight file %s", w.fileName) + } + ws = append(ws, witem) + domains[dname] = ws + default: + return nil, fmt.Errorf("Could not parse weight line:\"%s\" in weight file %s", nextLine, w.fileName) + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("Weight file %s parsing error:%s", w.fileName, err) + } + + return domains, nil +} diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/provider.go b/vendor/github.com/coredns/coredns/plugin/metadata/provider.go index e1bd70590..2e88d58ea 100644 --- a/vendor/github.com/coredns/coredns/plugin/metadata/provider.go +++ b/vendor/github.com/coredns/coredns/plugin/metadata/provider.go @@ -8,33 +8,32 @@ // // Implement the Provider interface for a plugin p: // -// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { -// metadata.SetValueFunc(ctx, "test/something", func() string { return "myvalue" }) -// return ctx -// } +// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { +// metadata.SetValueFunc(ctx, "test/something", func() string { return "myvalue" }) +// return ctx +// } // // Basic example with caching: // -// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { -// cached := "" -// f := func() string { -// if cached != "" { -// return cached -// } -// cached = expensiveFunc() -// return cached -// } -// metadata.SetValueFunc(ctx, "test/something", f) -// return ctx -// } +// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { +// cached := "" +// f := func() string { +// if cached != "" { +// return cached +// } +// cached = expensiveFunc() +// return cached +// } +// metadata.SetValueFunc(ctx, "test/something", f) +// return ctx +// } // // If you need access to this metadata from another plugin: // -// // ... -// valueFunc := metadata.ValueFunc(ctx, "test/something") -// value := valueFunc() -// // use 'value' -// +// // ... +// valueFunc := metadata.ValueFunc(ctx, "test/something") +// value := valueFunc() +// // use 'value' package metadata import ( diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/README.md b/vendor/github.com/coredns/coredns/plugin/metrics/README.md index ec5da10d0..144a5d1c6 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/README.md +++ b/vendor/github.com/coredns/coredns/plugin/metrics/README.md @@ -21,6 +21,7 @@ the following metrics are exported: * `coredns_dns_response_size_bytes{server, zone, view, proto}` - response size in bytes. * `coredns_dns_responses_total{server, zone, view, rcode, plugin}` - response per zone, rcode and plugin. * `coredns_dns_https_responses_total{server, status}` - responses per server and http status code. +* `coredns_dns_quic_responses_total{server, status}` - responses per server and QUIC application code. * `coredns_plugin_enabled{server, zone, view, name}` - indicates whether a plugin is enabled on per server, zone and view basis. Almost each counter has a label `zone` which is the zonename used for the request/response. diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go b/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go index f0cf829c9..7b8078509 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go @@ -17,19 +17,21 @@ var ( }, []string{"server", "zone", "view", "proto", "family", "type"}) RequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: subsystem, - Name: "request_duration_seconds", - Buckets: plugin.TimeBuckets, - Help: "Histogram of the time (in seconds) each request took per zone.", + Namespace: plugin.Namespace, + Subsystem: subsystem, + Name: "request_duration_seconds", + Buckets: plugin.TimeBuckets, + NativeHistogramBucketFactor: plugin.NativeHistogramBucketFactor, + Help: "Histogram of the time (in seconds) each request took per zone.", }, []string{"server", "zone", "view"}) RequestSize = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: subsystem, - Name: "request_size_bytes", - Help: "Size of the EDNS0 UDP buffer in bytes (64K for TCP) per zone and protocol.", - Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3}, + Namespace: plugin.Namespace, + Subsystem: subsystem, + Name: "request_size_bytes", + Help: "Size of the EDNS0 UDP buffer in bytes (64K for TCP) per zone and protocol.", + Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3}, + NativeHistogramBucketFactor: plugin.NativeHistogramBucketFactor, }, []string{"server", "zone", "view", "proto"}) RequestDo = promauto.NewCounterVec(prometheus.CounterOpts{ @@ -40,11 +42,12 @@ var ( }, []string{"server", "zone", "view"}) ResponseSize = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: subsystem, - Name: "response_size_bytes", - Help: "Size of the returned response in bytes.", - Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3}, + Namespace: plugin.Namespace, + Subsystem: subsystem, + Name: "response_size_bytes", + Help: "Size of the returned response in bytes.", + Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3}, + NativeHistogramBucketFactor: plugin.NativeHistogramBucketFactor, }, []string{"server", "zone", "view", "proto"}) ResponseRcode = promauto.NewCounterVec(prometheus.CounterOpts{ @@ -72,6 +75,13 @@ var ( Name: "https_responses_total", Help: "Counter of DoH responses per server and http status code.", }, []string{"server", "status"}) + + QUICResponsesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: plugin.Namespace, + Subsystem: subsystem, + Name: "quic_responses_total", + Help: "Counter of DoQ responses per server and QUIC application code.", + }, []string{"server", "status"}) ) const ( diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go index e2b26526d..c7f423a76 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go @@ -48,5 +48,6 @@ const ( // MinimalDefaultTTL is the absolute lowest TTL we use in CoreDNS. MinimalDefaultTTL = 5 * time.Second // MaximumDefaulTTL is the maximum TTL was use on RRsets in CoreDNS. + // TODO: rename as MaximumDefaultTTL MaximumDefaulTTL = 1 * time.Hour ) diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go b/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go index 9d5305b34..faddfc8aa 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/miekg/dns" ) @@ -16,18 +17,30 @@ const MimeType = "application/dns-message" // Path is the URL path that should be used. const Path = "/dns-query" -// NewRequest returns a new DoH request given a method, URL (without any paths, so exclude /dns-query) and dns.Msg. +// NewRequest returns a new DoH request given a HTTP method, URL and dns.Msg. +// +// The URL should not have a path, so please exclude /dns-query. The URL will +// be prefixed with https:// by default, unless it's already prefixed with +// either http:// or https://. func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) { buf, err := m.Pack() if err != nil { return nil, err } + if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { + url = fmt.Sprintf("https://%s", url) + } + switch method { case http.MethodGet: b64 := base64.RawURLEncoding.EncodeToString(buf) - req, err := http.NewRequest(http.MethodGet, "https://"+url+Path+"?dns="+b64, nil) + req, err := http.NewRequest( + http.MethodGet, + fmt.Sprintf("%s%s?dns=%s", url, Path, b64), + nil, + ) if err != nil { return req, err } @@ -37,7 +50,11 @@ func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) { return req, nil case http.MethodPost: - req, err := http.NewRequest(http.MethodPost, "https://"+url+Path+"?bla=foo:443", bytes.NewReader(buf)) + req, err := http.NewRequest( + http.MethodPost, + fmt.Sprintf("%s%s?bla=foo:443", url, Path), + bytes.NewReader(buf), + ) if err != nil { return req, err } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go b/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go index 31f57ea9b..cd8639915 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go @@ -36,8 +36,7 @@ func SupportedOption(option uint16) bool { // Version checks the EDNS version in the request. If error // is nil everything is OK and we can invoke the plugin. If non-nil, the -// returned Msg is valid to be returned to the client (and should). For some -// reason this response should not contain a question RR in the question section. +// returned Msg is valid to be returned to the client (and should). func Version(req *dns.Msg) (*dns.Msg, error) { opt := req.IsEdns0() if opt == nil { @@ -48,8 +47,6 @@ func Version(req *dns.Msg) (*dns.Msg, error) { } m := new(dns.Msg) m.SetReply(req) - // zero out question section, wtf. - m.Question = nil o := new(dns.OPT) o.Hdr.Name = "." diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/fall/fall.go b/vendor/github.com/coredns/coredns/plugin/pkg/fall/fall.go index f819f99b6..898c8db76 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/fall/fall.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/fall/fall.go @@ -10,7 +10,6 @@ // See https://github.com/coredns/coredns/issues/2723 for some discussion on this, which includes this quote: // // TL;DR: `fallthrough` is indeed risky and hackish, but still a good feature of CoreDNS as it allows to quickly answer boring edge cases. -// package fall import ( diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go b/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go index 0589a3457..ad8d7ac3d 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go @@ -13,7 +13,7 @@ import ( "io" golog "log" "os" - "sync" + "sync/atomic" ) // D controls whether we should output debug logs. If true, we do, once set @@ -21,30 +21,22 @@ import ( var D = &d{} type d struct { - on bool - sync.RWMutex + on atomic.Bool } // Set enables debug logging. func (d *d) Set() { - d.Lock() - d.on = true - d.Unlock() + d.on.Store(true) } // Clear disables debug logging. func (d *d) Clear() { - d.Lock() - d.on = false - d.Unlock() + d.on.Store(false) } // Value returns if debug logging is enabled. func (d *d) Value() bool { - d.RLock() - b := d.on - d.RUnlock() - return b + return d.on.Load() } // logf calls log.Printf prefixed with level. diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go b/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go index 9206a033d..78f7cd93b 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go @@ -33,6 +33,14 @@ func HostPortOrFile(s ...string) ([]string, error) { var servers []string for _, h := range s { trans, host := Transport(h) + if len(host) == 0 { + return servers, fmt.Errorf("invalid address: %q", h) + } + + if trans == transport.UNIX { + servers = append(servers, trans+"://"+host) + continue + } addr, _, err := net.SplitHostPort(host) @@ -53,6 +61,8 @@ func HostPortOrFile(s ...string) ([]string, error) { ss = net.JoinHostPort(host, transport.Port) case transport.TLS: ss = transport.TLS + "://" + net.JoinHostPort(host, transport.TLSPort) + case transport.QUIC: + ss = transport.QUIC + "://" + net.JoinHostPort(host, transport.QUICPort) case transport.GRPC: ss = transport.GRPC + "://" + net.JoinHostPort(host, transport.GRPCPort) case transport.HTTPS: @@ -89,7 +99,7 @@ func tryFile(s string) ([]string, error) { servers := []string{} for _, s := range c.Servers { - servers = append(servers, net.JoinHostPort(s, c.Port)) + servers = append(servers, net.JoinHostPort(stripZone(s), c.Port)) } return servers, nil } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go b/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go index d632120d7..f0cf1c249 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go @@ -19,6 +19,10 @@ func Transport(s string) (trans string, addr string) { s = s[len(transport.DNS+"://"):] return transport.DNS, s + case strings.HasPrefix(s, transport.QUIC+"://"): + s = s[len(transport.QUIC+"://"):] + return transport.QUIC, s + case strings.HasPrefix(s, transport.GRPC+"://"): s = s[len(transport.GRPC+"://"):] return transport.GRPC, s @@ -27,6 +31,9 @@ func Transport(s string) (trans string, addr string) { s = s[len(transport.HTTPS+"://"):] return transport.HTTPS, s + case strings.HasPrefix(s, transport.UNIX+"://"): + s = s[len(transport.UNIX+"://"):] + return transport.UNIX, s } return transport.DNS, s diff --git a/vendor/github.com/coredns/coredns/plugin/forward/connect.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/connect.go similarity index 64% rename from vendor/github.com/coredns/coredns/plugin/forward/connect.go rename to vendor/github.com/coredns/coredns/plugin/pkg/proxy/connect.go index 3d53044e5..27385a467 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/connect.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/connect.go @@ -1,13 +1,15 @@ -// Package forward implements a forwarding proxy. It caches an upstream net.Conn for some time, so if the same +// Package proxy implements a forwarding proxy. It caches an upstream net.Conn for some time, so if the same // client returns the upstream's Conn will be precached. Depending on how you benchmark this looks to be // 50% faster than just opening a new connection for every client. It works with UDP and TCP and uses // inband healthchecking. -package forward +package proxy import ( "context" + "errors" "io" "strconv" + "strings" "sync/atomic" "time" @@ -54,10 +56,10 @@ func (t *Transport) Dial(proto string) (*persistConn, bool, error) { pc := <-t.ret if pc != nil { - ConnCacheHitsCount.WithLabelValues(t.addr, proto).Add(1) + connCacheHitsCount.WithLabelValues(t.proxyName, t.addr, proto).Add(1) return pc, true, nil } - ConnCacheMissesCount.WithLabelValues(t.addr, proto).Add(1) + connCacheMissesCount.WithLabelValues(t.proxyName, t.addr, proto).Add(1) reqTime := time.Now() timeout := t.dialTimeout() @@ -72,14 +74,14 @@ func (t *Transport) Dial(proto string) (*persistConn, bool, error) { } // Connect selects an upstream, sends the request and waits for a response. -func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options) (*dns.Msg, error) { +func (p *Proxy) Connect(ctx context.Context, state request.Request, opts Options) (*dns.Msg, error) { start := time.Now() proto := "" switch { - case opts.forceTCP: // TCP flag has precedence over UDP flag + case opts.ForceTCP: // TCP flag has precedence over UDP flag proto = "tcp" - case opts.preferUDP: + case opts.PreferUDP: proto = "udp" default: proto = state.Proto() @@ -113,10 +115,22 @@ func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options } var ret *dns.Msg - pc.c.SetReadDeadline(time.Now().Add(readTimeout)) + pc.c.SetReadDeadline(time.Now().Add(p.readTimeout)) for { ret, err = pc.c.ReadMsg() if err != nil { + if ret != nil && (state.Req.Id == ret.Id) && p.transport.transportTypeFromConn(pc) == typeUDP && shouldTruncateResponse(err) { + // For UDP, if the error is an overflow, we probably have an upstream misbehaving in some way. + // (e.g. sending >512 byte responses without an eDNS0 OPT RR). + // Instead of returning an error, return an empty response with TC bit set. This will make the + // client retry over TCP (if that's supported) or at least receive a clean + // error. The connection is still good so we break before the close. + + // Truncate the response. + ret = truncateResponse(ret) + break + } + pc.c.Close() // not giving it back if err == io.EOF && cached { return nil, ErrCachedClosed @@ -142,11 +156,33 @@ func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options rc = strconv.Itoa(ret.Rcode) } - RequestCount.WithLabelValues(p.addr).Add(1) - RcodeCount.WithLabelValues(rc, p.addr).Add(1) - RequestDuration.WithLabelValues(p.addr, rc).Observe(time.Since(start).Seconds()) + requestDuration.WithLabelValues(p.proxyName, p.addr, rc).Observe(time.Since(start).Seconds()) return ret, nil } const cumulativeAvgWeight = 4 + +// Function to determine if a response should be truncated. +func shouldTruncateResponse(err error) bool { + // This is to handle a scenario in which upstream sets the TC bit, but doesn't truncate the response + // and we get ErrBuf instead of overflow. + if _, isDNSErr := err.(*dns.Error); isDNSErr && errors.Is(err, dns.ErrBuf) { + return true + } else if strings.Contains(err.Error(), "overflow") { + return true + } + return false +} + +// Function to return an empty response with TC (truncated) bit set. +func truncateResponse(response *dns.Msg) *dns.Msg { + // Clear out Answer, Extra, and Ns sections + response.Answer = nil + response.Extra = nil + response.Ns = nil + + // Set TC bit to indicate truncation. + response.Truncated = true + return response +} diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/proxy/errors.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/errors.go new file mode 100644 index 000000000..461236423 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/errors.go @@ -0,0 +1,26 @@ +package proxy + +import ( + "errors" +) + +var ( + // ErrNoHealthy means no healthy proxies left. + ErrNoHealthy = errors.New("no healthy proxies") + // ErrNoForward means no forwarder defined. + ErrNoForward = errors.New("no forwarder defined") + // ErrCachedClosed means cached connection was closed by peer. + ErrCachedClosed = errors.New("cached connection was closed by peer") +) + +// Options holds various Options that can be set. +type Options struct { + // ForceTCP use TCP protocol for upstream DNS request. Has precedence over PreferUDP flag + ForceTCP bool + // PreferUDP use UDP protocol for upstream DNS request. + PreferUDP bool + // HCRecursionDesired sets recursion desired flag for Proxy healthcheck requests + HCRecursionDesired bool + // HCDomain sets domain for Proxy healthcheck requests + HCDomain string +} diff --git a/vendor/github.com/coredns/coredns/plugin/forward/health.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/health.go similarity index 63% rename from vendor/github.com/coredns/coredns/plugin/forward/health.go rename to vendor/github.com/coredns/coredns/plugin/pkg/proxy/health.go index ec0b48143..4b4b4cc01 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/health.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/health.go @@ -1,10 +1,11 @@ -package forward +package proxy import ( "crypto/tls" "sync/atomic" "time" + "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/transport" "github.com/miekg/dns" @@ -14,11 +15,16 @@ import ( type HealthChecker interface { Check(*Proxy) error SetTLSConfig(*tls.Config) + GetTLSConfig() *tls.Config SetRecursionDesired(bool) GetRecursionDesired() bool SetDomain(domain string) GetDomain() string SetTCPTransport() + GetReadTimeout() time.Duration + SetReadTimeout(time.Duration) + GetWriteTimeout() time.Duration + SetWriteTimeout(time.Duration) } // dnsHc is a health checker for a DNS endpoint (DNS, and DoT). @@ -26,23 +32,25 @@ type dnsHc struct { c *dns.Client recursionDesired bool domain string -} -var ( - hcReadTimeout = 1 * time.Second - hcWriteTimeout = 1 * time.Second -) + proxyName string +} // NewHealthChecker returns a new HealthChecker based on transport. -func NewHealthChecker(trans string, recursionDesired bool, domain string) HealthChecker { +func NewHealthChecker(proxyName, trans string, recursionDesired bool, domain string) HealthChecker { switch trans { case transport.DNS, transport.TLS: c := new(dns.Client) c.Net = "udp" - c.ReadTimeout = hcReadTimeout - c.WriteTimeout = hcWriteTimeout - - return &dnsHc{c: c, recursionDesired: recursionDesired, domain: domain} + c.ReadTimeout = 1 * time.Second + c.WriteTimeout = 1 * time.Second + + return &dnsHc{ + c: c, + recursionDesired: recursionDesired, + domain: domain, + proxyName: proxyName, + } } log.Warningf("No healthchecker for transport %q", trans) @@ -54,6 +62,10 @@ func (h *dnsHc) SetTLSConfig(cfg *tls.Config) { h.c.TLSConfig = cfg } +func (h *dnsHc) GetTLSConfig() *tls.Config { + return h.c.TLSConfig +} + func (h *dnsHc) SetRecursionDesired(recursionDesired bool) { h.recursionDesired = recursionDesired } @@ -72,15 +84,31 @@ func (h *dnsHc) SetTCPTransport() { h.c.Net = "tcp" } -// For HC we send to . IN NS +[no]rec message to the upstream. Dial timeouts and empty +func (h *dnsHc) GetReadTimeout() time.Duration { + return h.c.ReadTimeout +} + +func (h *dnsHc) SetReadTimeout(t time.Duration) { + h.c.ReadTimeout = t +} + +func (h *dnsHc) GetWriteTimeout() time.Duration { + return h.c.WriteTimeout +} + +func (h *dnsHc) SetWriteTimeout(t time.Duration) { + h.c.WriteTimeout = t +} + +// For HC, we send to . IN NS +[no]rec message to the upstream. Dial timeouts and empty // replies are considered fails, basically anything else constitutes a healthy upstream. // Check is used as the up.Func in the up.Probe. func (h *dnsHc) Check(p *Proxy) error { err := h.send(p.addr) if err != nil { - HealthcheckFailureCount.WithLabelValues(p.addr).Add(1) - atomic.AddUint32(&p.fails, 1) + healthcheckFailureCount.WithLabelValues(p.proxyName, p.addr).Add(1) + p.incrementFails() return err } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/proxy/metrics.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/metrics.go new file mode 100644 index 000000000..7def8b7ea --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/metrics.go @@ -0,0 +1,41 @@ +package proxy + +import ( + "github.com/coredns/coredns/plugin" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +// Variables declared for monitoring. +var ( + requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: plugin.Namespace, + Subsystem: "proxy", + Name: "request_duration_seconds", + Buckets: plugin.TimeBuckets, + NativeHistogramBucketFactor: plugin.NativeHistogramBucketFactor, + Help: "Histogram of the time each request took.", + }, []string{"proxy_name", "to", "rcode"}) + + healthcheckFailureCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: plugin.Namespace, + Subsystem: "proxy", + Name: "healthcheck_failures_total", + Help: "Counter of the number of failed healthchecks.", + }, []string{"proxy_name", "to"}) + + connCacheHitsCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: plugin.Namespace, + Subsystem: "proxy", + Name: "conn_cache_hits_total", + Help: "Counter of connection cache hits per upstream and protocol.", + }, []string{"proxy_name", "to", "proto"}) + + connCacheMissesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: plugin.Namespace, + Subsystem: "proxy", + Name: "conn_cache_misses_total", + Help: "Counter of connection cache misses per upstream and protocol.", + }, []string{"proxy_name", "to", "proto"}) +) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/persistent.go similarity index 92% rename from vendor/github.com/coredns/coredns/plugin/forward/persistent.go rename to vendor/github.com/coredns/coredns/plugin/pkg/proxy/persistent.go index 95d08e1e1..49c9dd385 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/persistent.go @@ -1,4 +1,4 @@ -package forward +package proxy import ( "crypto/tls" @@ -21,6 +21,7 @@ type Transport struct { expire time.Duration // After this duration a connection is expired. addr string tlsConfig *tls.Config + proxyName string dial chan string yield chan *persistConn @@ -28,7 +29,7 @@ type Transport struct { stop chan bool } -func newTransport(addr string) *Transport { +func newTransport(proxyName, addr string) *Transport { t := &Transport{ avgDialTime: int64(maxDialTimeout / 2), conns: [typeTotalCount][]*persistConn{}, @@ -38,13 +39,15 @@ func newTransport(addr string) *Transport { yield: make(chan *persistConn), ret: make(chan *persistConn), stop: make(chan bool), + proxyName: proxyName, } return t } -// connManagers manages the persistent connection cache for UDP and TCP. +// connManager manages the persistent connection cache for UDP and TCP. func (t *Transport) connManager() { ticker := time.NewTicker(defaultExpire) + defer ticker.Stop() Wait: for { select { @@ -153,9 +156,3 @@ const ( minDialTimeout = 1 * time.Second maxDialTimeout = 30 * time.Second ) - -// Make a var for minimizing this value in tests. -var ( - // Some resolves might take quite a while, usually (cached) responses are fast. Set to 2s to give us some time to retry a different upstream. - readTimeout = 2 * time.Second -) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/proxy.go similarity index 53% rename from vendor/github.com/coredns/coredns/plugin/forward/proxy.go rename to vendor/github.com/coredns/coredns/plugin/pkg/proxy/proxy.go index 6a4b5693e..99fb5df78 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/proxy.go @@ -1,4 +1,4 @@ -package forward +package proxy import ( "crypto/tls" @@ -6,34 +6,43 @@ import ( "sync/atomic" "time" + "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/up" ) // Proxy defines an upstream host. type Proxy struct { - fails uint32 - addr string + fails uint32 + addr string + proxyName string transport *Transport + readTimeout time.Duration + // health checking probe *up.Probe health HealthChecker } // NewProxy returns a new proxy. -func NewProxy(addr, trans string) *Proxy { +func NewProxy(proxyName, addr, trans string) *Proxy { p := &Proxy{ - addr: addr, - fails: 0, - probe: up.New(), - transport: newTransport(addr), + addr: addr, + fails: 0, + probe: up.New(), + readTimeout: 2 * time.Second, + transport: newTransport(proxyName, addr), + health: NewHealthChecker(proxyName, trans, true, "."), + proxyName: proxyName, } - p.health = NewHealthChecker(trans, true, ".") + runtime.SetFinalizer(p, (*Proxy).finalizer) return p } +func (p *Proxy) Addr() string { return p.addr } + // SetTLSConfig sets the TLS config in the lower p.transport and in the healthchecking client. func (p *Proxy) SetTLSConfig(cfg *tls.Config) { p.transport.SetTLSConfig(cfg) @@ -43,6 +52,14 @@ func (p *Proxy) SetTLSConfig(cfg *tls.Config) { // SetExpire sets the expire duration in the lower p.transport. func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) } +func (p *Proxy) GetHealthchecker() HealthChecker { + return p.health +} + +func (p *Proxy) Fails() uint32 { + return atomic.LoadUint32(&p.fails) +} + // Healthcheck kicks of a round of health checks for this proxy. func (p *Proxy) Healthcheck() { if p.health == nil { @@ -65,18 +82,30 @@ func (p *Proxy) Down(maxfails uint32) bool { return fails > maxfails } -// close stops the health checking goroutine. -func (p *Proxy) stop() { p.probe.Stop() } +// Stop close stops the health checking goroutine. +func (p *Proxy) Stop() { p.probe.Stop() } func (p *Proxy) finalizer() { p.transport.Stop() } -// start starts the proxy's healthchecking. -func (p *Proxy) start(duration time.Duration) { +// Start starts the proxy's healthchecking. +func (p *Proxy) Start(duration time.Duration) { p.probe.Start(duration) p.transport.Start() } +func (p *Proxy) SetReadTimeout(duration time.Duration) { + p.readTimeout = duration +} + +// incrementFails increments the number of fails safely. +func (p *Proxy) incrementFails() { + curVal := atomic.LoadUint32(&p.fails) + if curVal > curVal+1 { + // overflow occurred, do not update the counter again + return + } + atomic.AddUint32(&p.fails, 1) +} + const ( maxTimeout = 2 * time.Second ) - -var hcInterval = 500 * time.Millisecond diff --git a/vendor/github.com/coredns/coredns/plugin/forward/type.go b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/type.go similarity index 94% rename from vendor/github.com/coredns/coredns/plugin/forward/type.go rename to vendor/github.com/coredns/coredns/plugin/pkg/proxy/type.go index 9de842fbe..10f3a4639 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/type.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/proxy/type.go @@ -1,6 +1,8 @@ -package forward +package proxy -import "net" +import ( + "net" +) type transportType int diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go b/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go index f927305c2..457244328 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go @@ -59,31 +59,6 @@ var labels = map[string]struct{}{ // appendValue appends the current value of label. func appendValue(b []byte, state request.Request, rr *dnstest.Recorder, label string) []byte { switch label { - case "{type}": - return append(b, state.Type()...) - case "{name}": - return append(b, state.Name()...) - case "{class}": - return append(b, state.Class()...) - case "{proto}": - return append(b, state.Proto()...) - case "{size}": - return strconv.AppendInt(b, int64(state.Req.Len()), 10) - case "{remote}": - return appendAddrToRFC3986(b, state.IP()) - case "{port}": - return append(b, state.Port()...) - case "{local}": - return appendAddrToRFC3986(b, state.LocalIP()) - // Header placeholders (case-insensitive). - case headerReplacer + "id}": - return strconv.AppendInt(b, int64(state.Req.Id), 10) - case headerReplacer + "opcode}": - return strconv.AppendInt(b, int64(state.Req.Opcode), 10) - case headerReplacer + "do}": - return strconv.AppendBool(b, state.Do()) - case headerReplacer + "bufsize}": - return strconv.AppendInt(b, int64(state.Size()), 10) // Recorded replacements. case "{rcode}": if rr == nil || rr.Msg == nil { @@ -109,6 +84,38 @@ func appendValue(b []byte, state request.Request, rr *dnstest.Recorder, label st return appendFlags(b, rr.Msg.MsgHdr) } return append(b, EmptyValue...) + } + + if (request.Request{}) == state { + return append(b, EmptyValue...) + } + + switch label { + case "{type}": + return append(b, state.Type()...) + case "{name}": + return append(b, state.Name()...) + case "{class}": + return append(b, state.Class()...) + case "{proto}": + return append(b, state.Proto()...) + case "{size}": + return strconv.AppendInt(b, int64(state.Req.Len()), 10) + case "{remote}": + return appendAddrToRFC3986(b, state.IP()) + case "{port}": + return append(b, state.Port()...) + case "{local}": + return appendAddrToRFC3986(b, state.LocalIP()) + // Header placeholders (case-insensitive). + case headerReplacer + "id}": + return strconv.AppendInt(b, int64(state.Req.Id), 10) + case headerReplacer + "opcode}": + return strconv.AppendInt(b, int64(state.Req.Opcode), 10) + case headerReplacer + "do}": + return strconv.AppendBool(b, state.Do()) + case headerReplacer + "bufsize}": + return strconv.AppendInt(b, int64(state.Size()), 10) default: return append(b, EmptyValue...) } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/tls/tls.go b/vendor/github.com/coredns/coredns/plugin/pkg/tls/tls.go index cba25503e..41eff4bc0 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/tls/tls.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/tls/tls.go @@ -31,28 +31,31 @@ func setTLSDefaults(ctls *tls.Config) { // in list of arguments. Typically these come straight from the // Corefile. // no args -// - creates a Config with no cert and using system CAs -// - use for a client that talks to a server with a public signed cert (CA installed in system) -// - the client will not be authenticated by the server since there is no cert +// - creates a Config with no cert and using system CAs +// - use for a client that talks to a server with a public signed cert (CA installed in system) +// - the client will not be authenticated by the server since there is no cert +// // one arg: the path to CA PEM file -// - creates a Config with no cert using a specific CA -// - use for a client that talks to a server with a private signed cert (CA not installed in system) -// - the client will not be authenticated by the server since there is no cert +// - creates a Config with no cert using a specific CA +// - use for a client that talks to a server with a private signed cert (CA not installed in system) +// - the client will not be authenticated by the server since there is no cert +// // two args: path to cert PEM file, the path to private key PEM file -// - creates a Config with a cert, using system CAs to validate the other end -// - use for: -// - a server; or, -// - a client that talks to a server with a public cert and needs certificate-based authentication -// - the other end will authenticate this end via the provided cert -// - the cert of the other end will be verified via system CAs +// - creates a Config with a cert, using system CAs to validate the other end +// - use for: +// - a server; or, +// - a client that talks to a server with a public cert and needs certificate-based authentication +// - the other end will authenticate this end via the provided cert +// - the cert of the other end will be verified via system CAs +// // three args: path to cert PEM file, path to client private key PEM file, path to CA PEM file -// - creates a Config with the cert, using specified CA to validate the other end -// - use for: -// - a server; or, -// - a client that talks to a server with a privately signed cert and needs certificate-based -// authentication -// - the other end will authenticate this end via the provided cert -// - this end will verify the other end's cert using the specified CA +// - creates a Config with the cert, using specified CA to validate the other end +// - use for: +// - a server; or, +// - a client that talks to a server with a privately signed cert and needs certificate-based +// authentication +// - the other end will authenticate this end via the provided cert +// - this end will verify the other end's cert using the specified CA func NewTLSConfigFromArgs(args ...string) (*tls.Config, error) { var err error var c *tls.Config diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go b/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go index 85b3bee5f..cdb2c79b7 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go @@ -4,8 +4,10 @@ package transport const ( DNS = "dns" TLS = "tls" + QUIC = "quic" GRPC = "grpc" HTTPS = "https" + UNIX = "unix" ) // Port numbers for the various transports. @@ -14,6 +16,8 @@ const ( Port = "53" // TLSPort is the default port for DNS-over-TLS. TLSPort = "853" + // QUICPort is the default port for DNS-over-QUIC. + QUICPort = "853" // GRPCPort is the default port for DNS-over-gRPC. GRPCPort = "443" // HTTPSPort is the default port for DNS-over-HTTPS. diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go b/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go index 6f18ffb70..649107f59 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go @@ -10,9 +10,9 @@ import ( // Probe is used to run a single Func until it returns true (indicating a target is healthy). If an Func // is already in progress no new one will be added, i.e. there is always a maximum of 1 checks in flight. // -// There is a tradeoff to be made in figuring out quickly that an upstream is healthy and not doing to much work -// (sending queries) to find that out. Having some kind of exp. backoff here won't help much, because you don't won't -// to backoff too much. You then also need random queries to be perfomed every so often to quickly detect a working +// There is a tradeoff to be made in figuring out quickly that an upstream is healthy and not doing much work +// (sending queries) to find that out. Having some kind of exp. backoff here won't help much, because you don't want +// to backoff too much. You then also need random queries to be performed every so often to quickly detect a working // upstream. In the end we just send a query every 0.5 second to check the upstream. This hopefully strikes a balance // between getting information about the upstream state quickly and not doing too much work. Note that 0.5s is still an // eternity in DNS, so we may actually want to shorten it. diff --git a/vendor/github.com/coredns/coredns/plugin/plugin.go b/vendor/github.com/coredns/coredns/plugin/plugin.go index 51f5ba79c..ca5fe0100 100644 --- a/vendor/github.com/coredns/coredns/plugin/plugin.go +++ b/vendor/github.com/coredns/coredns/plugin/plugin.go @@ -108,5 +108,9 @@ var TimeBuckets = prometheus.ExponentialBuckets(0.00025, 2, 16) // from 0.25ms t // SlimTimeBuckets is low cardinality set of duration buckets. var SlimTimeBuckets = prometheus.ExponentialBuckets(0.00025, 10, 5) // from 0.25ms to 2.5 seconds +// NativeHistogramBucketFactor controls the resolution of Prometheus native histogram buckets. +// See: https://pkg.go.dev/github.com/prometheus/client_golang@v1.19.0/prometheus#section-readme +var NativeHistogramBucketFactor = 1.05 + // ErrOnce is returned when a plugin doesn't support multiple setups per server. var ErrOnce = errors.New("this plugin can only be used once per Server Block") diff --git a/vendor/github.com/coredns/coredns/plugin/reload/README.md b/vendor/github.com/coredns/coredns/plugin/reload/README.md index 1288a23f5..b4dff5505 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/README.md +++ b/vendor/github.com/coredns/coredns/plugin/reload/README.md @@ -68,7 +68,7 @@ Check every 10 seconds (jitter is automatically set to 10 / 2 = 5 in this case): ## Bugs The reload happens without data loss (i.e. DNS queries keep flowing), but there is a corner case -where the reload fails, and you loose functionality. Consider the following Corefile: +where the reload fails, and you lose functionality. Consider the following Corefile: ~~~ txt . { diff --git a/vendor/github.com/coredns/coredns/plugin/reload/reload.go b/vendor/github.com/coredns/coredns/plugin/reload/reload.go index 632c0369e..917681c3e 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/reload.go +++ b/vendor/github.com/coredns/coredns/plugin/reload/reload.go @@ -83,6 +83,7 @@ func hook(event caddy.EventName, info interface{}) error { go func() { tick := time.NewTicker(r.interval()) + defer tick.Stop() for { select { diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/README.md b/vendor/github.com/coredns/coredns/plugin/rewrite/README.md index b460989ce..895ef6325 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/README.md +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/README.md @@ -25,6 +25,8 @@ e.g., to rewrite ANY queries to HINFO, use `rewrite type ANY HINFO`. * `class` - the class of the message will be rewritten. FROM/TO must be a DNS class type (`IN`, `CH`, or `HS`); e.g., to rewrite CH queries to IN use `rewrite class CH IN`. * `edns0` - an EDNS0 option can be appended to the request as described below in the **EDNS0 Options** section. * `ttl` - the TTL value in the _response_ is rewritten. + * `cname` - the CNAME target if the response has a CNAME record + * `rcode` - the response code (RCODE) value in the _response_ is rewritten. * **TYPE** this optional element can be specified for a `name` or `ttl` field. If not given type `exact` will be assumed. If options should be specified the @@ -334,6 +336,61 @@ rewrite ttl example.com. 30- rewrite ttl example.com. 30 # equivalent to rewrite ttl example.com. 30-30 ``` +### RCODE Field Rewrites + +At times, the need to rewrite a RCODE value could arise. For example, a DNS server +may respond with a SERVFAIL instead of NOERROR records when AAAA records are requested. + +In the below example, the rcode value the answer for `coredns.rocks` the replies with SERVFAIL +is being switched to NOERROR. + +This example rewrites all the *.coredns.rocks domain SERVFAIL errors to NOERROR +``` + rewrite continue { + rcode regex (.*)\.coredns\.rocks SERVFAIL NOERROR + } +``` + +The same result numeric values: +``` + rewrite continue { + rcode regex (.*)\.coredns\.rocks 2 0 + } +``` + +The syntax for the RCODE rewrite rule is as follows. The meaning of +`exact|prefix|suffix|substring|regex` is the same as with the name rewrite rules. +An omitted type is defaulted to `exact`. + +``` +rewrite [continue|stop] rcode [exact|prefix|suffix|substring|regex] STRING FROM TO +``` + +The values of FROM and TO can be any of the following, text value or numeric: + +``` + 0 NOERROR + 1 FORMERR + 2 SERVFAIL + 3 NXDOMAIN + 4 NOTIMP + 5 REFUSED + 6 YXDOMAIN + 7 YXRRSET + 8 NXRRSET + 9 NOTAUTH + 10 NOTZONE + 16 BADSIG + 17 BADKEY + 18 BADTIME + 19 BADMODE + 20 BADNAME + 21 BADALG + 22 BADTRUNC + 23 BADCOOKIE +``` + + ## EDNS0 Options Using the FIELD edns0, you can set, append, or replace specific EDNS0 options in the request. @@ -404,3 +461,49 @@ rewrite edns0 subnet set 24 56 * If the query's source IP address is an IPv4 address, the first 24 bits in the IP will be the network subnet. * If the query's source IP address is an IPv6 address, the first 56 bits in the IP will be the network subnet. + + +### CNAME Field Rewrites + +There might be a scenario where you want the `CNAME` target of the response to be rewritten. You can do this by using the `CNAME` field rewrite. This will generate new answer records according to the new `CNAME` target. + +The syntax for the CNAME rewrite rule is as follows. The meaning of +`exact|prefix|suffix|substring|regex` is the same as with the name rewrite rules. +An omitted type is defaulted to `exact`. + +``` +rewrite [continue|stop] cname [exact|prefix|suffix|substring|regex] FROM TO +``` + +Consider the following `CNAME` rewrite rule with regex type. +``` +rewrite cname regex (.*).cdn.example.net. {1}.other.cdn.com. +``` + +If you were to send the following DNS request without the above rule, an example response would be: + +``` +$ dig @10.1.1.1 my-app.com + +;; QUESTION SECTION: +;my-app.com. IN A + +;; ANSWER SECTION: +my-app.com. 200 IN CNAME my-app.com.cdn.example.net. +my-app.com.cdn.example.net. 300 IN A 20.2.0.1 +my-app.com.cdn.example.net. 300 IN A 20.2.0.2 +``` + +If you were to send the same DNS request with the above rule set up, an example response would be: + +``` +$ dig @10.1.1.1 my-app.com + +;; QUESTION SECTION: +;my-app.com. IN A + +;; ANSWER SECTION: +my-app.com. 200 IN CNAME my-app.com.other.cdn.com. +my-app.com.other.cdn.com. 100 IN A 30.3.1.2 +``` +Note that the answer will contain a completely different set of answer records after rewriting the `CNAME` target. diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/cname_target.go b/vendor/github.com/coredns/coredns/plugin/rewrite/cname_target.go new file mode 100644 index 000000000..d57bae3b2 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/cname_target.go @@ -0,0 +1,152 @@ +package rewrite + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/upstream" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +// UpstreamInt wraps the Upstream API for dependency injection during testing +type UpstreamInt interface { + Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) +} + +// cnameTargetRule is cname target rewrite rule. +type cnameTargetRule struct { + rewriteType string + paramFromTarget string + paramToTarget string + nextAction string + Upstream UpstreamInt // Upstream for looking up external names during the resolution process. +} + +// cnameTargetRuleWithReqState is cname target rewrite rule state +type cnameTargetRuleWithReqState struct { + rule cnameTargetRule + state request.Request + ctx context.Context +} + +func (r *cnameTargetRule) getFromAndToTarget(inputCName string) (from string, to string) { + switch r.rewriteType { + case ExactMatch: + return r.paramFromTarget, r.paramToTarget + case PrefixMatch: + if strings.HasPrefix(inputCName, r.paramFromTarget) { + return inputCName, r.paramToTarget + strings.TrimPrefix(inputCName, r.paramFromTarget) + } + case SuffixMatch: + if strings.HasSuffix(inputCName, r.paramFromTarget) { + return inputCName, strings.TrimSuffix(inputCName, r.paramFromTarget) + r.paramToTarget + } + case SubstringMatch: + if strings.Contains(inputCName, r.paramFromTarget) { + return inputCName, strings.Replace(inputCName, r.paramFromTarget, r.paramToTarget, -1) + } + case RegexMatch: + pattern := regexp.MustCompile(r.paramFromTarget) + regexGroups := pattern.FindStringSubmatch(inputCName) + if len(regexGroups) == 0 { + return "", "" + } + substitution := r.paramToTarget + for groupIndex, groupValue := range regexGroups { + groupIndexStr := "{" + strconv.Itoa(groupIndex) + "}" + substitution = strings.Replace(substitution, groupIndexStr, groupValue, -1) + } + return inputCName, substitution + } + return "", "" +} + +func (r *cnameTargetRuleWithReqState) RewriteResponse(res *dns.Msg, rr dns.RR) { + // logic to rewrite the cname target of dns response + switch rr.Header().Rrtype { + case dns.TypeCNAME: + // rename the target of the cname response + if cname, ok := rr.(*dns.CNAME); ok { + fromTarget, toTarget := r.rule.getFromAndToTarget(cname.Target) + if cname.Target == fromTarget { + // create upstream request with the new target with the same qtype + r.state.Req.Question[0].Name = toTarget + upRes, err := r.rule.Upstream.Lookup(r.ctx, r.state, toTarget, r.state.Req.Question[0].Qtype) + + if err != nil { + log.Errorf("Error upstream request %v", err) + } + + var newAnswer []dns.RR + // iterate over first upstram response + // add the cname record to the new answer + for _, rr := range res.Answer { + if cname, ok := rr.(*dns.CNAME); ok { + // change the target name in the response + cname.Target = toTarget + newAnswer = append(newAnswer, rr) + } + } + // iterate over upstream response received + for _, rr := range upRes.Answer { + if rr.Header().Name == toTarget { + newAnswer = append(newAnswer, rr) + } + } + res.Answer = newAnswer + } + } + } +} + +func newCNAMERule(nextAction string, args ...string) (Rule, error) { + var rewriteType string + var paramFromTarget, paramToTarget string + if len(args) == 3 { + rewriteType = (strings.ToLower(args[0])) + switch rewriteType { + case ExactMatch: + case PrefixMatch: + case SuffixMatch: + case SubstringMatch: + case RegexMatch: + default: + return nil, fmt.Errorf("unknown cname rewrite type: %s", rewriteType) + } + paramFromTarget, paramToTarget = strings.ToLower(args[1]), strings.ToLower(args[2]) + } else if len(args) == 2 { + rewriteType = ExactMatch + paramFromTarget, paramToTarget = strings.ToLower(args[0]), strings.ToLower(args[1]) + } else { + return nil, fmt.Errorf("too few (%d) arguments for a cname rule", len(args)) + } + rule := cnameTargetRule{ + rewriteType: rewriteType, + paramFromTarget: paramFromTarget, + paramToTarget: paramToTarget, + nextAction: nextAction, + Upstream: upstream.New(), + } + return &rule, nil +} + +// Rewrite rewrites the current request. +func (r *cnameTargetRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + if r != nil && len(r.rewriteType) > 0 && len(r.paramFromTarget) > 0 && len(r.paramToTarget) > 0 { + return ResponseRules{&cnameTargetRuleWithReqState{ + rule: *r, + state: state, + ctx: ctx, + }}, RewriteDone + } + return nil, RewriteIgnored +} + +// Mode returns the processing mode. +func (r *cnameTargetRule) Mode() string { return r.nextAction } diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/name.go b/vendor/github.com/coredns/coredns/plugin/rewrite/name.go index 95d2b7a0d..d3da9c2b8 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/name.go +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/name.go @@ -92,7 +92,7 @@ type nameRewriterResponseRule struct { stringRewriter } -func (r *nameRewriterResponseRule) RewriteResponse(rr dns.RR) { +func (r *nameRewriterResponseRule) RewriteResponse(res *dns.Msg, rr dns.RR) { rr.Header().Name = r.rewriteString(rr.Header().Name) } @@ -101,7 +101,7 @@ type valueRewriterResponseRule struct { stringRewriter } -func (r *valueRewriterResponseRule) RewriteResponse(rr dns.RR) { +func (r *valueRewriterResponseRule) RewriteResponse(res *dns.Msg, rr dns.RR) { value := getRecordValueForRewrite(rr) if value != "" { new := r.rewriteString(value) diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/rcode.go b/vendor/github.com/coredns/coredns/plugin/rewrite/rcode.go new file mode 100644 index 000000000..814b95fd9 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/rcode.go @@ -0,0 +1,178 @@ +package rewrite + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +type rcodeResponseRule struct { + old int + new int +} + +func (r *rcodeResponseRule) RewriteResponse(res *dns.Msg, rr dns.RR) { + if r.old == res.MsgHdr.Rcode { + res.MsgHdr.Rcode = r.new + } +} + +type rcodeRuleBase struct { + nextAction string + response rcodeResponseRule +} + +func newRCodeRuleBase(nextAction string, old, new int) rcodeRuleBase { + return rcodeRuleBase{ + nextAction: nextAction, + response: rcodeResponseRule{old: old, new: new}, + } +} + +func (rule *rcodeRuleBase) responseRule(match bool) (ResponseRules, Result) { + if match { + return ResponseRules{&rule.response}, RewriteDone + } + return nil, RewriteIgnored +} + +// Mode returns the processing nextAction +func (rule *rcodeRuleBase) Mode() string { return rule.nextAction } + +type exactRCodeRule struct { + rcodeRuleBase + From string +} + +type prefixRCodeRule struct { + rcodeRuleBase + Prefix string +} + +type suffixRCodeRule struct { + rcodeRuleBase + Suffix string +} + +type substringRCodeRule struct { + rcodeRuleBase + Substring string +} + +type regexRCodeRule struct { + rcodeRuleBase + Pattern *regexp.Regexp +} + +// Rewrite rewrites the current request based upon exact match of the name +// in the question section of the request. +func (rule *exactRCodeRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + return rule.responseRule(rule.From == state.Name()) +} + +// Rewrite rewrites the current request when the name begins with the matching string. +func (rule *prefixRCodeRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + return rule.responseRule(strings.HasPrefix(state.Name(), rule.Prefix)) +} + +// Rewrite rewrites the current request when the name ends with the matching string. +func (rule *suffixRCodeRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + return rule.responseRule(strings.HasSuffix(state.Name(), rule.Suffix)) +} + +// Rewrite rewrites the current request based upon partial match of the +// name in the question section of the request. +func (rule *substringRCodeRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + return rule.responseRule(strings.Contains(state.Name(), rule.Substring)) +} + +// Rewrite rewrites the current request when the name in the question +// section of the request matches a regular expression. +func (rule *regexRCodeRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { + return rule.responseRule(len(rule.Pattern.FindStringSubmatch(state.Name())) != 0) +} + +// newRCodeRule creates a name matching rule based on exact, partial, or regex match +func newRCodeRule(nextAction string, args ...string) (Rule, error) { + if len(args) < 3 { + return nil, fmt.Errorf("too few (%d) arguments for a rcode rule", len(args)) + } + var oldStr, newStr string + if len(args) == 3 { + oldStr, newStr = args[1], args[2] + } + if len(args) == 4 { + oldStr, newStr = args[2], args[3] + } + old, valid := isValidRCode(oldStr) + if !valid { + return nil, fmt.Errorf("invalid matching RCODE '%s' for a rcode rule", oldStr) + } + new, valid := isValidRCode(newStr) + if !valid { + return nil, fmt.Errorf("invalid replacement RCODE '%s' for a rcode rule", newStr) + } + if len(args) == 4 { + switch strings.ToLower(args[0]) { + case ExactMatch: + return &exactRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + plugin.Name(args[1]).Normalize(), + }, nil + case PrefixMatch: + return &prefixRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + plugin.Name(args[1]).Normalize(), + }, nil + case SuffixMatch: + return &suffixRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + plugin.Name(args[1]).Normalize(), + }, nil + case SubstringMatch: + return &substringRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + plugin.Name(args[1]).Normalize(), + }, nil + case RegexMatch: + regexPattern, err := regexp.Compile(args[1]) + if err != nil { + return nil, fmt.Errorf("invalid regex pattern in a rcode rule: %s", args[1]) + } + return ®exRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + regexPattern, + }, nil + default: + return nil, fmt.Errorf("rcode rule supports only exact, prefix, suffix, substring, and regex name matching") + } + } + if len(args) > 4 { + return nil, fmt.Errorf("many few arguments for a rcode rule") + } + return &exactRCodeRule{ + newRCodeRuleBase(nextAction, old, new), + plugin.Name(args[0]).Normalize(), + }, nil +} + +// validRCode returns true if v is valid RCode value. +func isValidRCode(v string) (int, bool) { + i, err := strconv.ParseUint(v, 10, 32) + // try parsing integer based rcode + if err == nil && i <= 23 { + return int(i), true + } + + if RCodeInt, ok := dns.StringToRcode[strings.ToUpper(v)]; ok { + return RCodeInt, true + } + return 0, false +} diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/reverter.go b/vendor/github.com/coredns/coredns/plugin/rewrite/reverter.go index 7abbfb89f..853d96d9d 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/reverter.go +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/reverter.go @@ -41,7 +41,7 @@ func NewRevertPolicy(noRevert, noRestore bool) RevertPolicy { // ResponseRule contains a rule to rewrite a response with. type ResponseRule interface { - RewriteResponse(rr dns.RR) + RewriteResponse(res *dns.Msg, rr dns.RR) } // ResponseRules describes an ordered list of response rules to apply @@ -91,7 +91,7 @@ func (r *ResponseReverter) WriteMsg(res1 *dns.Msg) error { func (r *ResponseReverter) rewriteResourceRecord(res *dns.Msg, rr dns.RR) { for _, rule := range r.ResponseRules { - rule.RewriteResponse(rr) + rule.RewriteResponse(res, rr) } } diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/rewrite.go b/vendor/github.com/coredns/coredns/plugin/rewrite/rewrite.go index b28352cbd..edb11813e 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/rewrite.go +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/rewrite.go @@ -139,6 +139,10 @@ func newRule(args ...string) (Rule, error) { return newEdns0Rule(mode, args[startArg:]...) case "ttl": return newTTLRule(mode, args[startArg:]...) + case "cname": + return newCNAMERule(mode, args[startArg:]...) + case "rcode": + return newRCodeRule(mode, args[startArg:]...) default: return nil, fmt.Errorf("invalid rule type %q", args[0]) } diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/ttl.go b/vendor/github.com/coredns/coredns/plugin/rewrite/ttl.go index 1791301d6..5430fc923 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/ttl.go +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/ttl.go @@ -18,7 +18,7 @@ type ttlResponseRule struct { maxTTL uint32 } -func (r *ttlResponseRule) RewriteResponse(rr dns.RR) { +func (r *ttlResponseRule) RewriteResponse(res *dns.Msg, rr dns.RR) { if rr.Header().Ttl < r.minTTL { rr.Header().Ttl = r.minTTL } else if rr.Header().Ttl > r.maxTTL { diff --git a/vendor/github.com/coredns/coredns/plugin/rewrite/wire.go b/vendor/github.com/coredns/coredns/plugin/rewrite/wire.go index 11b4dac05..df25f7faa 100644 --- a/vendor/github.com/coredns/coredns/plugin/rewrite/wire.go +++ b/vendor/github.com/coredns/coredns/plugin/rewrite/wire.go @@ -21,7 +21,7 @@ func ipToWire(family int, ipAddr string) ([]byte, error) { // uint16ToWire writes unit16 to wire/binary format func uint16ToWire(data uint16) []byte { buf := make([]byte, 2) - binary.BigEndian.PutUint16(buf, uint16(data)) + binary.BigEndian.PutUint16(buf, data) return buf } diff --git a/vendor/github.com/coredns/coredns/plugin/template/README.md b/vendor/github.com/coredns/coredns/plugin/template/README.md index 5e14ae24a..1bca90662 100644 --- a/vendor/github.com/coredns/coredns/plugin/template/README.md +++ b/vendor/github.com/coredns/coredns/plugin/template/README.md @@ -17,6 +17,7 @@ template CLASS TYPE [ZONE...] { additional RR authority RR rcode CODE + ederror EXTENDED_ERROR_CODE [EXTRA_REASON] fallthrough [FALLTHROUGH-ZONE...] } ~~~ @@ -31,6 +32,8 @@ template CLASS TYPE [ZONE...] { in a response with an empty answer section. * `rcode` **CODE** A response code (`NXDOMAIN, SERVFAIL, ...`). The default is `NOERROR`. Valid response code values are per the `RcodeToString` map defined by the `miekg/dns` package in `msg.go`. +* `ederror` **EXTENDED_ERROR_CODE** is an extended DNS error code as a number defined in `RFC8914` (0, 1, 2,..., 24). + **EXTRA_REASON** is an additional string explaining the reason for returning the error. * `fallthrough` Continue with the next _template_ instance if the _template_'s **ZONE** matches a query name but no regex match. If there is no next _template_, continue resolution with the next plugin. If **[FALLTHROUGH-ZONE...]** are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. Without @@ -104,6 +107,7 @@ The `.invalid` domain is a reserved TLD (see [RFC 2606 Reserved Top Level DNS Na template ANY ANY invalid { rcode NXDOMAIN authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)" + ederror 21 "Blocked according to RFC2606" } } ~~~ diff --git a/vendor/github.com/coredns/coredns/plugin/template/setup.go b/vendor/github.com/coredns/coredns/plugin/template/setup.go index fc6b75165..56058f032 100644 --- a/vendor/github.com/coredns/coredns/plugin/template/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/template/setup.go @@ -2,6 +2,7 @@ package template import ( "regexp" + "strconv" gotmpl "text/template" "github.com/coredns/caddy" @@ -123,6 +124,22 @@ func templateParse(c *caddy.Controller) (handler Handler, err error) { } t.rcode = rcode + case "ederror": + args := c.RemainingArgs() + if len(args) != 1 && len(args) != 2 { + return handler, c.ArgErr() + } + + code, err := strconv.ParseUint(args[0], 10, 16) + if err != nil { + return handler, c.Errf("error parsing extended DNS error code %s, %v\n", c.Val(), err) + } + if len(args) == 2 { + t.ederror = &ederror{code: uint16(code), reason: args[1]} + } else { + t.ederror = &ederror{code: uint16(code)} + } + case "fallthrough": t.fall.SetZonesFromArgs(c.RemainingArgs()) diff --git a/vendor/github.com/coredns/coredns/plugin/template/template.go b/vendor/github.com/coredns/coredns/plugin/template/template.go index aee1e1b80..5eac81e8e 100644 --- a/vendor/github.com/coredns/coredns/plugin/template/template.go +++ b/vendor/github.com/coredns/coredns/plugin/template/template.go @@ -33,10 +33,16 @@ type template struct { authority []*gotmpl.Template qclass uint16 qtype uint16 + ederror *ederror fall fall.F upstream Upstreamer } +type ederror struct { + code uint16 + reason string +} + // Upstreamer looks up targets of CNAME templates type Upstreamer interface { Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) @@ -125,6 +131,12 @@ func (h Handler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) msg.Ns = append(msg.Ns, rr) } + if template.ederror != nil { + msg = msg.SetEdns0(4096, true) + ede := dns.EDNS0_EDE{InfoCode: template.ederror.code, ExtraText: template.ederror.reason} + msg.IsEdns0().Option = append(msg.IsEdns0().Option, &ede) + } + w.WriteMsg(msg) return template.rcode, nil } diff --git a/vendor/github.com/coredns/coredns/plugin/test/file.go b/vendor/github.com/coredns/coredns/plugin/test/file.go index 969406e96..667b6a3f7 100644 --- a/vendor/github.com/coredns/coredns/plugin/test/file.go +++ b/vendor/github.com/coredns/coredns/plugin/test/file.go @@ -3,6 +3,7 @@ package test import ( "os" "path/filepath" + "testing" ) // TempFile will create a temporary file on disk and returns the name and a cleanup function to remove it later. @@ -18,12 +19,9 @@ func TempFile(dir, content string) (string, func(), error) { return f.Name(), rmFunc, nil } -// WritePEMFiles creates a tmp dir with ca.pem, cert.pem, and key.pem and the func to remove it -func WritePEMFiles(dir string) (string, func(), error) { - tempDir, err := os.MkdirTemp(dir, "go-test-pemfiles") - if err != nil { - return "", nil, err - } +// WritePEMFiles creates a tmp dir with ca.pem, cert.pem, and key.pem +func WritePEMFiles(t *testing.T) (string, error) { + tempDir := t.TempDir() data := `-----BEGIN CERTIFICATE----- MIIC9zCCAd+gAwIBAgIJALGtqdMzpDemMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV @@ -45,7 +43,7 @@ I1rs/VUGKzcJGVIWbHrgjP68CTStGAvKgbsTqw7aLXTSqtPw88N9XVSyRg== -----END CERTIFICATE-----` path := filepath.Join(tempDir, "ca.pem") if err := os.WriteFile(path, []byte(data), 0644); err != nil { - return "", nil, err + return "", err } data = `-----BEGIN CERTIFICATE----- MIICozCCAYsCCQCRlf5BrvPuqjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdr @@ -65,8 +63,8 @@ zhDEPP4FhY+Sz+y1yWirphl7A1aZwhXVPcfWIGqpQ3jzNwUeocbH27kuLh+U4hQo qeg10RdFnw== -----END CERTIFICATE-----` path = filepath.Join(tempDir, "cert.pem") - if err = os.WriteFile(path, []byte(data), 0644); err != nil { - return "", nil, err + if err := os.WriteFile(path, []byte(data), 0644); err != nil { + return "", err } data = `-----BEGIN RSA PRIVATE KEY----- @@ -97,10 +95,9 @@ E/WObVJXDnBdViu0L9abE9iaTToBVri4cmlDlZagLuKVR+TFTCN/DSlVZTDkqkLI 8chzqtkH6b2b2R73hyRysWjsomys34ma3mEEPTX/aXeAF2MSZ/EWT9yL -----END RSA PRIVATE KEY-----` path = filepath.Join(tempDir, "key.pem") - if err = os.WriteFile(path, []byte(data), 0644); err != nil { - return "", nil, err + if err := os.WriteFile(path, []byte(data), 0644); err != nil { + return "", err } - rmFunc := func() { os.RemoveAll(tempDir) } - return tempDir, rmFunc, nil + return tempDir, nil } diff --git a/vendor/github.com/coredns/coredns/plugin/test/helpers.go b/vendor/github.com/coredns/coredns/plugin/test/helpers.go index 8145b605a..f99790a23 100644 --- a/vendor/github.com/coredns/coredns/plugin/test/helpers.go +++ b/vendor/github.com/coredns/coredns/plugin/test/helpers.go @@ -29,15 +29,19 @@ func (p RRSet) Less(i, j int) bool { return p[i].String() < p[j].String() } // Case represents a test case that encapsulates various data from a query and response. // Note that is the TTL of a record is 303 we don't compare it with the TTL. type Case struct { - Qname string - Qtype uint16 - Rcode int - Do bool - AuthenticatedData bool - Answer []dns.RR - Ns []dns.RR - Extra []dns.RR - Error error + Qname string + Qtype uint16 + Rcode int + Do bool + CheckingDisabled bool + RecursionAvailable bool + AuthenticatedData bool + Authoritative bool + Truncated bool + Answer []dns.RR + Ns []dns.RR + Extra []dns.RR + Error error } // Msg returns a *dns.Msg embedded in c. diff --git a/vendor/github.com/coredns/coredns/plugin/test/scrape.go b/vendor/github.com/coredns/coredns/plugin/test/scrape.go index 7847e39d4..7ac22d531 100644 --- a/vendor/github.com/coredns/coredns/plugin/test/scrape.go +++ b/vendor/github.com/coredns/coredns/plugin/test/scrape.go @@ -19,7 +19,6 @@ // // result := Scrape("http://localhost:9153/metrics") // v := MetricValue("coredns_cache_capacity", result) -// package test import ( @@ -217,7 +216,7 @@ func makeBuckets(m *dto.Metric) map[string]string { func fetchMetricFamilies(url string, ch chan<- *dto.MetricFamily) { defer close(ch) - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return } diff --git a/vendor/github.com/coreos/go-semver/semver/semver.go b/vendor/github.com/coreos/go-semver/semver/semver.go index 76cf4852c..eb9fb7ff2 100644 --- a/vendor/github.com/coreos/go-semver/semver/semver.go +++ b/vendor/github.com/coreos/go-semver/semver/semver.go @@ -85,7 +85,7 @@ func (v *Version) Set(version string) error { return fmt.Errorf("failed to validate metadata: %v", err) } - parsed := make([]int64, 3, 3) + parsed := make([]int64, 3) for i, v := range dotParts[:3] { val, err := strconv.ParseInt(v, 10, 64) diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go index 8d58ca0fb..c5b23a819 100644 --- a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !windows // +build !windows // Package journal provides write bindings to the local systemd journal. @@ -53,15 +54,9 @@ var ( onceConn sync.Once ) -func init() { - onceConn.Do(initConn) -} - // Enabled checks whether the local systemd journal is available for logging. func Enabled() bool { - onceConn.Do(initConn) - - if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil { + if c := getOrInitConn(); c == nil { return false } @@ -74,6 +69,58 @@ func Enabled() bool { return true } +// StderrIsJournalStream returns whether the process stderr is connected +// to the Journal's stream transport. +// +// This can be used for automatic protocol upgrading described in [Journal Native Protocol]. +// +// Returns true if JOURNAL_STREAM environment variable is present, +// and stderr's device and inode numbers match it. +// +// Error is returned if unexpected error occurs: e.g. if JOURNAL_STREAM environment variable +// is present, but malformed, fstat syscall fails, etc. +// +// [Journal Native Protocol]: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/#automatic-protocol-upgrading +func StderrIsJournalStream() (bool, error) { + return fdIsJournalStream(syscall.Stderr) +} + +// StdoutIsJournalStream returns whether the process stdout is connected +// to the Journal's stream transport. +// +// Returns true if JOURNAL_STREAM environment variable is present, +// and stdout's device and inode numbers match it. +// +// Error is returned if unexpected error occurs: e.g. if JOURNAL_STREAM environment variable +// is present, but malformed, fstat syscall fails, etc. +// +// Most users should probably use [StderrIsJournalStream]. +func StdoutIsJournalStream() (bool, error) { + return fdIsJournalStream(syscall.Stdout) +} + +func fdIsJournalStream(fd int) (bool, error) { + journalStream := os.Getenv("JOURNAL_STREAM") + if journalStream == "" { + return false, nil + } + + var expectedStat syscall.Stat_t + _, err := fmt.Sscanf(journalStream, "%d:%d", &expectedStat.Dev, &expectedStat.Ino) + if err != nil { + return false, fmt.Errorf("failed to parse JOURNAL_STREAM=%q: %v", journalStream, err) + } + + var stat syscall.Stat_t + err = syscall.Fstat(fd, &stat) + if err != nil { + return false, err + } + + match := stat.Dev == expectedStat.Dev && stat.Ino == expectedStat.Ino + return match, nil +} + // Send a message to the local systemd journal. vars is a map of journald // fields to values. Fields must be composed of uppercase letters, numbers, // and underscores, but must not start with an underscore. Within these @@ -82,7 +129,7 @@ func Enabled() bool { // (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html) // for more details. vars may be nil. func Send(message string, priority Priority, vars map[string]string) error { - conn := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) + conn := getOrInitConn() if conn == nil { return errors.New("could not initialize socket to journald") } @@ -126,6 +173,16 @@ func Send(message string, priority Priority, vars map[string]string) error { return nil } +// getOrInitConn attempts to get the global `unixConnPtr` socket, initializing if necessary +func getOrInitConn() *net.UnixConn { + conn := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) + if conn != nil { + return conn + } + onceConn.Do(initConn) + return (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) +} + func appendVariable(w io.Writer, name, value string) { if err := validVarName(name); err != nil { fmt.Fprintf(os.Stderr, "variable name %s contains invalid character, ignoring\n", name) @@ -194,7 +251,7 @@ func tempFd() (*os.File, error) { } // initConn initializes the global `unixConnPtr` socket. -// It is meant to be called exactly once, at program startup. +// It is automatically called when needed. func initConn() { autobind, err := net.ResolveUnixAddr("unixgram", "") if err != nil { diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go index 677aca68e..322e41e74 100644 --- a/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go @@ -33,3 +33,11 @@ func Enabled() bool { func Send(message string, priority Priority, vars map[string]string) error { return errors.New("could not initialize socket to journald") } + +func StderrIsJournalStream() (bool, error) { + return false, nil +} + +func StdoutIsJournalStream() (bool, error) { + return false, nil +} diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml index ba95cdd15..ac12e485a 100644 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: go +go_import_path: github.com/dustin/go-humanize go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x + - stable - master matrix: allow_failures: @@ -15,7 +15,7 @@ matrix: install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . + - go vet . + - go install -v -race ./... - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown index 91b4ae564..7d0b16b34 100644 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -5,7 +5,7 @@ Just a few functions for helping humanize times and sizes. `go get` it as `github.com/dustin/go-humanize`, import it as `"github.com/dustin/go-humanize"`, use it as `humanize`. -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for complete documentation. ## Sizes diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go index 1a2bf6172..3b015fd59 100644 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -28,6 +28,10 @@ var ( BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) // BigYiByte is 1,024 z bytes in bit.Ints BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) + // BigRiByte is 1,024 y bytes in bit.Ints + BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) + // BigQiByte is 1,024 r bytes in bit.Ints + BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) ) var ( @@ -51,6 +55,10 @@ var ( BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) // BigYByte is 1,000 SI z bytes in big.Ints BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) + // BigRByte is 1,000 SI y bytes in big.Ints + BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) + // BigQByte is 1,000 SI r bytes in big.Ints + BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) ) var bigBytesSizeTable = map[string]*big.Int{ @@ -71,6 +79,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zb": BigZByte, "yib": BigYiByte, "yb": BigYByte, + "rib": BigRiByte, + "rb": BigRByte, + "qib": BigQiByte, + "qb": BigQByte, // Without suffix "": BigByte, "ki": BigKiByte, @@ -89,6 +101,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zi": BigZiByte, "y": BigYByte, "yi": BigYiByte, + "r": BigRByte, + "ri": BigRiByte, + "q": BigQByte, + "qi": BigQiByte, } var ten = big.NewInt(10) @@ -115,7 +131,7 @@ func humanateBigBytes(s, base *big.Int, sizes []string) string { // // BigBytes(82854982) -> 83 MB func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} return humanateBigBytes(s, bigSIExp, sizes) } @@ -125,7 +141,7 @@ func BigBytes(s *big.Int) string { // // BigIBytes(82854982) -> 79 MiB func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} return humanateBigBytes(s, bigIECExp, sizes) } diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go index 620690dec..2bc83a03c 100644 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -1,3 +1,4 @@ +//go:build go1.6 // +build go1.6 package humanize diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go index 1c62b640d..bce923f37 100644 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -6,6 +6,9 @@ import ( ) func stripTrailingZeros(s string) string { + if !strings.ContainsRune(s, '.') { + return s + } offset := len(s) - 1 for offset > 0 { if s[offset] == '.' { diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go index dec618659..6470d0d47 100644 --- a/vendor/github.com/dustin/go-humanize/number.go +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -73,7 +73,7 @@ func FormatFloat(format string, n float64) string { if n > math.MaxFloat64 { return "Infinity" } - if n < -math.MaxFloat64 { + if n < (0.0 - math.MaxFloat64) { return "-Infinity" } diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go index ae659e0e4..8b8501984 100644 --- a/vendor/github.com/dustin/go-humanize/si.go +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -8,6 +8,8 @@ import ( ) var siPrefixTable = map[float64]string{ + -30: "q", // quecto + -27: "r", // ronto -24: "y", // yocto -21: "z", // zepto -18: "a", // atto @@ -25,6 +27,8 @@ var siPrefixTable = map[float64]string{ 18: "E", // exa 21: "Z", // zetta 24: "Y", // yotta + 27: "R", // ronna + 30: "Q", // quetta } var revSIPrefixTable = revfmap(siPrefixTable) diff --git a/vendor/github.com/ebitengine/purego/.gitignore b/vendor/github.com/ebitengine/purego/.gitignore new file mode 100644 index 000000000..b25c15b81 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/vendor/github.com/ebitengine/purego/LICENSE b/vendor/github.com/ebitengine/purego/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/ebitengine/purego/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ebitengine/purego/README.md b/vendor/github.com/ebitengine/purego/README.md new file mode 100644 index 000000000..8f40f8b7a --- /dev/null +++ b/vendor/github.com/ebitengine/purego/README.md @@ -0,0 +1,96 @@ +# purego +[![Go Reference](https://pkg.go.dev/badge/github.com/ebitengine/purego?GOOS=darwin.svg)](https://pkg.go.dev/github.com/ebitengine/purego?GOOS=darwin) + +A library for calling C functions from Go without Cgo. + +> This is beta software so expect bugs and potentially API breaking changes +> but each release will be tagged to avoid breaking people's code. +> Bug reports are encouraged. + +## Motivation + +The [Ebitengine](https://github.com/hajimehoshi/ebiten) game engine was ported to use only Go on Windows. This enabled +cross-compiling to Windows from any other operating system simply by setting `GOOS=windows`. The purego project was +born to bring that same vision to the other platforms supported by Ebitengine. + +## Benefits + +- **Simple Cross-Compilation**: No C means you can build for other platforms easily without a C compiler. +- **Faster Compilation**: Efficiently cache your entirely Go builds. +- **Smaller Binaries**: Using Cgo generates a C wrapper function for each C function called. Purego doesn't! +- **Dynamic Linking**: Load symbols at runtime and use it as a plugin system. +- **Foreign Function Interface**: Call into other languages that are compiled into shared objects. +- **Cgo Fallback**: Works even with CGO_ENABLED=1 so incremental porting is possible. +This also means unsupported GOARCHs (freebsd/riscv64, linux/mips, etc.) will still work +except for float arguments and return values. + +## Supported Platforms + +- **FreeBSD**: amd64, arm64 +- **Linux**: amd64, arm64 +- **macOS / iOS**: amd64, arm64 +- **Windows**: 386*, amd64, arm*, arm64 + +`*` These architectures only support SyscallN and NewCallback + +## Example + +This example only works on macOS and Linux. For a complete example look at [libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD. + +```go +package main + +import ( + "fmt" + "runtime" + + "github.com/ebitengine/purego" +) + +func getSystemLibrary() string { + switch runtime.GOOS { + case "darwin": + return "/usr/lib/libSystem.B.dylib" + case "linux": + return "libc.so.6" + default: + panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS)) + } +} + +func main() { + libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } + var puts func(string) + purego.RegisterLibFunc(&puts, libc, "puts") + puts("Calling C from Go without Cgo!") +} +``` + +Then to run: `CGO_ENABLED=0 go run main.go` + +## Questions + +If you have questions about how to incorporate purego in your project or want to discuss +how it works join the [Discord](https://discord.com/channels/842049801528016967/1123106378731487345)! + +### External Code + +Purego uses code that originates from the Go runtime. These files are under the BSD-3 +License that can be found [in the Go Source](https://github.com/golang/go/blob/master/LICENSE). +This is a list of the copied files: + +* `abi_*.h` from package `runtime/cgo` +* `zcallback_darwin_*.s` from package `runtime` +* `internal/fakecgo/abi_*.h` from package `runtime/cgo` +* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo` +* `internal/fakecgo/callbacks.go` from package `runtime/cgo` +* `internal/fakecgo/go_GOOS_GOARCH.go` from package `runtime/cgo` +* `internal/fakecgo/iscgo.go` from package `runtime/cgo` +* `internal/fakecgo/setenv.go` from package `runtime/cgo` +* `internal/fakecgo/freebsd.go` from package `runtime/cgo` + +The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of +`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636)) diff --git a/vendor/github.com/ebitengine/purego/abi_amd64.h b/vendor/github.com/ebitengine/purego/abi_amd64.h new file mode 100644 index 000000000..9949435fe --- /dev/null +++ b/vendor/github.com/ebitengine/purego/abi_amd64.h @@ -0,0 +1,99 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These save the frame pointer, so in general, functions that use +// these should have zero frame size to suppress the automatic frame +// pointer, though it's harmless to not do this. + +#ifdef GOOS_windows + +// REGS_HOST_TO_ABI0_STACK is the stack bytes used by +// PUSH_REGS_HOST_TO_ABI0. +#define REGS_HOST_TO_ABI0_STACK (28*8 + 8) + +// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from +// the host ABI to Go ABI0 code. It saves all registers that are +// callee-save in the host ABI and caller-save in Go ABI0 and prepares +// for entry to Go. +// +// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag. +// Clear the DF flag for the Go ABI. +// MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +#define PUSH_REGS_HOST_TO_ABI0() \ + PUSHFQ \ + CLD \ + ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \ + MOVQ DI, (0*0)(SP) \ + MOVQ SI, (1*8)(SP) \ + MOVQ BP, (2*8)(SP) \ + MOVQ BX, (3*8)(SP) \ + MOVQ R12, (4*8)(SP) \ + MOVQ R13, (5*8)(SP) \ + MOVQ R14, (6*8)(SP) \ + MOVQ R15, (7*8)(SP) \ + MOVUPS X6, (8*8)(SP) \ + MOVUPS X7, (10*8)(SP) \ + MOVUPS X8, (12*8)(SP) \ + MOVUPS X9, (14*8)(SP) \ + MOVUPS X10, (16*8)(SP) \ + MOVUPS X11, (18*8)(SP) \ + MOVUPS X12, (20*8)(SP) \ + MOVUPS X13, (22*8)(SP) \ + MOVUPS X14, (24*8)(SP) \ + MOVUPS X15, (26*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*0)(SP), DI \ + MOVQ (1*8)(SP), SI \ + MOVQ (2*8)(SP), BP \ + MOVQ (3*8)(SP), BX \ + MOVQ (4*8)(SP), R12 \ + MOVQ (5*8)(SP), R13 \ + MOVQ (6*8)(SP), R14 \ + MOVQ (7*8)(SP), R15 \ + MOVUPS (8*8)(SP), X6 \ + MOVUPS (10*8)(SP), X7 \ + MOVUPS (12*8)(SP), X8 \ + MOVUPS (14*8)(SP), X9 \ + MOVUPS (16*8)(SP), X10 \ + MOVUPS (18*8)(SP), X11 \ + MOVUPS (20*8)(SP), X12 \ + MOVUPS (22*8)(SP), X13 \ + MOVUPS (24*8)(SP), X14 \ + MOVUPS (26*8)(SP), X15 \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \ + POPFQ + +#else +// SysV ABI + +#define REGS_HOST_TO_ABI0_STACK (6*8) + +// SysV MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +// Both SysV and Go require DF to be cleared, so that's already clear. +// The SysV and Go frame pointer conventions are compatible. +#define PUSH_REGS_HOST_TO_ABI0() \ + ADJSP $(REGS_HOST_TO_ABI0_STACK) \ + MOVQ BP, (5*8)(SP) \ + LEAQ (5*8)(SP), BP \ + MOVQ BX, (0*8)(SP) \ + MOVQ R12, (1*8)(SP) \ + MOVQ R13, (2*8)(SP) \ + MOVQ R14, (3*8)(SP) \ + MOVQ R15, (4*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*8)(SP), BX \ + MOVQ (1*8)(SP), R12 \ + MOVQ (2*8)(SP), R13 \ + MOVQ (3*8)(SP), R14 \ + MOVQ (4*8)(SP), R15 \ + MOVQ (5*8)(SP), BP \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK) + +#endif diff --git a/vendor/github.com/ebitengine/purego/abi_arm64.h b/vendor/github.com/ebitengine/purego/abi_arm64.h new file mode 100644 index 000000000..5d5061ec1 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/abi_arm64.h @@ -0,0 +1,39 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP). +// +// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP). +// +// R29 is not saved because Go will save and restore it. + +#define SAVE_R19_TO_R28(offset) \ + STP (R19, R20), ((offset)+0*8)(RSP) \ + STP (R21, R22), ((offset)+2*8)(RSP) \ + STP (R23, R24), ((offset)+4*8)(RSP) \ + STP (R25, R26), ((offset)+6*8)(RSP) \ + STP (R27, g), ((offset)+8*8)(RSP) +#define RESTORE_R19_TO_R28(offset) \ + LDP ((offset)+0*8)(RSP), (R19, R20) \ + LDP ((offset)+2*8)(RSP), (R21, R22) \ + LDP ((offset)+4*8)(RSP), (R23, R24) \ + LDP ((offset)+6*8)(RSP), (R25, R26) \ + LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */ +#define SAVE_F8_TO_F15(offset) \ + FSTPD (F8, F9), ((offset)+0*8)(RSP) \ + FSTPD (F10, F11), ((offset)+2*8)(RSP) \ + FSTPD (F12, F13), ((offset)+4*8)(RSP) \ + FSTPD (F14, F15), ((offset)+6*8)(RSP) +#define RESTORE_F8_TO_F15(offset) \ + FLDPD ((offset)+0*8)(RSP), (F8, F9) \ + FLDPD ((offset)+2*8)(RSP), (F10, F11) \ + FLDPD ((offset)+4*8)(RSP), (F12, F13) \ + FLDPD ((offset)+6*8)(RSP), (F14, F15) diff --git a/vendor/github.com/ebitengine/purego/cgo.go b/vendor/github.com/ebitengine/purego/cgo.go new file mode 100644 index 000000000..7d5abef34 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/cgo.go @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build cgo && (darwin || freebsd || linux) + +package purego + +// if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly. +// This is required since some frameworks need TLS setup the C way which Go doesn't do. +// We currently don't support ios in fakecgo mode so force Cgo or fail +// Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used. +// which will import this package automatically. Normally this isn't an issue since it +// usually isn't possible to call into C without using that import. However, with purego +// it is since we don't use `import "C"`! +import ( + _ "runtime/cgo" + + _ "github.com/ebitengine/purego/internal/cgo" +) diff --git a/vendor/github.com/ebitengine/purego/dlerror.go b/vendor/github.com/ebitengine/purego/dlerror.go new file mode 100644 index 000000000..cf4c0505d --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlerror.go @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package purego + +// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose. +type Dlerror struct { + s string +} + +func (e Dlerror) Error() string { + return e.s +} diff --git a/vendor/github.com/ebitengine/purego/dlfcn.go b/vendor/github.com/ebitengine/purego/dlfcn.go new file mode 100644 index 000000000..2ee6f34b3 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn.go @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package purego + +import ( + "unsafe" +) + +// Unix Specification for dlfcn.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html + +var ( + fnDlopen func(path string, mode int) uintptr + fnDlsym func(handle uintptr, name string) uintptr + fnDlerror func() string + fnDlclose func(handle uintptr) bool +) + +func init() { + RegisterFunc(&fnDlopen, dlopenABI0) + RegisterFunc(&fnDlsym, dlsymABI0) + RegisterFunc(&fnDlerror, dlerrorABI0) + RegisterFunc(&fnDlclose, dlcloseABI0) +} + +// Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible +// with the current process and has not already been loaded into the +// current process, it is loaded and linked. After being linked, if it contains +// any initializer functions, they are called, before Dlopen +// returns. It returns a handle that can be used with Dlsym and Dlclose. +// A second call to Dlopen with the same path will return the same handle, but the internal +// reference count for the handle will be incremented. Therefore, all +// Dlopen calls should be balanced with a Dlclose call. +func Dlopen(path string, mode int) (uintptr, error) { + u := fnDlopen(path, mode) + if u == 0 { + return 0, Dlerror{fnDlerror()} + } + return u, nil +} + +// Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name. +// It returns the address where that symbol is loaded into memory. If the symbol is not found, +// in the specified library or any of the libraries that were automatically loaded by Dlopen +// when that library was loaded, Dlsym returns zero. +func Dlsym(handle uintptr, name string) (uintptr, error) { + u := fnDlsym(handle, name) + if u == 0 { + return 0, Dlerror{fnDlerror()} + } + return u, nil +} + +// Dlclose decrements the reference count on the dynamic library handle. +// If the reference count drops to zero and no other loaded libraries +// use symbols in it, then the dynamic library is unloaded. +func Dlclose(handle uintptr) error { + if fnDlclose(handle) { + return Dlerror{fnDlerror()} + } + return nil +} + +//go:linkname openLibrary openLibrary +func openLibrary(name string) (uintptr, error) { + return Dlopen(name, RTLD_NOW|RTLD_GLOBAL) +} + +func loadSymbol(handle uintptr, name string) (uintptr, error) { + return Dlsym(handle, name) +} + +// these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go +// the indirection is necessary because a function is actually a pointer to the pointer to the code. +// sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't +// appear to work if you link directly to the C function on darwin arm64. + +//go:linkname dlopen dlopen +var dlopen uintptr +var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen)) + +//go:linkname dlsym dlsym +var dlsym uintptr +var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym)) + +//go:linkname dlclose dlclose +var dlclose uintptr +var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose)) + +//go:linkname dlerror dlerror +var dlerror uintptr +var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror)) diff --git a/vendor/github.com/ebitengine/purego/dlfcn_darwin.go b/vendor/github.com/ebitengine/purego/dlfcn_darwin.go new file mode 100644 index 000000000..b343f8461 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_darwin.go @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package purego + +// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html + +const ( + RTLD_DEFAULT = ^uintptr(0) - 1 // Pseudo-handle for dlsym so search for any loaded symbol + RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time. + RTLD_NOW = 0x2 // Relocations are performed when the object is loaded. + RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules. + RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules. +) + +//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib" + +//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib" diff --git a/vendor/github.com/ebitengine/purego/dlfcn_freebsd.go b/vendor/github.com/ebitengine/purego/dlfcn_freebsd.go new file mode 100644 index 000000000..38f067832 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_freebsd.go @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package purego + +// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h +const ( + RTLD_DEFAULT = ^uintptr(0) - 2 // Pseudo-handle for dlsym so search for any loaded symbol + RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time. + RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded. + RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules. + RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules. +) diff --git a/vendor/github.com/ebitengine/purego/dlfcn_linux.go b/vendor/github.com/ebitengine/purego/dlfcn_linux.go new file mode 100644 index 000000000..301127538 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_linux.go @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package purego + +// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html + +const ( + RTLD_DEFAULT = 0x00000 // Pseudo-handle for dlsym so search for any loaded symbol + RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time. + RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded. + RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules. + RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules. +) diff --git a/vendor/github.com/ebitengine/purego/dlfcn_nocgo_freebsd.go b/vendor/github.com/ebitengine/purego/dlfcn_nocgo_freebsd.go new file mode 100644 index 000000000..1bd66125b --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_nocgo_freebsd.go @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build !cgo + +package purego + +//go:cgo_import_dynamic purego_dlopen dlopen "libc.so.7" +//go:cgo_import_dynamic purego_dlsym dlsym "libc.so.7" +//go:cgo_import_dynamic purego_dlerror dlerror "libc.so.7" +//go:cgo_import_dynamic purego_dlclose dlclose "libc.so.7" diff --git a/vendor/github.com/ebitengine/purego/dlfcn_nocgo_linux.go b/vendor/github.com/ebitengine/purego/dlfcn_nocgo_linux.go new file mode 100644 index 000000000..2c8009fdb --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_nocgo_linux.go @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build !cgo + +package purego + +// if there is no Cgo we must link to each of the functions from dlfcn.h +// then the functions are called inside dlfcn_stubs.s + +//go:cgo_import_dynamic purego_dlopen dlopen "libdl.so.2" +//go:cgo_import_dynamic purego_dlsym dlsym "libdl.so.2" +//go:cgo_import_dynamic purego_dlerror dlerror "libdl.so.2" +//go:cgo_import_dynamic purego_dlclose dlclose "libdl.so.2" + +// on amd64 we don't need the following line - on 386 we do... +// anyway - with those lines the output is better (but doesn't matter) - without it on amd64 we get multiple DT_NEEDED with "libc.so.6" etc + +//go:cgo_import_dynamic _ _ "libdl.so.2" diff --git a/vendor/github.com/ebitengine/purego/dlfcn_stubs.s b/vendor/github.com/ebitengine/purego/dlfcn_stubs.s new file mode 100644 index 000000000..fa434bf1d --- /dev/null +++ b/vendor/github.com/ebitengine/purego/dlfcn_stubs.s @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || !cgo && (freebsd || linux) + +#include "textflag.h" + +// func dlopen(path *byte, mode int) (ret uintptr) +TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_dlopen(SB) + RET + +// func dlsym(handle uintptr, symbol *byte) (ret uintptr) +TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_dlsym(SB) + RET + +// func dlerror() (ret *byte) +TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_dlerror(SB) + RET + +// func dlclose(handle uintptr) (ret int) +TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_dlclose(SB) + RET diff --git a/vendor/github.com/ebitengine/purego/func.go b/vendor/github.com/ebitengine/purego/func.go new file mode 100644 index 000000000..03d7dae7e --- /dev/null +++ b/vendor/github.com/ebitengine/purego/func.go @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux || windows + +package purego + +import ( + "math" + "reflect" + "runtime" + "unsafe" + + "github.com/ebitengine/purego/internal/strings" +) + +// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). +// It panics if it can't find the name symbol. +func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { + sym, err := loadSymbol(handle, name) + if err != nil { + panic(err) + } + RegisterFunc(fptr, sym) +} + +// RegisterFunc takes a pointer to a Go function representing the calling convention of the C function. +// fptr will be set to a function that when called will call the C function given by cfn with the +// parameters passed in the correct registers and stack. +// +// A panic is produced if the type is not a function pointer or if the function returns more than 1 value. +// +// These conversions describe how a Go type in the fptr will be used to call +// the C function. It is important to note that there is no way to verify that fptr +// matches the C function. This also holds true for struct types where the padding +// needs to be ensured to match that of C; RegisterFunc does not verify this. +// +// # Type Conversions (Go <=> C) +// +// string <=> char* +// bool <=> _Bool +// uintptr <=> uintptr_t +// uint <=> uint32_t or uint64_t +// uint8 <=> uint8_t +// uint16 <=> uint16_t +// uint32 <=> uint32_t +// uint64 <=> uint64_t +// int <=> int32_t or int64_t +// int8 <=> int8_t +// int16 <=> int16_t +// int32 <=> int32_t +// int64 <=> int64_t +// float32 <=> float +// float64 <=> double +// struct <=> struct (WIP - darwin only) +// func <=> C function +// unsafe.Pointer, *T <=> void* +// []T => void* +// +// There is a special case when the last argument of fptr is a variadic interface (or []interface} +// it will be expanded into a call to the C function as if it had the arguments in that slice. +// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg. +// This is not the same as C variadic. +// +// # Memory +// +// In general it is not possible for purego to guarantee the lifetimes of objects returned or received from +// calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't +// hold onto a reference to Go memory. This is the same as the [Cgo rules]. +// +// However, there are some special cases. When passing a string as an argument if the string does not end in a null +// terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for +// that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some +// undefined time. However, if the string does already contain a null-terminated byte then no copy is done. +// It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory. +// This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function +// returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory +// and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced. +// This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue +// to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice +// using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime +// of the pointer +// +// # Structs +// +// Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However, +// it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure +// that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example. +// +// # Example +// +// All functions below call this C function: +// +// char *foo(char *str); +// +// // Let purego convert types +// var foo func(s string) string +// goString := foo("copied") +// // Go will garbage collect this string +// +// // Manually, handle allocations +// var foo2 func(b string) *byte +// mustFree := foo2("not copied\x00") +// defer free(mustFree) +// +// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C +func RegisterFunc(fptr interface{}, cfn uintptr) { + fn := reflect.ValueOf(fptr).Elem() + ty := fn.Type() + if ty.Kind() != reflect.Func { + panic("purego: fptr must be a function pointer") + } + if ty.NumOut() > 1 { + panic("purego: function can only return zero or one values") + } + if cfn == 0 { + panic("purego: cfn is nil") + } + { + // this code checks how many registers and stack this function will use + // to avoid crashing with too many arguments + var ints int + var floats int + var stack int + for i := 0; i < ty.NumIn(); i++ { + arg := ty.In(i) + switch arg.Kind() { + case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer, reflect.Slice, + reflect.Func, reflect.Bool: + if ints < numOfIntegerRegisters() { + ints++ + } else { + stack++ + } + case reflect.Float32, reflect.Float64: + if floats < numOfFloats { + floats++ + } else { + stack++ + } + case reflect.Struct: + if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") { + panic("purego: struct arguments are only supported on darwin amd64 & arm64") + } + if arg.Size() == 0 { + continue + } + addInt := func(u uintptr) { + ints++ + } + addFloat := func(u uintptr) { + floats++ + } + addStack := func(u uintptr) { + stack++ + } + _ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil) + default: + panic("purego: unsupported kind " + arg.Kind().String()) + } + } + sizeOfStack := maxArgs - numOfIntegerRegisters() + if stack > sizeOfStack { + panic("purego: too many arguments") + } + } + v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { + if len(args) > 0 { + if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok { + // subtract one from args bc the last argument in args is []interface{} + // which we are currently expanding + tmp := make([]reflect.Value, len(args)-1+len(variadic)) + n := copy(tmp, args[:len(args)-1]) + for i, v := range variadic { + tmp[n+i] = reflect.ValueOf(v) + } + args = tmp + } + } + var sysargs [maxArgs]uintptr + stack := sysargs[numOfIntegerRegisters():] + var floats [numOfFloats]uintptr + var numInts int + var numFloats int + var numStack int + var addStack, addInt, addFloat func(x uintptr) + if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { + // Windows arm64 uses the same calling convention as macOS and Linux + addStack = func(x uintptr) { + stack[numStack] = x + numStack++ + } + addInt = func(x uintptr) { + if numInts >= numOfIntegerRegisters() { + addStack(x) + } else { + sysargs[numInts] = x + numInts++ + } + } + addFloat = func(x uintptr) { + if numFloats < len(floats) { + floats[numFloats] = x + numFloats++ + } else { + addStack(x) + } + } + } else { + // On Windows amd64 the arguments are passed in the numbered registered. + // So the first int is in the first integer register and the first float + // is in the second floating register if there is already a first int. + // This is in contrast to how macOS and Linux pass arguments which + // tries to use as many registers as possible in the calling convention. + addStack = func(x uintptr) { + sysargs[numStack] = x + numStack++ + } + addInt = addStack + addFloat = addStack + } + + var keepAlive []interface{} + defer func() { + runtime.KeepAlive(keepAlive) + runtime.KeepAlive(args) + }() + for _, v := range args { + switch v.Kind() { + case reflect.String: + ptr := strings.CString(v.String()) + keepAlive = append(keepAlive, ptr) + addInt(uintptr(unsafe.Pointer(ptr))) + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + addInt(uintptr(v.Uint())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + addInt(uintptr(v.Int())) + case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: + // There is no need to keepAlive this pointer separately because it is kept alive in the args variable + addInt(v.Pointer()) + case reflect.Func: + addInt(NewCallback(v.Interface())) + case reflect.Bool: + if v.Bool() { + addInt(1) + } else { + addInt(0) + } + case reflect.Float32: + addFloat(uintptr(math.Float32bits(float32(v.Float())))) + case reflect.Float64: + addFloat(uintptr(math.Float64bits(v.Float()))) + case reflect.Struct: + keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive) + default: + panic("purego: unsupported kind: " + v.Kind().String()) + } + } + // TODO: support structs + var r1, r2 uintptr + if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { + // Use the normal arm64 calling convention even on Windows + syscall := syscall15Args{ + cfn, + sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], + sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], + sysargs[12], sysargs[13], sysargs[14], + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], + 0, 0, 0, + } + runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall)) + r1, r2 = syscall.r1, syscall.r2 + } else { + // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats + r1, r2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], + sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], + sysargs[12], sysargs[13], sysargs[14]) + } + if ty.NumOut() == 0 { + return nil + } + outType := ty.Out(0) + v := reflect.New(outType).Elem() + switch outType.Kind() { + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v.SetUint(uint64(r1)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v.SetInt(int64(r1)) + case reflect.Bool: + v.SetBool(byte(r1) != 0) + case reflect.UnsafePointer: + // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer + v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1))) + case reflect.Ptr: + // It is safe to have the address of r1 not escape because it is immediately dereferenced with .Elem() + v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&r1))).Elem() + case reflect.Func: + // wrap this C function in a nicely typed Go function + v = reflect.New(outType) + RegisterFunc(v.Interface(), r1) + case reflect.String: + v.SetString(strings.GoString(r1)) + case reflect.Float32: + // NOTE: r2 is only the floating return value on 64bit platforms. + // On 32bit platforms r2 is the upper part of a 64bit return. + v.SetFloat(float64(math.Float32frombits(uint32(r2)))) + case reflect.Float64: + // NOTE: r2 is only the floating return value on 64bit platforms. + // On 32bit platforms r2 is the upper part of a 64bit return. + v.SetFloat(math.Float64frombits(uint64(r2))) + default: + panic("purego: unsupported return kind: " + outType.Kind().String()) + } + return []reflect.Value{v} + }) + fn.Set(v) +} + +func roundUpTo8(val uintptr) uintptr { + return (val + 7) &^ 7 +} + +func numOfIntegerRegisters() int { + switch runtime.GOARCH { + case "arm64": + return 8 + case "amd64": + return 6 + // TODO: figure out why 386 tests are not working + /*case "386": + return 0 + case "arm": + return 4*/ + default: + panic("purego: unknown GOARCH (" + runtime.GOARCH + ")") + } +} diff --git a/vendor/github.com/ebitengine/purego/go_runtime.go b/vendor/github.com/ebitengine/purego/go_runtime.go new file mode 100644 index 000000000..9593dac90 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/go_runtime.go @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux || windows + +package purego + +import ( + "unsafe" +) + +//go:linkname runtime_cgocall runtime.cgocall +func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go + +//go:linkname runtime_noescape runtime.noescape +//go:noescape +func runtime_noescape(p unsafe.Pointer) unsafe.Pointer // from runtime/stubs.go diff --git a/vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go b/vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go new file mode 100644 index 000000000..7e4dd0437 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2024 The Ebitengine Authors + +//go:build freebsd || linux + +package cgo + +/* + #cgo LDFLAGS: -ldl + +#include +*/ +import "C" + +// all that is needed is to assign each dl function because then its +// symbol will then be made available to the linker and linked to inside dlfcn.go +var ( + _ = C.dlopen + _ = C.dlsym + _ = C.dlerror + _ = C.dlclose +) diff --git a/vendor/github.com/ebitengine/purego/internal/cgo/empty.go b/vendor/github.com/ebitengine/purego/internal/cgo/empty.go new file mode 100644 index 000000000..1d7cffe2a --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/cgo/empty.go @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2024 The Ebitengine Authors + +package cgo + +// Empty so that importing this package doesn't cause issue for certain platforms. diff --git a/vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go b/vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go new file mode 100644 index 000000000..029a2598b --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build freebsd || (linux && !(arm64 || amd64)) + +package cgo + +// this file is placed inside internal/cgo and not package purego +// because Cgo and assembly files can't be in the same package. + +/* + #cgo LDFLAGS: -ldl + +#include +#include +#include +#include + +typedef struct syscall15Args { + uintptr_t fn; + uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15; + uintptr_t f1, f2, f3, f4, f5, f6, f7, f8; + uintptr_t r1, r2, err; +} syscall15Args; + +void syscall15(struct syscall15Args *args) { + assert((args->f1|args->f2|args->f3|args->f4|args->f5|args->f6|args->f7|args->f8) == 0); + uintptr_t (*func_name)(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, + uintptr_t a7, uintptr_t a8, uintptr_t a9, uintptr_t a10, uintptr_t a11, uintptr_t a12, + uintptr_t a13, uintptr_t a14, uintptr_t a15); + *(void**)(&func_name) = (void*)(args->fn); + uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9, + args->a10,args->a11,args->a12,args->a13,args->a14,args->a15); + args->r1 = r1; + args->err = errno; +} + +*/ +import "C" +import "unsafe" + +// assign purego.syscall15XABI0 to the C version of this function. +var Syscall15XABI0 = unsafe.Pointer(C.syscall15) + +//go:nosplit +func Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { + args := C.syscall15Args{ + C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3), + C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6), + C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), C.uintptr_t(a10), C.uintptr_t(a11), C.uintptr_t(a12), + C.uintptr_t(a13), C.uintptr_t(a14), C.uintptr_t(a15), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + C.syscall15(&args) + return uintptr(args.r1), uintptr(args.r2), uintptr(args.err) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_amd64.h b/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_amd64.h new file mode 100644 index 000000000..9949435fe --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_amd64.h @@ -0,0 +1,99 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These save the frame pointer, so in general, functions that use +// these should have zero frame size to suppress the automatic frame +// pointer, though it's harmless to not do this. + +#ifdef GOOS_windows + +// REGS_HOST_TO_ABI0_STACK is the stack bytes used by +// PUSH_REGS_HOST_TO_ABI0. +#define REGS_HOST_TO_ABI0_STACK (28*8 + 8) + +// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from +// the host ABI to Go ABI0 code. It saves all registers that are +// callee-save in the host ABI and caller-save in Go ABI0 and prepares +// for entry to Go. +// +// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag. +// Clear the DF flag for the Go ABI. +// MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +#define PUSH_REGS_HOST_TO_ABI0() \ + PUSHFQ \ + CLD \ + ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \ + MOVQ DI, (0*0)(SP) \ + MOVQ SI, (1*8)(SP) \ + MOVQ BP, (2*8)(SP) \ + MOVQ BX, (3*8)(SP) \ + MOVQ R12, (4*8)(SP) \ + MOVQ R13, (5*8)(SP) \ + MOVQ R14, (6*8)(SP) \ + MOVQ R15, (7*8)(SP) \ + MOVUPS X6, (8*8)(SP) \ + MOVUPS X7, (10*8)(SP) \ + MOVUPS X8, (12*8)(SP) \ + MOVUPS X9, (14*8)(SP) \ + MOVUPS X10, (16*8)(SP) \ + MOVUPS X11, (18*8)(SP) \ + MOVUPS X12, (20*8)(SP) \ + MOVUPS X13, (22*8)(SP) \ + MOVUPS X14, (24*8)(SP) \ + MOVUPS X15, (26*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*0)(SP), DI \ + MOVQ (1*8)(SP), SI \ + MOVQ (2*8)(SP), BP \ + MOVQ (3*8)(SP), BX \ + MOVQ (4*8)(SP), R12 \ + MOVQ (5*8)(SP), R13 \ + MOVQ (6*8)(SP), R14 \ + MOVQ (7*8)(SP), R15 \ + MOVUPS (8*8)(SP), X6 \ + MOVUPS (10*8)(SP), X7 \ + MOVUPS (12*8)(SP), X8 \ + MOVUPS (14*8)(SP), X9 \ + MOVUPS (16*8)(SP), X10 \ + MOVUPS (18*8)(SP), X11 \ + MOVUPS (20*8)(SP), X12 \ + MOVUPS (22*8)(SP), X13 \ + MOVUPS (24*8)(SP), X14 \ + MOVUPS (26*8)(SP), X15 \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \ + POPFQ + +#else +// SysV ABI + +#define REGS_HOST_TO_ABI0_STACK (6*8) + +// SysV MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +// Both SysV and Go require DF to be cleared, so that's already clear. +// The SysV and Go frame pointer conventions are compatible. +#define PUSH_REGS_HOST_TO_ABI0() \ + ADJSP $(REGS_HOST_TO_ABI0_STACK) \ + MOVQ BP, (5*8)(SP) \ + LEAQ (5*8)(SP), BP \ + MOVQ BX, (0*8)(SP) \ + MOVQ R12, (1*8)(SP) \ + MOVQ R13, (2*8)(SP) \ + MOVQ R14, (3*8)(SP) \ + MOVQ R15, (4*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*8)(SP), BX \ + MOVQ (1*8)(SP), R12 \ + MOVQ (2*8)(SP), R13 \ + MOVQ (3*8)(SP), R14 \ + MOVQ (4*8)(SP), R15 \ + MOVQ (5*8)(SP), BP \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK) + +#endif diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_arm64.h b/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_arm64.h new file mode 100644 index 000000000..5d5061ec1 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_arm64.h @@ -0,0 +1,39 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP). +// +// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP). +// +// R29 is not saved because Go will save and restore it. + +#define SAVE_R19_TO_R28(offset) \ + STP (R19, R20), ((offset)+0*8)(RSP) \ + STP (R21, R22), ((offset)+2*8)(RSP) \ + STP (R23, R24), ((offset)+4*8)(RSP) \ + STP (R25, R26), ((offset)+6*8)(RSP) \ + STP (R27, g), ((offset)+8*8)(RSP) +#define RESTORE_R19_TO_R28(offset) \ + LDP ((offset)+0*8)(RSP), (R19, R20) \ + LDP ((offset)+2*8)(RSP), (R21, R22) \ + LDP ((offset)+4*8)(RSP), (R23, R24) \ + LDP ((offset)+6*8)(RSP), (R25, R26) \ + LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */ +#define SAVE_F8_TO_F15(offset) \ + FSTPD (F8, F9), ((offset)+0*8)(RSP) \ + FSTPD (F10, F11), ((offset)+2*8)(RSP) \ + FSTPD (F12, F13), ((offset)+4*8)(RSP) \ + FSTPD (F14, F15), ((offset)+6*8)(RSP) +#define RESTORE_F8_TO_F15(offset) \ + FLDPD ((offset)+0*8)(RSP), (F8, F9) \ + FLDPD ((offset)+2*8)(RSP), (F10, F11) \ + FLDPD ((offset)+4*8)(RSP), (F12, F13) \ + FLDPD ((offset)+6*8)(RSP), (F14, F15) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_amd64.s b/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_amd64.s new file mode 100644 index 000000000..2b7eb57f8 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_amd64.s @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" +#include "abi_amd64.h" + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +// This signature is known to SWIG, so we can't change it. +TEXT crosscall2(SB), NOSPLIT, $0-0 + PUSH_REGS_HOST_TO_ABI0() + + // Make room for arguments to cgocallback. + ADJSP $0x18 + +#ifndef GOOS_windows + MOVQ DI, 0x0(SP) // fn + MOVQ SI, 0x8(SP) // arg + + // Skip n in DX. + MOVQ CX, 0x10(SP) // ctxt + +#else + MOVQ CX, 0x0(SP) // fn + MOVQ DX, 0x8(SP) // arg + + // Skip n in R8. + MOVQ R9, 0x10(SP) // ctxt + +#endif + + CALL runtime·cgocallback(SB) + + ADJSP $-0x18 + POP_REGS_HOST_TO_ABI0() + RET diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_arm64.s b/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_arm64.s new file mode 100644 index 000000000..50e5261d9 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_arm64.s @@ -0,0 +1,36 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" +#include "abi_arm64.h" + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0 +/* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R0, R1, R3), skipping R2. + * Also note that at procedure entry in gc world, 8(RSP) will be the + * first arg. + */ + SUB $(8*24), RSP + STP (R0, R1), (8*1)(RSP) + MOVD R3, (8*3)(RSP) + + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) + STP (R29, R30), (8*22)(RSP) + + // Initialize Go ABI environment + BL runtime·load_g(SB) + BL runtime·cgocallback(SB) + + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + LDP (8*22)(RSP), (R29, R30) + + ADD $(8*24), RSP + RET diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go new file mode 100644 index 000000000..f6a079a7c --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go @@ -0,0 +1,93 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || linux + +package fakecgo + +import ( + _ "unsafe" +) + +// TODO: decide if we need _runtime_cgo_panic_internal + +//go:linkname x_cgo_init_trampoline x_cgo_init_trampoline +//go:linkname _cgo_init _cgo_init +var x_cgo_init_trampoline byte +var _cgo_init = &x_cgo_init_trampoline + +// Creates a new system thread without updating any Go state. +// +// This method is invoked during shared library loading to create a new OS +// thread to perform the runtime initialization. This method is similar to +// _cgo_sys_thread_start except that it doesn't update any Go state. + +//go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline +//go:linkname _cgo_thread_start _cgo_thread_start +var x_cgo_thread_start_trampoline byte +var _cgo_thread_start = &x_cgo_thread_start_trampoline + +// Notifies that the runtime has been initialized. +// +// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done) +// to ensure that the runtime has been initialized before the CGO call is +// executed. This is necessary for shared libraries where we kickoff runtime +// initialization in a separate thread and return without waiting for this +// thread to complete the init. + +//go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline +//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done +var x_cgo_notify_runtime_init_done_trampoline byte +var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline + +// Indicates whether a dummy thread key has been created or not. +// +// When calling go exported function from C, we register a destructor +// callback, for a dummy thread key, by using pthread_key_create. + +//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created +var x_cgo_pthread_key_created uintptr +var _cgo_pthread_key_created = &x_cgo_pthread_key_created + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's for the runtime package to call at init time. +func set_crosscall2() { + // nothing needs to be done here for fakecgo + // because it's possible to just call cgocallback directly +} + +//go:linkname _set_crosscall2 runtime.set_crosscall2 +var _set_crosscall2 = set_crosscall2 + +// Store the g into the thread-specific value. +// So that pthread_key_destructor will dropm when the thread is exiting. + +//go:linkname x_cgo_bindm_trampoline x_cgo_bindm_trampoline +//go:linkname _cgo_bindm _cgo_bindm +var x_cgo_bindm_trampoline byte +var _cgo_bindm = &x_cgo_bindm_trampoline + +// TODO: decide if we need x_cgo_set_context_function +// TODO: decide if we need _cgo_yield + +var ( + // In Go 1.20 the race detector was rewritten to pure Go + // on darwin. This means that when CGO_ENABLED=0 is set + // fakecgo is built with race detector code. This is not + // good since this code is pretending to be C. The go:norace + // pragma is not enough, since it only applies to the native + // ABIInternal function. The ABIO wrapper (which is necessary, + // since all references to text symbols from assembly will use it) + // does not inherit the go:norace pragma, so it will still be + // instrumented by the race detector. + // + // To circumvent this issue, using closure calls in the + // assembly, which forces the compiler to use the ABIInternal + // native implementation (which has go:norace) instead. + threadentry_call = threadentry + x_cgo_init_call = x_cgo_init + x_cgo_setenv_call = x_cgo_setenv + x_cgo_unsetenv_call = x_cgo_unsetenv + x_cgo_thread_start_call = x_cgo_thread_start +) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go new file mode 100644 index 000000000..efbe4212e --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd + +// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go. +// This allows code that calls into C to function properly when CGO_ENABLED=0. +// +// # Goals +// +// fakecgo attempts to replicate the same naming structure as in the runtime. +// For example, functions that have the prefix "gcc_*" are named "go_*". +// This makes it easier to port other GOOSs and GOARCHs as well as to keep +// it in sync with runtime/cgo. +// +// # Support +// +// Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot +// be used with -buildmode=c-archive because that requires special initialization +// that fakecgo does not implement at the moment. +// +// # Usage +// +// Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then +// set the environment variable CGO_ENABLED=0. +// The recommended usage for fakecgo is to prefer using runtime/cgo if possible +// but if cross-compiling or fast build times are important fakecgo is available. +// Purego will pick which ever Cgo runtime is available and prefer the one that +// comes with Go (runtime/cgo). +package fakecgo + +//go:generate go run gen.go +//go:generate gofmt -s -w symbols.go diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/freebsd.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/freebsd.go new file mode 100644 index 000000000..bb73a709e --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/freebsd.go @@ -0,0 +1,27 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build freebsd && !cgo + +package fakecgo + +import _ "unsafe" // for go:linkname + +// Supply environ and __progname, because we don't +// link against the standard FreeBSD crt0.o and the +// libc dynamic library needs them. + +// Note: when building with cross-compiling or CGO_ENABLED=0, add +// the following argument to `go` so that these symbols are defined by +// making fakecgo the Cgo. +// -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" + +//go:linkname _environ environ +//go:linkname _progname __progname + +//go:cgo_export_dynamic environ +//go:cgo_export_dynamic __progname + +var _environ uintptr +var _progname uintptr diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go new file mode 100644 index 000000000..fb3a3f7f0 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go @@ -0,0 +1,71 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +//go:norace +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + size = pthread_get_stacksize_np(pthread_self()) + pthread_attr_init(&attr) + pthread_attr_setstacksize(&attr, size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +//go:norace +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +//go:nosplit +//go:norace +func x_cgo_init(g *G, setg uintptr) { + var size size_t + + setg_func = setg + + size = pthread_get_stacksize_np(pthread_self()) + g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096)) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go new file mode 100644 index 000000000..b000b3fbf --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go @@ -0,0 +1,86 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +//go:norace +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + size = pthread_get_stacksize_np(pthread_self()) + pthread_attr_init(&attr) + pthread_attr_setstacksize(&attr, size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +//go:norace +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + // TODO: support ios + //#if TARGET_OS_IPHONE + // darwin_arm_init_thread_exception_port(); + //#endif + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) +// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us +// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup +// This function can't be go:systemstack since go is not in a state where the systemcheck would work. +// +//go:nosplit +//go:norace +func x_cgo_init(g *G, setg uintptr) { + var size size_t + + setg_func = setg + size = pthread_get_stacksize_np(pthread_self()) + g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096)) + + //TODO: support ios + //#if TARGET_OS_IPHONE + // darwin_arm_init_mach_exception_handler(); + // darwin_arm_init_thread_exception_port(); + // init_working_dir(); + //#endif +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go new file mode 100644 index 000000000..9aa57ef66 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go @@ -0,0 +1,93 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + pthread_attr_init(&attr) + pthread_attr_getstacksize(&attr, &size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +//go:nosplit +func x_cgo_init(g *G, setg uintptr) { + var size size_t + var attr *pthread_attr_t + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_func = setg + attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr))) + if attr == nil { + println("fakecgo: malloc failed") + abort() + } + pthread_attr_init(attr) + pthread_attr_getstacksize(attr, &size) + // runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))` + // but this should be OK since we are taking the address of the first variable in this function. + g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 + pthread_attr_destroy(attr) + free(unsafe.Pointer(attr)) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go new file mode 100644 index 000000000..1db518e3a --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go @@ -0,0 +1,96 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + pthread_attr_init(&attr) + pthread_attr_getstacksize(&attr, &size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) +// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us +// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup +// This function can't be go:systemstack since go is not in a state where the systemcheck would work. +// +//go:nosplit +func x_cgo_init(g *G, setg uintptr) { + var size size_t + var attr *pthread_attr_t + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_func = setg + attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr))) + if attr == nil { + println("fakecgo: malloc failed") + abort() + } + pthread_attr_init(attr) + pthread_attr_getstacksize(attr, &size) + g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 + pthread_attr_destroy(attr) + free(unsafe.Pointer(attr)) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go new file mode 100644 index 000000000..71da11281 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package fakecgo + +import ( + "syscall" + "unsafe" +) + +var ( + pthread_g pthread_key_t + + runtime_init_cond = PTHREAD_COND_INITIALIZER + runtime_init_mu = PTHREAD_MUTEX_INITIALIZER + runtime_init_done int +) + +//go:nosplit +func x_cgo_notify_runtime_init_done() { + pthread_mutex_lock(&runtime_init_mu) + runtime_init_done = 1 + pthread_cond_broadcast(&runtime_init_cond) + pthread_mutex_unlock(&runtime_init_mu) +} + +// Store the g into a thread-specific value associated with the pthread key pthread_g. +// And pthread_key_destructor will dropm when the thread is exiting. +func x_cgo_bindm(g unsafe.Pointer) { + // We assume this will always succeed, otherwise, there might be extra M leaking, + // when a C thread exits after a cgo call. + // We only invoke this function once per thread in runtime.needAndBindM, + // and the next calls just reuse the bound m. + pthread_setspecific(pthread_g, g) +} + +// _cgo_try_pthread_create retries pthread_create if it fails with +// EAGAIN. +// +//go:nosplit +//go:norace +func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int { + var ts syscall.Timespec + // tries needs to be the same type as syscall.Timespec.Nsec + // but the fields are int32 on 32bit and int64 on 64bit. + // tries is assigned to syscall.Timespec.Nsec in order to match its type. + tries := ts.Nsec + var err int + + for tries = 0; tries < 20; tries++ { + err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg))) + if err == 0 { + pthread_detach(*thread) + return 0 + } + if err != int(syscall.EAGAIN) { + return err + } + ts.Sec = 0 + ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds. + nanosleep(&ts, nil) + } + return int(syscall.EAGAIN) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go new file mode 100644 index 000000000..9aa57ef66 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go @@ -0,0 +1,93 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + pthread_attr_init(&attr) + pthread_attr_getstacksize(&attr, &size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +//go:nosplit +func x_cgo_init(g *G, setg uintptr) { + var size size_t + var attr *pthread_attr_t + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_func = setg + attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr))) + if attr == nil { + println("fakecgo: malloc failed") + abort() + } + pthread_attr_init(attr) + pthread_attr_getstacksize(attr, &size) + // runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))` + // but this should be OK since we are taking the address of the first variable in this function. + g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 + pthread_attr_destroy(attr) + free(unsafe.Pointer(attr)) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go new file mode 100644 index 000000000..1db518e3a --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go @@ -0,0 +1,96 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakecgo + +import "unsafe" + +//go:nosplit +func _cgo_sys_thread_start(ts *ThreadStart) { + var attr pthread_attr_t + var ign, oset sigset_t + var p pthread_t + var size size_t + var err int + + //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug + sigfillset(&ign) + pthread_sigmask(SIG_SETMASK, &ign, &oset) + + pthread_attr_init(&attr) + pthread_attr_getstacksize(&attr, &size) + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts.g.stackhi = uintptr(size) + + err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) + + pthread_sigmask(SIG_SETMASK, &oset, nil) + + if err != 0 { + print("fakecgo: pthread_create failed: ") + println(err) + abort() + } +} + +// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function +// +//go:linkname x_threadentry_trampoline threadentry_trampoline +var x_threadentry_trampoline byte +var threadentry_trampolineABI0 = &x_threadentry_trampoline + +//go:nosplit +func threadentry(v unsafe.Pointer) unsafe.Pointer { + ts := *(*ThreadStart)(v) + free(v) + + setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) + + // faking funcs in go is a bit a... involved - but the following works :) + fn := uintptr(unsafe.Pointer(&ts.fn)) + (*(*func())(unsafe.Pointer(&fn)))() + + return nil +} + +// here we will store a pointer to the provided setg func +var setg_func uintptr + +// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) +// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us +// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup +// This function can't be go:systemstack since go is not in a state where the systemcheck would work. +// +//go:nosplit +func x_cgo_init(g *G, setg uintptr) { + var size size_t + var attr *pthread_attr_t + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_func = setg + attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr))) + if attr == nil { + println("fakecgo: malloc failed") + abort() + } + pthread_attr_init(attr) + pthread_attr_getstacksize(attr, &size) + g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 + pthread_attr_destroy(attr) + free(unsafe.Pointer(attr)) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go new file mode 100644 index 000000000..818372ea0 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package fakecgo + +//go:nosplit +//go:norace +func x_cgo_setenv(arg *[2]*byte) { + setenv(arg[0], arg[1], 1) +} + +//go:nosplit +//go:norace +func x_cgo_unsetenv(arg *[1]*byte) { + unsetenv(arg[0]) +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go new file mode 100644 index 000000000..7a43b42b6 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package fakecgo + +import "unsafe" + +// _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling) + +// _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c) +// This get's called instead of the go code for creating new threads +// -> pthread_* stuff is used, so threads are setup correctly for C +// If this is missing, TLS is only setup correctly on thread 1! +// This function should be go:systemstack instead of go:nosplit (but that requires runtime) +// +//go:nosplit +//go:norace +func x_cgo_thread_start(arg *ThreadStart) { + var ts *ThreadStart + // Make our own copy that can persist after we return. + // _cgo_tsan_acquire(); + ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts))) + // _cgo_tsan_release(); + if ts == nil { + println("fakecgo: out of memory in thread_start") + abort() + } + // *ts = *arg would cause a writebarrier so use memmove instead + memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts)) + _cgo_sys_thread_start(ts) // OS-dependent half +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go new file mode 100644 index 000000000..ce17d1828 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go @@ -0,0 +1,19 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || linux + +// The runtime package contains an uninitialized definition +// for runtime·iscgo. Override it to tell the runtime we're here. +// There are various function pointers that should be set too, +// but those depend on dynamic linker magic to get initialized +// correctly, and sometimes they break. This variable is a +// backup: it depends only on old C style static linking rules. + +package fakecgo + +import _ "unsafe" // for go:linkname + +//go:linkname _iscgo runtime.iscgo +var _iscgo bool = true diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go new file mode 100644 index 000000000..c12d403c3 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package fakecgo + +type ( + size_t uintptr + sigset_t [128]byte + pthread_attr_t [64]byte + pthread_t int + pthread_key_t uint64 +) + +// for pthread_sigmask: + +type sighow int32 + +const ( + SIG_BLOCK sighow = 0 + SIG_UNBLOCK sighow = 1 + SIG_SETMASK sighow = 2 +) + +type G struct { + stacklo uintptr + stackhi uintptr +} + +type ThreadStart struct { + g *G + tls *uintptr + fn uintptr +} diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go new file mode 100644 index 000000000..03c917183 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +type ( + pthread_mutex_t struct { + sig int64 + opaque [56]byte + } + pthread_cond_t struct { + sig int64 + opaque [40]byte + } +) + +var ( + PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB} + PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7} +) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go new file mode 100644 index 000000000..baf03fa85 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +type ( + pthread_cond_t uintptr + pthread_mutex_t uintptr +) + +var ( + PTHREAD_COND_INITIALIZER = pthread_cond_t(0) + PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0) +) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go new file mode 100644 index 000000000..93aa5b26e --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +type ( + pthread_cond_t [48]byte + pthread_mutex_t [48]byte +) + +var ( + PTHREAD_COND_INITIALIZER = pthread_cond_t{} + PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{} +) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go new file mode 100644 index 000000000..b69d4b39f --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go @@ -0,0 +1,19 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || linux + +package fakecgo + +import _ "unsafe" // for go:linkname + +//go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline +//go:linkname _cgo_setenv runtime._cgo_setenv +var x_cgo_setenv_trampoline byte +var _cgo_setenv = &x_cgo_setenv_trampoline + +//go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline +//go:linkname _cgo_unsetenv runtime._cgo_unsetenv +var x_cgo_unsetenv_trampoline byte +var _cgo_unsetenv = &x_cgo_unsetenv_trampoline diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go new file mode 100644 index 000000000..d7401f705 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go @@ -0,0 +1,184 @@ +// Code generated by 'go generate' with gen.go. DO NOT EDIT. + +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +package fakecgo + +import ( + "syscall" + "unsafe" +) + +// setg_trampoline calls setg with the G provided +func setg_trampoline(setg uintptr, G uintptr) + +//go:linkname memmove runtime.memmove +func memmove(to, from unsafe.Pointer, n uintptr) + +// call5 takes fn the C function and 5 arguments and calls the function with those arguments +func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr + +func malloc(size uintptr) unsafe.Pointer { + ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0) + // this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer + return *(*unsafe.Pointer)(unsafe.Pointer(&ret)) +} + +func free(ptr unsafe.Pointer) { + call5(freeABI0, uintptr(ptr), 0, 0, 0, 0) +} + +func setenv(name *byte, value *byte, overwrite int32) int32 { + return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0)) +} + +func unsetenv(name *byte) int32 { + return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0)) +} + +func sigfillset(set *sigset_t) int32 { + return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0)) +} + +func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 { + return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0)) +} + +func abort() { + call5(abortABI0, 0, 0, 0, 0, 0) +} + +func pthread_attr_init(attr *pthread_attr_t) int32 { + return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0)) +} + +func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 { + return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0)) +} + +func pthread_detach(thread pthread_t) int32 { + return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0)) +} + +func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 { + return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0)) +} + +func pthread_self() pthread_t { + return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0)) +} + +func pthread_get_stacksize_np(thread pthread_t) size_t { + return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0)) +} + +func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 { + return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0)) +} + +func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 { + return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0)) +} + +func pthread_attr_destroy(attr *pthread_attr_t) int32 { + return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0)) +} + +func pthread_mutex_lock(mutex *pthread_mutex_t) int32 { + return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0)) +} + +func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 { + return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0)) +} + +func pthread_cond_broadcast(cond *pthread_cond_t) int32 { + return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0)) +} + +func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 { + return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0)) +} + +//go:linkname _malloc _malloc +var _malloc uintptr +var mallocABI0 = uintptr(unsafe.Pointer(&_malloc)) + +//go:linkname _free _free +var _free uintptr +var freeABI0 = uintptr(unsafe.Pointer(&_free)) + +//go:linkname _setenv _setenv +var _setenv uintptr +var setenvABI0 = uintptr(unsafe.Pointer(&_setenv)) + +//go:linkname _unsetenv _unsetenv +var _unsetenv uintptr +var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv)) + +//go:linkname _sigfillset _sigfillset +var _sigfillset uintptr +var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset)) + +//go:linkname _nanosleep _nanosleep +var _nanosleep uintptr +var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep)) + +//go:linkname _abort _abort +var _abort uintptr +var abortABI0 = uintptr(unsafe.Pointer(&_abort)) + +//go:linkname _pthread_attr_init _pthread_attr_init +var _pthread_attr_init uintptr +var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init)) + +//go:linkname _pthread_create _pthread_create +var _pthread_create uintptr +var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create)) + +//go:linkname _pthread_detach _pthread_detach +var _pthread_detach uintptr +var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach)) + +//go:linkname _pthread_sigmask _pthread_sigmask +var _pthread_sigmask uintptr +var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask)) + +//go:linkname _pthread_self _pthread_self +var _pthread_self uintptr +var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self)) + +//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np +var _pthread_get_stacksize_np uintptr +var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np)) + +//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize +var _pthread_attr_getstacksize uintptr +var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize)) + +//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize +var _pthread_attr_setstacksize uintptr +var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize)) + +//go:linkname _pthread_attr_destroy _pthread_attr_destroy +var _pthread_attr_destroy uintptr +var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy)) + +//go:linkname _pthread_mutex_lock _pthread_mutex_lock +var _pthread_mutex_lock uintptr +var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock)) + +//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock +var _pthread_mutex_unlock uintptr +var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock)) + +//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast +var _pthread_cond_broadcast uintptr +var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast)) + +//go:linkname _pthread_setspecific _pthread_setspecific +var _pthread_setspecific uintptr +var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific)) diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go new file mode 100644 index 000000000..7341fecd4 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go @@ -0,0 +1,27 @@ +// Code generated by 'go generate' with gen.go. DO NOT EDIT. + +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_free free "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_setenv setenv "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_unsetenv unsetenv "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_self pthread_self "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "/usr/lib/libSystem.B.dylib" diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go new file mode 100644 index 000000000..bff096d51 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go @@ -0,0 +1,27 @@ +// Code generated by 'go generate' with gen.go. DO NOT EDIT. + +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +//go:cgo_import_dynamic purego_malloc malloc "libc.so.7" +//go:cgo_import_dynamic purego_free free "libc.so.7" +//go:cgo_import_dynamic purego_setenv setenv "libc.so.7" +//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.7" +//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7" +//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7" +//go:cgo_import_dynamic purego_abort abort "libc.so.7" +//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so" +//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so" +//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so" +//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so" +//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so" +//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so" +//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so" +//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so" +//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so" +//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so" +//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so" +//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so" +//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so" diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go new file mode 100644 index 000000000..ee3ab7aaf --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go @@ -0,0 +1,27 @@ +// Code generated by 'go generate' with gen.go. DO NOT EDIT. + +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package fakecgo + +//go:cgo_import_dynamic purego_malloc malloc "libc.so.6" +//go:cgo_import_dynamic purego_free free "libc.so.6" +//go:cgo_import_dynamic purego_setenv setenv "libc.so.6" +//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.6" +//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6" +//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6" +//go:cgo_import_dynamic purego_abort abort "libc.so.6" +//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so.0" +//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so.0" diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s new file mode 100644 index 000000000..24b620606 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || linux || freebsd + +/* +trampoline for emulating required C functions for cgo in go (see cgo.go) +(we convert cdecl calling convention to go and vice-versa) + +Since we're called from go and call into C we can cheat a bit with the calling conventions: + - in go all the registers are caller saved + - in C we have a couple of callee saved registers + +=> we can use BX, R12, R13, R14, R15 instead of the stack + +C Calling convention cdecl used here (we only need integer args): +1. arg: DI +2. arg: SI +3. arg: DX +4. arg: CX +5. arg: R8 +6. arg: R9 +We don't need floats with these functions -> AX=0 +return value will be in AX +*/ +#include "textflag.h" +#include "go_asm.h" + +// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. + +TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16 + MOVQ DI, AX + MOVQ SI, BX + MOVQ ·x_cgo_init_call(SB), DX + MOVQ (DX), CX + CALL CX + RET + +TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8 + MOVQ DI, AX + MOVQ ·x_cgo_thread_start_call(SB), DX + MOVQ (DX), CX + CALL CX + RET + +TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8 + MOVQ DI, AX + MOVQ ·x_cgo_setenv_call(SB), DX + MOVQ (DX), CX + CALL CX + RET + +TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8 + MOVQ DI, AX + MOVQ ·x_cgo_unsetenv_call(SB), DX + MOVQ (DX), CX + CALL CX + RET + +TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0 + CALL ·x_cgo_notify_runtime_init_done(SB) + RET + +TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0 + CALL ·x_cgo_bindm(SB) + RET + +// func setg_trampoline(setg uintptr, g uintptr) +TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 + MOVQ G+8(FP), DI + MOVQ setg+0(FP), BX + XORL AX, AX + CALL BX + RET + +TEXT threadentry_trampoline(SB), NOSPLIT, $16 + MOVQ DI, AX + MOVQ ·threadentry_call(SB), DX + MOVQ (DX), CX + CALL CX + RET + +TEXT ·call5(SB), NOSPLIT, $0-56 + MOVQ fn+0(FP), BX + MOVQ a1+8(FP), DI + MOVQ a2+16(FP), SI + MOVQ a3+24(FP), DX + MOVQ a4+32(FP), CX + MOVQ a5+40(FP), R8 + + XORL AX, AX // no floats + + PUSHQ BP // save BP + MOVQ SP, BP // save SP inside BP bc BP is callee-saved + SUBQ $16, SP // allocate space for alignment + ANDQ $-16, SP // align on 16 bytes for SSE + + CALL BX + + MOVQ BP, SP // get SP back + POPQ BP // restore BP + + MOVQ AX, ret+48(FP) + RET diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s new file mode 100644 index 000000000..9c80fe2f6 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +#include "textflag.h" +#include "go_asm.h" + +// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. + +TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0 + MOVD R0, 8(RSP) + MOVD R1, 16(RSP) + MOVD ·x_cgo_init_call(SB), R26 + MOVD (R26), R2 + CALL (R2) + RET + +TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0 + MOVD R0, 8(RSP) + MOVD ·x_cgo_thread_start_call(SB), R26 + MOVD (R26), R2 + CALL (R2) + RET + +TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0 + MOVD R0, 8(RSP) + MOVD ·x_cgo_setenv_call(SB), R26 + MOVD (R26), R2 + CALL (R2) + RET + +TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0 + MOVD R0, 8(RSP) + MOVD ·x_cgo_unsetenv_call(SB), R26 + MOVD (R26), R2 + CALL (R2) + RET + +TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0 + CALL ·x_cgo_notify_runtime_init_done(SB) + RET + +TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0 + CALL ·x_cgo_bindm(SB) + RET + +// func setg_trampoline(setg uintptr, g uintptr) +TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 + MOVD G+8(FP), R0 + MOVD setg+0(FP), R1 + CALL R1 + RET + +TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 + MOVD R0, 8(RSP) + MOVD ·threadentry_call(SB), R26 + MOVD (R26), R2 + CALL (R2) + MOVD $0, R0 // TODO: get the return value from threadentry + RET + +TEXT ·call5(SB), NOSPLIT, $0-0 + MOVD fn+0(FP), R6 + MOVD a1+8(FP), R0 + MOVD a2+16(FP), R1 + MOVD a3+24(FP), R2 + MOVD a4+32(FP), R3 + MOVD a5+40(FP), R4 + CALL R6 + MOVD R0, ret+48(FP) + RET diff --git a/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s new file mode 100644 index 000000000..e8726376d --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s @@ -0,0 +1,90 @@ +// Code generated by 'go generate' with gen.go. DO NOT EDIT. + +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +#include "textflag.h" + +// these stubs are here because it is not possible to go:linkname directly the C functions on darwin arm64 + +TEXT _malloc(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_malloc(SB) + RET + +TEXT _free(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_free(SB) + RET + +TEXT _setenv(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_setenv(SB) + RET + +TEXT _unsetenv(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_unsetenv(SB) + RET + +TEXT _sigfillset(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_sigfillset(SB) + RET + +TEXT _nanosleep(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_nanosleep(SB) + RET + +TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_abort(SB) + RET + +TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_attr_init(SB) + RET + +TEXT _pthread_create(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_create(SB) + RET + +TEXT _pthread_detach(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_detach(SB) + RET + +TEXT _pthread_sigmask(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_sigmask(SB) + RET + +TEXT _pthread_self(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_self(SB) + RET + +TEXT _pthread_get_stacksize_np(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_get_stacksize_np(SB) + RET + +TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_attr_getstacksize(SB) + RET + +TEXT _pthread_attr_setstacksize(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_attr_setstacksize(SB) + RET + +TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_attr_destroy(SB) + RET + +TEXT _pthread_mutex_lock(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_mutex_lock(SB) + RET + +TEXT _pthread_mutex_unlock(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_mutex_unlock(SB) + RET + +TEXT _pthread_cond_broadcast(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_cond_broadcast(SB) + RET + +TEXT _pthread_setspecific(SB), NOSPLIT|NOFRAME, $0-0 + JMP purego_pthread_setspecific(SB) + RET diff --git a/vendor/github.com/ebitengine/purego/internal/strings/strings.go b/vendor/github.com/ebitengine/purego/internal/strings/strings.go new file mode 100644 index 000000000..5b0d25225 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/internal/strings/strings.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package strings + +import ( + "unsafe" +) + +// hasSuffix tests whether the string s ends with suffix. +func hasSuffix(s, suffix string) bool { + return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix +} + +// CString converts a go string to *byte that can be passed to C code. +func CString(name string) *byte { + if hasSuffix(name, "\x00") { + return &(*(*[]byte)(unsafe.Pointer(&name)))[0] + } + b := make([]byte, len(name)+1) + copy(b, name) + return &b[0] +} + +// GoString copies a null-terminated char* to a Go string. +func GoString(c uintptr) string { + // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer + ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c)) + if ptr == nil { + return "" + } + var length int + for { + if *(*byte)(unsafe.Add(ptr, uintptr(length))) == '\x00' { + break + } + length++ + } + return string(unsafe.Slice((*byte)(ptr), length)) +} diff --git a/vendor/github.com/ebitengine/purego/is_ios.go b/vendor/github.com/ebitengine/purego/is_ios.go new file mode 100644 index 000000000..ed31da978 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/is_ios.go @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build !cgo + +package purego + +// if you are getting this error it means that you have +// CGO_ENABLED=0 while trying to build for ios. +// purego does not support this mode yet. +// the fix is to set CGO_ENABLED=1 which will require +// a C compiler. +var _ = _PUREGO_REQUIRES_CGO_ON_IOS diff --git a/vendor/github.com/ebitengine/purego/nocgo.go b/vendor/github.com/ebitengine/purego/nocgo.go new file mode 100644 index 000000000..5b989ea81 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/nocgo.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build !cgo && (darwin || freebsd || linux) + +package purego + +// if CGO_ENABLED=0 import fakecgo to setup the Cgo runtime correctly. +// This is required since some frameworks need TLS setup the C way which Go doesn't do. +// We currently don't support ios in fakecgo mode so force Cgo or fail +// +// The way that the Cgo runtime (runtime/cgo) works is by setting some variables found +// in runtime with non-null GCC compiled functions. The variables that are replaced are +// var ( +// iscgo bool // in runtime/cgo.go +// _cgo_init unsafe.Pointer // in runtime/cgo.go +// _cgo_thread_start unsafe.Pointer // in runtime/cgo.go +// _cgo_notify_runtime_init_done unsafe.Pointer // in runtime/cgo.go +// _cgo_setenv unsafe.Pointer // in runtime/env_posix.go +// _cgo_unsetenv unsafe.Pointer // in runtime/env_posix.go +// ) +// importing fakecgo will set these (using //go:linkname) with functions written +// entirely in Go (except for some assembly trampolines to change GCC ABI to Go ABI). +// Doing so makes it possible to build applications that call into C without CGO_ENABLED=1. +import _ "github.com/ebitengine/purego/internal/fakecgo" diff --git a/vendor/github.com/ebitengine/purego/struct_amd64.go b/vendor/github.com/ebitengine/purego/struct_amd64.go new file mode 100644 index 000000000..ec9397292 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/struct_amd64.go @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2024 The Ebitengine Authors + +package purego + +import ( + "math" + "reflect" +) + +// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf +// https://gitlab.com/x86-psABIs/x86-64-ABI +// Class determines where the 8 byte value goes. +// Higher value classes win over lower value classes +const ( + _NO_CLASS = 0b0000 + _SSE = 0b0001 + _X87 = 0b0011 // long double not used in Go + _INTEGER = 0b0111 + _MEMORY = 0b1111 +) + +func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { + if v.Type().Size() == 0 { + return keepAlive + } + + // if greater than 64 bytes place on stack + if v.Type().Size() > 8*8 { + placeStack(v, addStack) + return keepAlive + } + var ( + savedNumFloats = *numFloats + savedNumInts = *numInts + savedNumStack = *numStack + ) + placeOnStack := postMerger(v.Type()) || !tryPlaceRegister(v, addFloat, addInt) + if placeOnStack { + // reset any values placed in registers + *numFloats = savedNumFloats + *numInts = savedNumInts + *numStack = savedNumStack + placeStack(v, addStack) + } + return keepAlive +} + +func postMerger(t reflect.Type) bool { + // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other + // eightbyte isn’t SSEUP, the whole argument is passed in memory. + if t.Kind() != reflect.Struct { + return false + } + if t.Size() <= 2*8 { + return false + } + first := getFirst(t).Kind() + if first != reflect.Float32 && first != reflect.Float64 { + return false + } + return true +} + +func getFirst(t reflect.Type) reflect.Type { + first := t.Field(0).Type + if first.Kind() == reflect.Struct { + return getFirst(first) + } + return first +} + +func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) { + ok = true + var val uint64 + var shift byte // # of bits to shift + var flushed bool + class := _NO_CLASS + var place func(v reflect.Value) + place = func(v reflect.Value) { + var numFields int + if v.Kind() == reflect.Struct { + numFields = v.Type().NumField() + } else { + numFields = v.Type().Len() + } + + for i := 0; i < numFields; i++ { + flushed = false + var f reflect.Value + if v.Kind() == reflect.Struct { + f = v.Field(i) + } else { + f = v.Index(i) + } + if shift >= 64 { + shift = 0 + flushed = true + if class == _SSE { + addFloat(uintptr(val)) + } else { + addInt(uintptr(val)) + } + class = _NO_CLASS + } + switch f.Kind() { + case reflect.Struct: + place(f) + case reflect.Bool: + if f.Bool() { + val |= 1 + } + shift += 8 + class |= _INTEGER + case reflect.Pointer: + ok = false + return + case reflect.Int8: + val |= uint64(f.Int()&0xFF) << shift + shift += 8 + class |= _INTEGER + case reflect.Int16: + val |= uint64(f.Int()&0xFFFF) << shift + shift += 16 + class |= _INTEGER + case reflect.Int32: + val |= uint64(f.Int()&0xFFFF_FFFF) << shift + shift += 32 + class |= _INTEGER + case reflect.Int64: + addInt(uintptr(f.Int())) + shift = 0 + class = _NO_CLASS + flushed = true + case reflect.Uint8: + val |= f.Uint() << shift + shift += 8 + class |= _INTEGER + case reflect.Uint16: + val |= f.Uint() << shift + shift += 16 + class |= _INTEGER + case reflect.Uint32: + val |= f.Uint() << shift + shift += 32 + class |= _INTEGER + case reflect.Uint64: + addInt(uintptr(f.Uint())) + shift = 0 + class = _NO_CLASS + flushed = true + case reflect.Float32: + val |= uint64(math.Float32bits(float32(f.Float()))) << shift + shift += 32 + class |= _SSE + case reflect.Float64: + if v.Type().Size() > 16 { + ok = false + return + } + addFloat(uintptr(math.Float64bits(f.Float()))) + class = _NO_CLASS + flushed = true + case reflect.Array: + place(f) + default: + panic("purego: unsupported kind " + f.Kind().String()) + } + } + } + + place(v) + if !flushed { + if class == _SSE { + addFloat(uintptr(val)) + } else { + addInt(uintptr(val)) + } + } + return ok +} + +func placeStack(v reflect.Value, addStack func(uintptr)) { + for i := 0; i < v.Type().NumField(); i++ { + f := v.Field(i) + switch f.Kind() { + case reflect.Pointer: + addStack(f.Pointer()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + addStack(uintptr(f.Int())) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + addStack(uintptr(f.Uint())) + case reflect.Float32: + addStack(uintptr(math.Float32bits(float32(f.Float())))) + case reflect.Float64: + addStack(uintptr(math.Float64bits(f.Float()))) + case reflect.Struct: + placeStack(f, addStack) + default: + panic("purego: unsupported kind " + f.Kind().String()) + } + } +} diff --git a/vendor/github.com/ebitengine/purego/struct_arm64.go b/vendor/github.com/ebitengine/purego/struct_arm64.go new file mode 100644 index 000000000..32c14d43a --- /dev/null +++ b/vendor/github.com/ebitengine/purego/struct_arm64.go @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2024 The Ebitengine Authors + +package purego + +import ( + "math" + "reflect" +) + +// https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst +const ( + _NO_CLASS = 0b00 + _FLOAT = 0b01 + _INT = 0b11 +) + +func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { + if v.Type().Size() == 0 { + return keepAlive + } + + if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 { + // if this doesn't fit entirely in registers then + // each element goes onto the stack + if hfa && *numFloats+v.NumField() > numOfFloats { + *numFloats = numOfFloats + } else if hva && *numInts+v.NumField() > numOfIntegerRegisters() { + *numInts = numOfIntegerRegisters() + } + + placeRegisters(v, addFloat, addInt) + } else { + keepAlive = placeStack(v, keepAlive, addInt) + } + return keepAlive // the struct was allocated so don't panic +} + +func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) { + var val uint64 + var shift byte + var flushed bool + class := _NO_CLASS + var place func(v reflect.Value) + place = func(v reflect.Value) { + var numFields int + if v.Kind() == reflect.Struct { + numFields = v.Type().NumField() + } else { + numFields = v.Type().Len() + } + for k := 0; k < numFields; k++ { + flushed = false + var f reflect.Value + if v.Kind() == reflect.Struct { + f = v.Field(k) + } else { + f = v.Index(k) + } + if shift >= 64 { + shift = 0 + flushed = true + if class == _FLOAT { + addFloat(uintptr(val)) + } else { + addInt(uintptr(val)) + } + } + switch f.Type().Kind() { + case reflect.Struct: + place(f) + case reflect.Bool: + if f.Bool() { + val |= 1 + } + shift += 8 + class |= _INT + case reflect.Uint8: + val |= f.Uint() << shift + shift += 8 + class |= _INT + case reflect.Uint16: + val |= f.Uint() << shift + shift += 16 + class |= _INT + case reflect.Uint32: + val |= f.Uint() << shift + shift += 32 + class |= _INT + case reflect.Uint64: + addInt(uintptr(f.Uint())) + shift = 0 + flushed = true + case reflect.Int8: + val |= uint64(f.Int()&0xFF) << shift + shift += 8 + class |= _INT + case reflect.Int16: + val |= uint64(f.Int()&0xFFFF) << shift + shift += 16 + class |= _INT + case reflect.Int32: + val |= uint64(f.Int()&0xFFFF_FFFF) << shift + shift += 32 + class |= _INT + case reflect.Int64: + addInt(uintptr(f.Int())) + shift = 0 + flushed = true + case reflect.Float32: + if class == _FLOAT { + addFloat(uintptr(val)) + val = 0 + shift = 0 + } + val |= uint64(math.Float32bits(float32(f.Float()))) << shift + shift += 32 + class |= _FLOAT + case reflect.Float64: + addFloat(uintptr(math.Float64bits(float64(f.Float())))) + shift = 0 + flushed = true + case reflect.Array: + place(f) + default: + panic("purego: unsupported kind " + f.Kind().String()) + } + } + } + place(v) + if !flushed { + if class == _FLOAT { + addFloat(uintptr(val)) + } else { + addInt(uintptr(val)) + } + } +} + +func placeStack(v reflect.Value, keepAlive []interface{}, addInt func(uintptr)) []interface{} { + // Struct is too big to be placed in registers. + // Copy to heap and place the pointer in register + ptrStruct := reflect.New(v.Type()) + ptrStruct.Elem().Set(v) + ptr := ptrStruct.Elem().Addr().UnsafePointer() + keepAlive = append(keepAlive, ptr) + addInt(uintptr(ptr)) + return keepAlive +} + +// isHFA reports a Homogeneous Floating-point Aggregate (HFA) which is a Fundamental Data Type that is a +// Floating-Point type and at most four uniquely addressable members (5.9.5.1 in [Arm64 Calling Convention]). +// This type of struct will be placed more compactly than the individual fields. +// +// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst +func isHFA(t reflect.Type) bool { + // round up struct size to nearest 8 see section B.4 + structSize := roundUpTo8(t.Size()) + if structSize == 0 || t.NumField() > 4 { + return false + } + first := t.Field(0) + switch first.Type.Kind() { + case reflect.Float32, reflect.Float64: + firstKind := first.Type.Kind() + for i := 0; i < t.NumField(); i++ { + if t.Field(i).Type.Kind() != firstKind { + return false + } + } + return true + case reflect.Array: + switch first.Type.Elem().Kind() { + case reflect.Float32, reflect.Float64: + return true + default: + return false + } + case reflect.Struct: + for i := 0; i < first.Type.NumField(); i++ { + if !isHFA(first.Type) { + return false + } + } + return true + default: + return false + } +} + +// isHVA reports a Homogeneous Aggregate with a Fundamental Data Type that is a Short-Vector type +// and at most four uniquely addressable members (5.9.5.2 in [Arm64 Calling Convention]). +// A short vector is a machine type that is composed of repeated instances of one fundamental integral or +// floating-point type. It may be 8 or 16 bytes in total size (5.4 in [Arm64 Calling Convention]). +// This type of struct will be placed more compactly than the individual fields. +// +// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst +func isHVA(t reflect.Type) bool { + // round up struct size to nearest 8 see section B.4 + structSize := roundUpTo8(t.Size()) + if structSize == 0 || (structSize != 8 && structSize != 16) { + return false + } + first := t.Field(0) + switch first.Type.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32: + firstKind := first.Type.Kind() + for i := 0; i < t.NumField(); i++ { + if t.Field(i).Type.Kind() != firstKind { + return false + } + } + return true + case reflect.Array: + switch first.Type.Elem().Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32: + return true + default: + return false + } + default: + return false + } +} diff --git a/vendor/github.com/ebitengine/purego/struct_other.go b/vendor/github.com/ebitengine/purego/struct_other.go new file mode 100644 index 000000000..d191d9678 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/struct_other.go @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2024 The Ebitengine Authors + +//go:build !amd64 && !arm64 + +package purego + +import "reflect" + +func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { + panic("purego: struct arguments are not supported") +} diff --git a/vendor/github.com/ebitengine/purego/sys_amd64.s b/vendor/github.com/ebitengine/purego/sys_amd64.s new file mode 100644 index 000000000..eedf9b838 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/sys_amd64.s @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +#include "textflag.h" +#include "abi_amd64.h" +#include "go_asm.h" +#include "funcdata.h" + +#define STACK_SIZE 80 +#define PTR_ADDRESS (STACK_SIZE - 8) + +// syscall15X calls a function in libc on behalf of the syscall package. +// syscall15X takes a pointer to a struct like: +// struct { +// fn uintptr +// a1 uintptr +// a2 uintptr +// a3 uintptr +// a4 uintptr +// a5 uintptr +// a6 uintptr +// a7 uintptr +// a8 uintptr +// a9 uintptr +// a10 uintptr +// a11 uintptr +// a12 uintptr +// a13 uintptr +// a14 uintptr +// a15 uintptr +// r1 uintptr +// r2 uintptr +// err uintptr +// } +// syscall15X must be called on the g0 stack with the +// C calling convention (use libcCall). +GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8 +DATA ·syscall15XABI0(SB)/8, $syscall15X(SB) +TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0 + PUSHQ BP + MOVQ SP, BP + SUBQ $STACK_SIZE, SP + MOVQ DI, PTR_ADDRESS(BP) // save the pointer + MOVQ DI, R11 + + MOVQ syscall15Args_f1(R11), X0 // f1 + MOVQ syscall15Args_f2(R11), X1 // f2 + MOVQ syscall15Args_f3(R11), X2 // f3 + MOVQ syscall15Args_f4(R11), X3 // f4 + MOVQ syscall15Args_f5(R11), X4 // f5 + MOVQ syscall15Args_f6(R11), X5 // f6 + MOVQ syscall15Args_f7(R11), X6 // f7 + MOVQ syscall15Args_f8(R11), X7 // f8 + + MOVQ syscall15Args_a1(R11), DI // a1 + MOVQ syscall15Args_a2(R11), SI // a2 + MOVQ syscall15Args_a3(R11), DX // a3 + MOVQ syscall15Args_a4(R11), CX // a4 + MOVQ syscall15Args_a5(R11), R8 // a5 + MOVQ syscall15Args_a6(R11), R9 // a6 + + // push the remaining paramters onto the stack + MOVQ syscall15Args_a7(R11), R12 + MOVQ R12, 0(SP) // push a7 + MOVQ syscall15Args_a8(R11), R12 + MOVQ R12, 8(SP) // push a8 + MOVQ syscall15Args_a9(R11), R12 + MOVQ R12, 16(SP) // push a9 + MOVQ syscall15Args_a10(R11), R12 + MOVQ R12, 24(SP) // push a10 + MOVQ syscall15Args_a11(R11), R12 + MOVQ R12, 32(SP) // push a11 + MOVQ syscall15Args_a12(R11), R12 + MOVQ R12, 40(SP) // push a12 + MOVQ syscall15Args_a13(R11), R12 + MOVQ R12, 48(SP) // push a13 + MOVQ syscall15Args_a14(R11), R12 + MOVQ R12, 56(SP) // push a14 + MOVQ syscall15Args_a15(R11), R12 + MOVQ R12, 64(SP) // push a15 + XORL AX, AX // vararg: say "no float args" + + MOVQ syscall15Args_fn(R11), R10 // fn + CALL R10 + + MOVQ PTR_ADDRESS(BP), DI // get the pointer back + MOVQ AX, syscall15Args_r1(DI) // r1 + MOVQ X0, syscall15Args_r2(DI) // r2 + + XORL AX, AX // no error (it's ignored anyway) + ADDQ $STACK_SIZE, SP + MOVQ BP, SP + POPQ BP + RET + +TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 + MOVQ 0(SP), AX // save the return address to calculate the cb index + MOVQ 8(SP), R10 // get the return SP so that we can align register args with stack args + ADDQ $8, SP // remove return address from stack, we are not returning to callbackasm, but to its caller. + + // make space for first six int and 8 float arguments below the frame + ADJSP $14*8, SP + MOVSD X0, (1*8)(SP) + MOVSD X1, (2*8)(SP) + MOVSD X2, (3*8)(SP) + MOVSD X3, (4*8)(SP) + MOVSD X4, (5*8)(SP) + MOVSD X5, (6*8)(SP) + MOVSD X6, (7*8)(SP) + MOVSD X7, (8*8)(SP) + MOVQ DI, (9*8)(SP) + MOVQ SI, (10*8)(SP) + MOVQ DX, (11*8)(SP) + MOVQ CX, (12*8)(SP) + MOVQ R8, (13*8)(SP) + MOVQ R9, (14*8)(SP) + LEAQ 8(SP), R8 // R8 = address of args vector + + PUSHQ R10 // push the stack pointer below registers + + // determine index into runtime·cbs table + MOVQ $callbackasm(SB), DX + SUBQ DX, AX + MOVQ $0, DX + MOVQ $5, CX // divide by 5 because each call instruction in ·callbacks is 5 bytes long + DIVL CX + SUBQ $1, AX // subtract 1 because return PC is to the next slot + + // Switch from the host ABI to the Go ABI. + PUSH_REGS_HOST_TO_ABI0() + + // Create a struct callbackArgs on our stack to be passed as + // the "frame" to cgocallback and on to callbackWrap. + // $24 to make enough room for the arguments to runtime.cgocallback + SUBQ $(24+callbackArgs__size), SP + MOVQ AX, (24+callbackArgs_index)(SP) // callback index + MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector + MOVQ $0, (24+callbackArgs_result)(SP) // result + LEAQ 24(SP), AX // take the address of callbackArgs + + // Call cgocallback, which will call callbackWrap(frame). + MOVQ ·callbackWrap_call(SB), DI // Get the ABIInternal function pointer + MOVQ (DI), DI // without by using a closure. + MOVQ AX, SI // frame (address of callbackArgs) + MOVQ $0, CX // context + + CALL crosscall2(SB) // runtime.cgocallback(fn, frame, ctxt uintptr) + + // Get callback result. + MOVQ (24+callbackArgs_result)(SP), AX + ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct + + POP_REGS_HOST_TO_ABI0() + + POPQ R10 // get the SP back + ADJSP $-14*8, SP // remove arguments + + MOVQ R10, 0(SP) + + RET diff --git a/vendor/github.com/ebitengine/purego/sys_arm64.s b/vendor/github.com/ebitengine/purego/sys_arm64.s new file mode 100644 index 000000000..9cdabd2b1 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/sys_arm64.s @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux || windows + +#include "textflag.h" +#include "go_asm.h" +#include "funcdata.h" + +#define STACK_SIZE 64 +#define PTR_ADDRESS (STACK_SIZE - 8) + +// syscall15X calls a function in libc on behalf of the syscall package. +// syscall15X takes a pointer to a struct like: +// struct { +// fn uintptr +// a1 uintptr +// a2 uintptr +// a3 uintptr +// a4 uintptr +// a5 uintptr +// a6 uintptr +// a7 uintptr +// a8 uintptr +// a9 uintptr +// a10 uintptr +// a11 uintptr +// a12 uintptr +// a13 uintptr +// a14 uintptr +// a15 uintptr +// r1 uintptr +// r2 uintptr +// err uintptr +// } +// syscall15X must be called on the g0 stack with the +// C calling convention (use libcCall). +GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8 +DATA ·syscall15XABI0(SB)/8, $syscall15X(SB) +TEXT syscall15X(SB), NOSPLIT, $0 + SUB $STACK_SIZE, RSP // push structure pointer + MOVD R0, PTR_ADDRESS(RSP) + MOVD R0, R9 + + FMOVD syscall15Args_f1(R9), F0 // f1 + FMOVD syscall15Args_f2(R9), F1 // f2 + FMOVD syscall15Args_f3(R9), F2 // f3 + FMOVD syscall15Args_f4(R9), F3 // f4 + FMOVD syscall15Args_f5(R9), F4 // f5 + FMOVD syscall15Args_f6(R9), F5 // f6 + FMOVD syscall15Args_f7(R9), F6 // f7 + FMOVD syscall15Args_f8(R9), F7 // f8 + + MOVD syscall15Args_a1(R9), R0 // a1 + MOVD syscall15Args_a2(R9), R1 // a2 + MOVD syscall15Args_a3(R9), R2 // a3 + MOVD syscall15Args_a4(R9), R3 // a4 + MOVD syscall15Args_a5(R9), R4 // a5 + MOVD syscall15Args_a6(R9), R5 // a6 + MOVD syscall15Args_a7(R9), R6 // a7 + MOVD syscall15Args_a8(R9), R7 // a8 + + MOVD syscall15Args_a9(R9), R10 + MOVD R10, 0(RSP) // push a9 onto stack + MOVD syscall15Args_a10(R9), R10 + MOVD R10, 8(RSP) // push a10 onto stack + MOVD syscall15Args_a11(R9), R10 + MOVD R10, 16(RSP) // push a11 onto stack + MOVD syscall15Args_a12(R9), R10 + MOVD R10, 24(RSP) // push a12 onto stack + MOVD syscall15Args_a13(R9), R10 + MOVD R10, 32(RSP) // push a13 onto stack + MOVD syscall15Args_a14(R9), R10 + MOVD R10, 40(RSP) // push a14 onto stack + MOVD syscall15Args_a15(R9), R10 + MOVD R10, 48(RSP) // push a15 onto stack + + MOVD syscall15Args_fn(R9), R10 // fn + BL (R10) + + MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer + ADD $STACK_SIZE, RSP + MOVD R0, syscall15Args_r1(R2) // save r1 + FMOVD F0, syscall15Args_r2(R2) // save r2 + RET diff --git a/vendor/github.com/ebitengine/purego/sys_unix_arm64.s b/vendor/github.com/ebitengine/purego/sys_unix_arm64.s new file mode 100644 index 000000000..6da06b4d1 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/sys_unix_arm64.s @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 The Ebitengine Authors + +//go:build darwin || freebsd || linux + +#include "textflag.h" +#include "go_asm.h" +#include "funcdata.h" +#include "abi_arm64.h" + +TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 + NO_LOCAL_POINTERS + + // On entry, the trampoline in zcallback_darwin_arm64.s left + // the callback index in R12 (which is volatile in the C ABI). + + // Save callback register arguments R0-R7 and F0-F7. + // We do this at the top of the frame so they're contiguous with stack arguments. + SUB $(16*8), RSP, R14 + FSTPD (F0, F1), (0*8)(R14) + FSTPD (F2, F3), (2*8)(R14) + FSTPD (F4, F5), (4*8)(R14) + FSTPD (F6, F7), (6*8)(R14) + STP (R0, R1), (8*8)(R14) + STP (R2, R3), (10*8)(R14) + STP (R4, R5), (12*8)(R14) + STP (R6, R7), (14*8)(R14) + + // Adjust SP by frame size. + SUB $(26*8), RSP + + // It is important to save R27 because the go assembler + // uses it for move instructions for a variable. + // This line: + // MOVD ·callbackWrap_call(SB), R0 + // Creates the instructions: + // ADRP 14335(PC), R27 + // MOVD 388(27), R0 + // R27 is a callee saved register so we are responsible + // for ensuring its value doesn't change. So save it and + // restore it at the end of this function. + // R30 is the link register. crosscall2 doesn't save it + // so it's saved here. + STP (R27, R30), 0(RSP) + + // Create a struct callbackArgs on our stack. + MOVD $(callbackArgs__size)(RSP), R13 + MOVD R12, callbackArgs_index(R13) // callback index + MOVD R14, callbackArgs_args(R13) // address of args vector + MOVD ZR, callbackArgs_result(R13) // result + + // Move parameters into registers + // Get the ABIInternal function pointer + // without by using a closure. + MOVD ·callbackWrap_call(SB), R0 + MOVD (R0), R0 // fn unsafe.Pointer + MOVD R13, R1 // frame (&callbackArgs{...}) + MOVD $0, R3 // ctxt uintptr + + BL crosscall2(SB) + + // Get callback result. + MOVD $(callbackArgs__size)(RSP), R13 + MOVD callbackArgs_result(R13), R0 + + // Restore LR and R27 + LDP 0(RSP), (R27, R30) + ADD $(26*8), RSP + + RET diff --git a/vendor/github.com/ebitengine/purego/syscall.go b/vendor/github.com/ebitengine/purego/syscall.go new file mode 100644 index 000000000..f38e4a2f6 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/syscall.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || linux || windows + +package purego + +const ( + maxArgs = 15 + numOfFloats = 8 // arm64 and amd64 both have 8 float registers +) + +// SyscallN takes fn, a C function pointer and a list of arguments as uintptr. +// There is an internal maximum number of arguments that SyscallN can take. It panics +// when the maximum is exceeded. It returns the result and the libc error code if there is one. +// +// NOTE: SyscallN does not properly call functions that have both integer and float parameters. +// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607 +// for an explanation of why that is. +// +// On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the +// stack. +// +// The pragma go:nosplit is not needed at this function declaration because it uses go:uintptrescapes +// which forces all the objects that the uintptrs point to onto the heap where a stack split won't affect +// their memory location. +// +//go:uintptrescapes +func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if len(args) > maxArgs { + panic("purego: too many arguments to SyscallN") + } + // add padding so there is no out-of-bounds slicing + var tmp [maxArgs]uintptr + copy(tmp[:], args) + return syscall_syscall15X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14]) +} diff --git a/vendor/github.com/ebitengine/purego/syscall_cgo_linux.go b/vendor/github.com/ebitengine/purego/syscall_cgo_linux.go new file mode 100644 index 000000000..524a757e7 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/syscall_cgo_linux.go @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build cgo && !(amd64 || arm64) + +package purego + +import ( + _ "unsafe" // for go:linkname + + "github.com/ebitengine/purego/internal/cgo" +) + +var syscall15XABI0 = uintptr(cgo.Syscall15XABI0) + +// this is only here to make the assembly files happy :) +type syscall15Args struct { + fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr + f1, f2, f3, f4, f5, f6, f7, f8 uintptr + r1, r2, err uintptr +} + +//go:nosplit +func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { + return cgo.Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) +} + +func NewCallback(_ interface{}) uintptr { + panic("purego: NewCallback on Linux is only supported on amd64/arm64") +} diff --git a/vendor/github.com/ebitengine/purego/syscall_sysv.go b/vendor/github.com/ebitengine/purego/syscall_sysv.go new file mode 100644 index 000000000..c0860f722 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/syscall_sysv.go @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +//go:build darwin || freebsd || (linux && (amd64 || arm64)) + +package purego + +import ( + "reflect" + "runtime" + "sync" + "unsafe" +) + +var syscall15XABI0 uintptr + +type syscall15Args struct { + fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr + f1, f2, f3, f4, f5, f6, f7, f8 uintptr + r1, r2, err uintptr +} + +//go:nosplit +func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { + args := syscall15Args{ + fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, + a1, a2, a3, a4, a5, a6, a7, a8, + r1, r2, err, + } + runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args)) + return args.r1, args.r2, args.err +} + +// NewCallback converts a Go function to a function pointer conforming to the C calling convention. +// This is useful when interoperating with C code requiring callbacks. The argument is expected to be a +// function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size +// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated +// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function +// provides similar functionality to windows.NewCallback it is distinct. +func NewCallback(fn interface{}) uintptr { + return compileCallback(fn) +} + +// maxCb is the maximum number of callbacks +// only increase this if you have added more to the callbackasm function +const maxCB = 2000 + +var cbs struct { + lock sync.Mutex + numFn int // the number of functions currently in cbs.funcs + funcs [maxCB]reflect.Value // the saved callbacks +} + +type callbackArgs struct { + index uintptr + // args points to the argument block. + // + // The structure of the arguments goes + // float registers followed by the + // integer registers followed by the stack. + // + // This variable is treated as a continuous + // block of memory containing all of the arguments + // for this callback. + args unsafe.Pointer + // Below are out-args from callbackWrap + result uintptr +} + +func compileCallback(fn interface{}) uintptr { + val := reflect.ValueOf(fn) + if val.Kind() != reflect.Func { + panic("purego: the type must be a function but was not") + } + if val.IsNil() { + panic("purego: function must not be nil") + } + ty := val.Type() + for i := 0; i < ty.NumIn(); i++ { + in := ty.In(i) + switch in.Kind() { + case reflect.Struct, reflect.Interface, reflect.Func, reflect.Slice, + reflect.Chan, reflect.Complex64, reflect.Complex128, + reflect.String, reflect.Map, reflect.Invalid: + panic("purego: unsupported argument type: " + in.Kind().String()) + } + } +output: + switch { + case ty.NumOut() == 1: + switch ty.Out(0).Kind() { + case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.UnsafePointer: + break output + } + panic("purego: unsupported return type: " + ty.String()) + case ty.NumOut() > 1: + panic("purego: callbacks can only have one return") + } + cbs.lock.Lock() + defer cbs.lock.Unlock() + if cbs.numFn >= maxCB { + panic("purego: the maximum number of callbacks has been reached") + } + cbs.funcs[cbs.numFn] = val + cbs.numFn++ + return callbackasmAddr(cbs.numFn - 1) +} + +const ptrSize = unsafe.Sizeof((*int)(nil)) + +const callbackMaxFrame = 64 * ptrSize + +// callbackasm is implemented in zcallback_GOOS_GOARCH.s +// +//go:linkname __callbackasm callbackasm +var __callbackasm byte +var callbackasmABI0 = uintptr(unsafe.Pointer(&__callbackasm)) + +// callbackWrap_call allows the calling of the ABIInternal wrapper +// which is required for runtime.cgocallback without the +// tag which is only allowed in the runtime. +// This closure is used inside sys_darwin_GOARCH.s +var callbackWrap_call = callbackWrap + +// callbackWrap is called by assembly code which determines which Go function to call. +// This function takes the arguments and passes them to the Go function and returns the result. +func callbackWrap(a *callbackArgs) { + cbs.lock.Lock() + fn := cbs.funcs[a.index] + cbs.lock.Unlock() + fnType := fn.Type() + args := make([]reflect.Value, fnType.NumIn()) + frame := (*[callbackMaxFrame]uintptr)(a.args) + var floatsN int // floatsN represents the number of float arguments processed + var intsN int // intsN represents the number of integer arguments processed + // stack points to the index into frame of the current stack element. + // The stack begins after the float and integer registers. + stack := numOfIntegerRegisters() + numOfFloats + for i := range args { + var pos int + switch fnType.In(i).Kind() { + case reflect.Float32, reflect.Float64: + if floatsN >= numOfFloats { + pos = stack + stack++ + } else { + pos = floatsN + } + floatsN++ + default: + if intsN >= numOfIntegerRegisters() { + pos = stack + stack++ + } else { + // the integers begin after the floats in frame + pos = intsN + numOfFloats + } + intsN++ + } + args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[pos])).Elem() + } + ret := fn.Call(args) + if len(ret) > 0 { + switch k := ret[0].Kind(); k { + case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: + a.result = uintptr(ret[0].Uint()) + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + a.result = uintptr(ret[0].Int()) + case reflect.Bool: + if ret[0].Bool() { + a.result = 1 + } else { + a.result = 0 + } + case reflect.Pointer: + a.result = ret[0].Pointer() + case reflect.UnsafePointer: + a.result = ret[0].Pointer() + default: + panic("purego: unsupported kind: " + k.String()) + } + } +} + +// callbackasmAddr returns address of runtime.callbackasm +// function adjusted by i. +// On x86 and amd64, runtime.callbackasm is a series of CALL instructions, +// and we want callback to arrive at +// correspondent call instruction instead of start of +// runtime.callbackasm. +// On ARM, runtime.callbackasm is a series of mov and branch instructions. +// R12 is loaded with the callback index. Each entry is two instructions, +// hence 8 bytes. +func callbackasmAddr(i int) uintptr { + var entrySize int + switch runtime.GOARCH { + default: + panic("purego: unsupported architecture") + case "386", "amd64": + entrySize = 5 + case "arm", "arm64": + // On ARM and ARM64, each entry is a MOV instruction + // followed by a branch instruction + entrySize = 8 + } + return callbackasmABI0 + uintptr(i*entrySize) +} diff --git a/vendor/github.com/ebitengine/purego/syscall_windows.go b/vendor/github.com/ebitengine/purego/syscall_windows.go new file mode 100644 index 000000000..9a0d86c24 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/syscall_windows.go @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 The Ebitengine Authors + +package purego + +import ( + "syscall" + _ "unsafe" // only for go:linkname + + "golang.org/x/sys/windows" +) + +var syscall15XABI0 uintptr + +type syscall15Args struct { + fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr + f1, f2, f3, f4, f5, f6, f7, f8 uintptr + r1, r2, err uintptr +} + +func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { + r1, r2, errno := syscall.Syscall15(fn, 15, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + return r1, r2, uintptr(errno) +} + +// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. +// This is useful when interoperating with Windows code requiring callbacks. The argument is expected to be a +// function with one uintptr-sized result. The function must not have arguments with size larger than the +// size of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory +// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024 +// callbacks can always be created. Although this function is similiar to the darwin version it may act +// differently. +func NewCallback(fn interface{}) uintptr { + return syscall.NewCallback(fn) +} + +//go:linkname openLibrary openLibrary +func openLibrary(name string) (uintptr, error) { + handle, err := windows.LoadLibrary(name) + return uintptr(handle), err +} + +func loadSymbol(handle uintptr, name string) (uintptr, error) { + return windows.GetProcAddress(windows.Handle(handle), name) +} diff --git a/vendor/github.com/ebitengine/purego/zcallback_amd64.s b/vendor/github.com/ebitengine/purego/zcallback_amd64.s new file mode 100644 index 000000000..6a778bfca --- /dev/null +++ b/vendor/github.com/ebitengine/purego/zcallback_amd64.s @@ -0,0 +1,2014 @@ +// Code generated by wincallback.go using 'go generate'. DO NOT EDIT. + +//go:build darwin || freebsd || linux + +// runtime·callbackasm is called by external code to +// execute Go implemented callback function. It is not +// called from the start, instead runtime·compilecallback +// always returns address into runtime·callbackasm offset +// appropriately so different callbacks start with different +// CALL instruction in runtime·callbackasm. This determines +// which Go callback function is executed later on. +#include "textflag.h" + +TEXT callbackasm(SB), NOSPLIT|NOFRAME, $0 + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) + CALL callbackasm1(SB) diff --git a/vendor/github.com/ebitengine/purego/zcallback_arm64.s b/vendor/github.com/ebitengine/purego/zcallback_arm64.s new file mode 100644 index 000000000..c079b8038 --- /dev/null +++ b/vendor/github.com/ebitengine/purego/zcallback_arm64.s @@ -0,0 +1,4014 @@ +// Code generated by wincallback.go using 'go generate'. DO NOT EDIT. + +//go:build darwin || freebsd || linux + +// External code calls into callbackasm at an offset corresponding +// to the callback index. Callbackasm is a table of MOV and B instructions. +// The MOV instruction loads R12 with the callback index, and the +// B instruction branches to callbackasm1. +// callbackasm1 takes the callback index from R12 and +// indexes into an array that stores information about each callback. +// It then calls the Go implementation for that callback. +#include "textflag.h" + +TEXT callbackasm(SB), NOSPLIT|NOFRAME, $0 + MOVD $0, R12 + B callbackasm1(SB) + MOVD $1, R12 + B callbackasm1(SB) + MOVD $2, R12 + B callbackasm1(SB) + MOVD $3, R12 + B callbackasm1(SB) + MOVD $4, R12 + B callbackasm1(SB) + MOVD $5, R12 + B callbackasm1(SB) + MOVD $6, R12 + B callbackasm1(SB) + MOVD $7, R12 + B callbackasm1(SB) + MOVD $8, R12 + B callbackasm1(SB) + MOVD $9, R12 + B callbackasm1(SB) + MOVD $10, R12 + B callbackasm1(SB) + MOVD $11, R12 + B callbackasm1(SB) + MOVD $12, R12 + B callbackasm1(SB) + MOVD $13, R12 + B callbackasm1(SB) + MOVD $14, R12 + B callbackasm1(SB) + MOVD $15, R12 + B callbackasm1(SB) + MOVD $16, R12 + B callbackasm1(SB) + MOVD $17, R12 + B callbackasm1(SB) + MOVD $18, R12 + B callbackasm1(SB) + MOVD $19, R12 + B callbackasm1(SB) + MOVD $20, R12 + B callbackasm1(SB) + MOVD $21, R12 + B callbackasm1(SB) + MOVD $22, R12 + B callbackasm1(SB) + MOVD $23, R12 + B callbackasm1(SB) + MOVD $24, R12 + B callbackasm1(SB) + MOVD $25, R12 + B callbackasm1(SB) + MOVD $26, R12 + B callbackasm1(SB) + MOVD $27, R12 + B callbackasm1(SB) + MOVD $28, R12 + B callbackasm1(SB) + MOVD $29, R12 + B callbackasm1(SB) + MOVD $30, R12 + B callbackasm1(SB) + MOVD $31, R12 + B callbackasm1(SB) + MOVD $32, R12 + B callbackasm1(SB) + MOVD $33, R12 + B callbackasm1(SB) + MOVD $34, R12 + B callbackasm1(SB) + MOVD $35, R12 + B callbackasm1(SB) + MOVD $36, R12 + B callbackasm1(SB) + MOVD $37, R12 + B callbackasm1(SB) + MOVD $38, R12 + B callbackasm1(SB) + MOVD $39, R12 + B callbackasm1(SB) + MOVD $40, R12 + B callbackasm1(SB) + MOVD $41, R12 + B callbackasm1(SB) + MOVD $42, R12 + B callbackasm1(SB) + MOVD $43, R12 + B callbackasm1(SB) + MOVD $44, R12 + B callbackasm1(SB) + MOVD $45, R12 + B callbackasm1(SB) + MOVD $46, R12 + B callbackasm1(SB) + MOVD $47, R12 + B callbackasm1(SB) + MOVD $48, R12 + B callbackasm1(SB) + MOVD $49, R12 + B callbackasm1(SB) + MOVD $50, R12 + B callbackasm1(SB) + MOVD $51, R12 + B callbackasm1(SB) + MOVD $52, R12 + B callbackasm1(SB) + MOVD $53, R12 + B callbackasm1(SB) + MOVD $54, R12 + B callbackasm1(SB) + MOVD $55, R12 + B callbackasm1(SB) + MOVD $56, R12 + B callbackasm1(SB) + MOVD $57, R12 + B callbackasm1(SB) + MOVD $58, R12 + B callbackasm1(SB) + MOVD $59, R12 + B callbackasm1(SB) + MOVD $60, R12 + B callbackasm1(SB) + MOVD $61, R12 + B callbackasm1(SB) + MOVD $62, R12 + B callbackasm1(SB) + MOVD $63, R12 + B callbackasm1(SB) + MOVD $64, R12 + B callbackasm1(SB) + MOVD $65, R12 + B callbackasm1(SB) + MOVD $66, R12 + B callbackasm1(SB) + MOVD $67, R12 + B callbackasm1(SB) + MOVD $68, R12 + B callbackasm1(SB) + MOVD $69, R12 + B callbackasm1(SB) + MOVD $70, R12 + B callbackasm1(SB) + MOVD $71, R12 + B callbackasm1(SB) + MOVD $72, R12 + B callbackasm1(SB) + MOVD $73, R12 + B callbackasm1(SB) + MOVD $74, R12 + B callbackasm1(SB) + MOVD $75, R12 + B callbackasm1(SB) + MOVD $76, R12 + B callbackasm1(SB) + MOVD $77, R12 + B callbackasm1(SB) + MOVD $78, R12 + B callbackasm1(SB) + MOVD $79, R12 + B callbackasm1(SB) + MOVD $80, R12 + B callbackasm1(SB) + MOVD $81, R12 + B callbackasm1(SB) + MOVD $82, R12 + B callbackasm1(SB) + MOVD $83, R12 + B callbackasm1(SB) + MOVD $84, R12 + B callbackasm1(SB) + MOVD $85, R12 + B callbackasm1(SB) + MOVD $86, R12 + B callbackasm1(SB) + MOVD $87, R12 + B callbackasm1(SB) + MOVD $88, R12 + B callbackasm1(SB) + MOVD $89, R12 + B callbackasm1(SB) + MOVD $90, R12 + B callbackasm1(SB) + MOVD $91, R12 + B callbackasm1(SB) + MOVD $92, R12 + B callbackasm1(SB) + MOVD $93, R12 + B callbackasm1(SB) + MOVD $94, R12 + B callbackasm1(SB) + MOVD $95, R12 + B callbackasm1(SB) + MOVD $96, R12 + B callbackasm1(SB) + MOVD $97, R12 + B callbackasm1(SB) + MOVD $98, R12 + B callbackasm1(SB) + MOVD $99, R12 + B callbackasm1(SB) + MOVD $100, R12 + B callbackasm1(SB) + MOVD $101, R12 + B callbackasm1(SB) + MOVD $102, R12 + B callbackasm1(SB) + MOVD $103, R12 + B callbackasm1(SB) + MOVD $104, R12 + B callbackasm1(SB) + MOVD $105, R12 + B callbackasm1(SB) + MOVD $106, R12 + B callbackasm1(SB) + MOVD $107, R12 + B callbackasm1(SB) + MOVD $108, R12 + B callbackasm1(SB) + MOVD $109, R12 + B callbackasm1(SB) + MOVD $110, R12 + B callbackasm1(SB) + MOVD $111, R12 + B callbackasm1(SB) + MOVD $112, R12 + B callbackasm1(SB) + MOVD $113, R12 + B callbackasm1(SB) + MOVD $114, R12 + B callbackasm1(SB) + MOVD $115, R12 + B callbackasm1(SB) + MOVD $116, R12 + B callbackasm1(SB) + MOVD $117, R12 + B callbackasm1(SB) + MOVD $118, R12 + B callbackasm1(SB) + MOVD $119, R12 + B callbackasm1(SB) + MOVD $120, R12 + B callbackasm1(SB) + MOVD $121, R12 + B callbackasm1(SB) + MOVD $122, R12 + B callbackasm1(SB) + MOVD $123, R12 + B callbackasm1(SB) + MOVD $124, R12 + B callbackasm1(SB) + MOVD $125, R12 + B callbackasm1(SB) + MOVD $126, R12 + B callbackasm1(SB) + MOVD $127, R12 + B callbackasm1(SB) + MOVD $128, R12 + B callbackasm1(SB) + MOVD $129, R12 + B callbackasm1(SB) + MOVD $130, R12 + B callbackasm1(SB) + MOVD $131, R12 + B callbackasm1(SB) + MOVD $132, R12 + B callbackasm1(SB) + MOVD $133, R12 + B callbackasm1(SB) + MOVD $134, R12 + B callbackasm1(SB) + MOVD $135, R12 + B callbackasm1(SB) + MOVD $136, R12 + B callbackasm1(SB) + MOVD $137, R12 + B callbackasm1(SB) + MOVD $138, R12 + B callbackasm1(SB) + MOVD $139, R12 + B callbackasm1(SB) + MOVD $140, R12 + B callbackasm1(SB) + MOVD $141, R12 + B callbackasm1(SB) + MOVD $142, R12 + B callbackasm1(SB) + MOVD $143, R12 + B callbackasm1(SB) + MOVD $144, R12 + B callbackasm1(SB) + MOVD $145, R12 + B callbackasm1(SB) + MOVD $146, R12 + B callbackasm1(SB) + MOVD $147, R12 + B callbackasm1(SB) + MOVD $148, R12 + B callbackasm1(SB) + MOVD $149, R12 + B callbackasm1(SB) + MOVD $150, R12 + B callbackasm1(SB) + MOVD $151, R12 + B callbackasm1(SB) + MOVD $152, R12 + B callbackasm1(SB) + MOVD $153, R12 + B callbackasm1(SB) + MOVD $154, R12 + B callbackasm1(SB) + MOVD $155, R12 + B callbackasm1(SB) + MOVD $156, R12 + B callbackasm1(SB) + MOVD $157, R12 + B callbackasm1(SB) + MOVD $158, R12 + B callbackasm1(SB) + MOVD $159, R12 + B callbackasm1(SB) + MOVD $160, R12 + B callbackasm1(SB) + MOVD $161, R12 + B callbackasm1(SB) + MOVD $162, R12 + B callbackasm1(SB) + MOVD $163, R12 + B callbackasm1(SB) + MOVD $164, R12 + B callbackasm1(SB) + MOVD $165, R12 + B callbackasm1(SB) + MOVD $166, R12 + B callbackasm1(SB) + MOVD $167, R12 + B callbackasm1(SB) + MOVD $168, R12 + B callbackasm1(SB) + MOVD $169, R12 + B callbackasm1(SB) + MOVD $170, R12 + B callbackasm1(SB) + MOVD $171, R12 + B callbackasm1(SB) + MOVD $172, R12 + B callbackasm1(SB) + MOVD $173, R12 + B callbackasm1(SB) + MOVD $174, R12 + B callbackasm1(SB) + MOVD $175, R12 + B callbackasm1(SB) + MOVD $176, R12 + B callbackasm1(SB) + MOVD $177, R12 + B callbackasm1(SB) + MOVD $178, R12 + B callbackasm1(SB) + MOVD $179, R12 + B callbackasm1(SB) + MOVD $180, R12 + B callbackasm1(SB) + MOVD $181, R12 + B callbackasm1(SB) + MOVD $182, R12 + B callbackasm1(SB) + MOVD $183, R12 + B callbackasm1(SB) + MOVD $184, R12 + B callbackasm1(SB) + MOVD $185, R12 + B callbackasm1(SB) + MOVD $186, R12 + B callbackasm1(SB) + MOVD $187, R12 + B callbackasm1(SB) + MOVD $188, R12 + B callbackasm1(SB) + MOVD $189, R12 + B callbackasm1(SB) + MOVD $190, R12 + B callbackasm1(SB) + MOVD $191, R12 + B callbackasm1(SB) + MOVD $192, R12 + B callbackasm1(SB) + MOVD $193, R12 + B callbackasm1(SB) + MOVD $194, R12 + B callbackasm1(SB) + MOVD $195, R12 + B callbackasm1(SB) + MOVD $196, R12 + B callbackasm1(SB) + MOVD $197, R12 + B callbackasm1(SB) + MOVD $198, R12 + B callbackasm1(SB) + MOVD $199, R12 + B callbackasm1(SB) + MOVD $200, R12 + B callbackasm1(SB) + MOVD $201, R12 + B callbackasm1(SB) + MOVD $202, R12 + B callbackasm1(SB) + MOVD $203, R12 + B callbackasm1(SB) + MOVD $204, R12 + B callbackasm1(SB) + MOVD $205, R12 + B callbackasm1(SB) + MOVD $206, R12 + B callbackasm1(SB) + MOVD $207, R12 + B callbackasm1(SB) + MOVD $208, R12 + B callbackasm1(SB) + MOVD $209, R12 + B callbackasm1(SB) + MOVD $210, R12 + B callbackasm1(SB) + MOVD $211, R12 + B callbackasm1(SB) + MOVD $212, R12 + B callbackasm1(SB) + MOVD $213, R12 + B callbackasm1(SB) + MOVD $214, R12 + B callbackasm1(SB) + MOVD $215, R12 + B callbackasm1(SB) + MOVD $216, R12 + B callbackasm1(SB) + MOVD $217, R12 + B callbackasm1(SB) + MOVD $218, R12 + B callbackasm1(SB) + MOVD $219, R12 + B callbackasm1(SB) + MOVD $220, R12 + B callbackasm1(SB) + MOVD $221, R12 + B callbackasm1(SB) + MOVD $222, R12 + B callbackasm1(SB) + MOVD $223, R12 + B callbackasm1(SB) + MOVD $224, R12 + B callbackasm1(SB) + MOVD $225, R12 + B callbackasm1(SB) + MOVD $226, R12 + B callbackasm1(SB) + MOVD $227, R12 + B callbackasm1(SB) + MOVD $228, R12 + B callbackasm1(SB) + MOVD $229, R12 + B callbackasm1(SB) + MOVD $230, R12 + B callbackasm1(SB) + MOVD $231, R12 + B callbackasm1(SB) + MOVD $232, R12 + B callbackasm1(SB) + MOVD $233, R12 + B callbackasm1(SB) + MOVD $234, R12 + B callbackasm1(SB) + MOVD $235, R12 + B callbackasm1(SB) + MOVD $236, R12 + B callbackasm1(SB) + MOVD $237, R12 + B callbackasm1(SB) + MOVD $238, R12 + B callbackasm1(SB) + MOVD $239, R12 + B callbackasm1(SB) + MOVD $240, R12 + B callbackasm1(SB) + MOVD $241, R12 + B callbackasm1(SB) + MOVD $242, R12 + B callbackasm1(SB) + MOVD $243, R12 + B callbackasm1(SB) + MOVD $244, R12 + B callbackasm1(SB) + MOVD $245, R12 + B callbackasm1(SB) + MOVD $246, R12 + B callbackasm1(SB) + MOVD $247, R12 + B callbackasm1(SB) + MOVD $248, R12 + B callbackasm1(SB) + MOVD $249, R12 + B callbackasm1(SB) + MOVD $250, R12 + B callbackasm1(SB) + MOVD $251, R12 + B callbackasm1(SB) + MOVD $252, R12 + B callbackasm1(SB) + MOVD $253, R12 + B callbackasm1(SB) + MOVD $254, R12 + B callbackasm1(SB) + MOVD $255, R12 + B callbackasm1(SB) + MOVD $256, R12 + B callbackasm1(SB) + MOVD $257, R12 + B callbackasm1(SB) + MOVD $258, R12 + B callbackasm1(SB) + MOVD $259, R12 + B callbackasm1(SB) + MOVD $260, R12 + B callbackasm1(SB) + MOVD $261, R12 + B callbackasm1(SB) + MOVD $262, R12 + B callbackasm1(SB) + MOVD $263, R12 + B callbackasm1(SB) + MOVD $264, R12 + B callbackasm1(SB) + MOVD $265, R12 + B callbackasm1(SB) + MOVD $266, R12 + B callbackasm1(SB) + MOVD $267, R12 + B callbackasm1(SB) + MOVD $268, R12 + B callbackasm1(SB) + MOVD $269, R12 + B callbackasm1(SB) + MOVD $270, R12 + B callbackasm1(SB) + MOVD $271, R12 + B callbackasm1(SB) + MOVD $272, R12 + B callbackasm1(SB) + MOVD $273, R12 + B callbackasm1(SB) + MOVD $274, R12 + B callbackasm1(SB) + MOVD $275, R12 + B callbackasm1(SB) + MOVD $276, R12 + B callbackasm1(SB) + MOVD $277, R12 + B callbackasm1(SB) + MOVD $278, R12 + B callbackasm1(SB) + MOVD $279, R12 + B callbackasm1(SB) + MOVD $280, R12 + B callbackasm1(SB) + MOVD $281, R12 + B callbackasm1(SB) + MOVD $282, R12 + B callbackasm1(SB) + MOVD $283, R12 + B callbackasm1(SB) + MOVD $284, R12 + B callbackasm1(SB) + MOVD $285, R12 + B callbackasm1(SB) + MOVD $286, R12 + B callbackasm1(SB) + MOVD $287, R12 + B callbackasm1(SB) + MOVD $288, R12 + B callbackasm1(SB) + MOVD $289, R12 + B callbackasm1(SB) + MOVD $290, R12 + B callbackasm1(SB) + MOVD $291, R12 + B callbackasm1(SB) + MOVD $292, R12 + B callbackasm1(SB) + MOVD $293, R12 + B callbackasm1(SB) + MOVD $294, R12 + B callbackasm1(SB) + MOVD $295, R12 + B callbackasm1(SB) + MOVD $296, R12 + B callbackasm1(SB) + MOVD $297, R12 + B callbackasm1(SB) + MOVD $298, R12 + B callbackasm1(SB) + MOVD $299, R12 + B callbackasm1(SB) + MOVD $300, R12 + B callbackasm1(SB) + MOVD $301, R12 + B callbackasm1(SB) + MOVD $302, R12 + B callbackasm1(SB) + MOVD $303, R12 + B callbackasm1(SB) + MOVD $304, R12 + B callbackasm1(SB) + MOVD $305, R12 + B callbackasm1(SB) + MOVD $306, R12 + B callbackasm1(SB) + MOVD $307, R12 + B callbackasm1(SB) + MOVD $308, R12 + B callbackasm1(SB) + MOVD $309, R12 + B callbackasm1(SB) + MOVD $310, R12 + B callbackasm1(SB) + MOVD $311, R12 + B callbackasm1(SB) + MOVD $312, R12 + B callbackasm1(SB) + MOVD $313, R12 + B callbackasm1(SB) + MOVD $314, R12 + B callbackasm1(SB) + MOVD $315, R12 + B callbackasm1(SB) + MOVD $316, R12 + B callbackasm1(SB) + MOVD $317, R12 + B callbackasm1(SB) + MOVD $318, R12 + B callbackasm1(SB) + MOVD $319, R12 + B callbackasm1(SB) + MOVD $320, R12 + B callbackasm1(SB) + MOVD $321, R12 + B callbackasm1(SB) + MOVD $322, R12 + B callbackasm1(SB) + MOVD $323, R12 + B callbackasm1(SB) + MOVD $324, R12 + B callbackasm1(SB) + MOVD $325, R12 + B callbackasm1(SB) + MOVD $326, R12 + B callbackasm1(SB) + MOVD $327, R12 + B callbackasm1(SB) + MOVD $328, R12 + B callbackasm1(SB) + MOVD $329, R12 + B callbackasm1(SB) + MOVD $330, R12 + B callbackasm1(SB) + MOVD $331, R12 + B callbackasm1(SB) + MOVD $332, R12 + B callbackasm1(SB) + MOVD $333, R12 + B callbackasm1(SB) + MOVD $334, R12 + B callbackasm1(SB) + MOVD $335, R12 + B callbackasm1(SB) + MOVD $336, R12 + B callbackasm1(SB) + MOVD $337, R12 + B callbackasm1(SB) + MOVD $338, R12 + B callbackasm1(SB) + MOVD $339, R12 + B callbackasm1(SB) + MOVD $340, R12 + B callbackasm1(SB) + MOVD $341, R12 + B callbackasm1(SB) + MOVD $342, R12 + B callbackasm1(SB) + MOVD $343, R12 + B callbackasm1(SB) + MOVD $344, R12 + B callbackasm1(SB) + MOVD $345, R12 + B callbackasm1(SB) + MOVD $346, R12 + B callbackasm1(SB) + MOVD $347, R12 + B callbackasm1(SB) + MOVD $348, R12 + B callbackasm1(SB) + MOVD $349, R12 + B callbackasm1(SB) + MOVD $350, R12 + B callbackasm1(SB) + MOVD $351, R12 + B callbackasm1(SB) + MOVD $352, R12 + B callbackasm1(SB) + MOVD $353, R12 + B callbackasm1(SB) + MOVD $354, R12 + B callbackasm1(SB) + MOVD $355, R12 + B callbackasm1(SB) + MOVD $356, R12 + B callbackasm1(SB) + MOVD $357, R12 + B callbackasm1(SB) + MOVD $358, R12 + B callbackasm1(SB) + MOVD $359, R12 + B callbackasm1(SB) + MOVD $360, R12 + B callbackasm1(SB) + MOVD $361, R12 + B callbackasm1(SB) + MOVD $362, R12 + B callbackasm1(SB) + MOVD $363, R12 + B callbackasm1(SB) + MOVD $364, R12 + B callbackasm1(SB) + MOVD $365, R12 + B callbackasm1(SB) + MOVD $366, R12 + B callbackasm1(SB) + MOVD $367, R12 + B callbackasm1(SB) + MOVD $368, R12 + B callbackasm1(SB) + MOVD $369, R12 + B callbackasm1(SB) + MOVD $370, R12 + B callbackasm1(SB) + MOVD $371, R12 + B callbackasm1(SB) + MOVD $372, R12 + B callbackasm1(SB) + MOVD $373, R12 + B callbackasm1(SB) + MOVD $374, R12 + B callbackasm1(SB) + MOVD $375, R12 + B callbackasm1(SB) + MOVD $376, R12 + B callbackasm1(SB) + MOVD $377, R12 + B callbackasm1(SB) + MOVD $378, R12 + B callbackasm1(SB) + MOVD $379, R12 + B callbackasm1(SB) + MOVD $380, R12 + B callbackasm1(SB) + MOVD $381, R12 + B callbackasm1(SB) + MOVD $382, R12 + B callbackasm1(SB) + MOVD $383, R12 + B callbackasm1(SB) + MOVD $384, R12 + B callbackasm1(SB) + MOVD $385, R12 + B callbackasm1(SB) + MOVD $386, R12 + B callbackasm1(SB) + MOVD $387, R12 + B callbackasm1(SB) + MOVD $388, R12 + B callbackasm1(SB) + MOVD $389, R12 + B callbackasm1(SB) + MOVD $390, R12 + B callbackasm1(SB) + MOVD $391, R12 + B callbackasm1(SB) + MOVD $392, R12 + B callbackasm1(SB) + MOVD $393, R12 + B callbackasm1(SB) + MOVD $394, R12 + B callbackasm1(SB) + MOVD $395, R12 + B callbackasm1(SB) + MOVD $396, R12 + B callbackasm1(SB) + MOVD $397, R12 + B callbackasm1(SB) + MOVD $398, R12 + B callbackasm1(SB) + MOVD $399, R12 + B callbackasm1(SB) + MOVD $400, R12 + B callbackasm1(SB) + MOVD $401, R12 + B callbackasm1(SB) + MOVD $402, R12 + B callbackasm1(SB) + MOVD $403, R12 + B callbackasm1(SB) + MOVD $404, R12 + B callbackasm1(SB) + MOVD $405, R12 + B callbackasm1(SB) + MOVD $406, R12 + B callbackasm1(SB) + MOVD $407, R12 + B callbackasm1(SB) + MOVD $408, R12 + B callbackasm1(SB) + MOVD $409, R12 + B callbackasm1(SB) + MOVD $410, R12 + B callbackasm1(SB) + MOVD $411, R12 + B callbackasm1(SB) + MOVD $412, R12 + B callbackasm1(SB) + MOVD $413, R12 + B callbackasm1(SB) + MOVD $414, R12 + B callbackasm1(SB) + MOVD $415, R12 + B callbackasm1(SB) + MOVD $416, R12 + B callbackasm1(SB) + MOVD $417, R12 + B callbackasm1(SB) + MOVD $418, R12 + B callbackasm1(SB) + MOVD $419, R12 + B callbackasm1(SB) + MOVD $420, R12 + B callbackasm1(SB) + MOVD $421, R12 + B callbackasm1(SB) + MOVD $422, R12 + B callbackasm1(SB) + MOVD $423, R12 + B callbackasm1(SB) + MOVD $424, R12 + B callbackasm1(SB) + MOVD $425, R12 + B callbackasm1(SB) + MOVD $426, R12 + B callbackasm1(SB) + MOVD $427, R12 + B callbackasm1(SB) + MOVD $428, R12 + B callbackasm1(SB) + MOVD $429, R12 + B callbackasm1(SB) + MOVD $430, R12 + B callbackasm1(SB) + MOVD $431, R12 + B callbackasm1(SB) + MOVD $432, R12 + B callbackasm1(SB) + MOVD $433, R12 + B callbackasm1(SB) + MOVD $434, R12 + B callbackasm1(SB) + MOVD $435, R12 + B callbackasm1(SB) + MOVD $436, R12 + B callbackasm1(SB) + MOVD $437, R12 + B callbackasm1(SB) + MOVD $438, R12 + B callbackasm1(SB) + MOVD $439, R12 + B callbackasm1(SB) + MOVD $440, R12 + B callbackasm1(SB) + MOVD $441, R12 + B callbackasm1(SB) + MOVD $442, R12 + B callbackasm1(SB) + MOVD $443, R12 + B callbackasm1(SB) + MOVD $444, R12 + B callbackasm1(SB) + MOVD $445, R12 + B callbackasm1(SB) + MOVD $446, R12 + B callbackasm1(SB) + MOVD $447, R12 + B callbackasm1(SB) + MOVD $448, R12 + B callbackasm1(SB) + MOVD $449, R12 + B callbackasm1(SB) + MOVD $450, R12 + B callbackasm1(SB) + MOVD $451, R12 + B callbackasm1(SB) + MOVD $452, R12 + B callbackasm1(SB) + MOVD $453, R12 + B callbackasm1(SB) + MOVD $454, R12 + B callbackasm1(SB) + MOVD $455, R12 + B callbackasm1(SB) + MOVD $456, R12 + B callbackasm1(SB) + MOVD $457, R12 + B callbackasm1(SB) + MOVD $458, R12 + B callbackasm1(SB) + MOVD $459, R12 + B callbackasm1(SB) + MOVD $460, R12 + B callbackasm1(SB) + MOVD $461, R12 + B callbackasm1(SB) + MOVD $462, R12 + B callbackasm1(SB) + MOVD $463, R12 + B callbackasm1(SB) + MOVD $464, R12 + B callbackasm1(SB) + MOVD $465, R12 + B callbackasm1(SB) + MOVD $466, R12 + B callbackasm1(SB) + MOVD $467, R12 + B callbackasm1(SB) + MOVD $468, R12 + B callbackasm1(SB) + MOVD $469, R12 + B callbackasm1(SB) + MOVD $470, R12 + B callbackasm1(SB) + MOVD $471, R12 + B callbackasm1(SB) + MOVD $472, R12 + B callbackasm1(SB) + MOVD $473, R12 + B callbackasm1(SB) + MOVD $474, R12 + B callbackasm1(SB) + MOVD $475, R12 + B callbackasm1(SB) + MOVD $476, R12 + B callbackasm1(SB) + MOVD $477, R12 + B callbackasm1(SB) + MOVD $478, R12 + B callbackasm1(SB) + MOVD $479, R12 + B callbackasm1(SB) + MOVD $480, R12 + B callbackasm1(SB) + MOVD $481, R12 + B callbackasm1(SB) + MOVD $482, R12 + B callbackasm1(SB) + MOVD $483, R12 + B callbackasm1(SB) + MOVD $484, R12 + B callbackasm1(SB) + MOVD $485, R12 + B callbackasm1(SB) + MOVD $486, R12 + B callbackasm1(SB) + MOVD $487, R12 + B callbackasm1(SB) + MOVD $488, R12 + B callbackasm1(SB) + MOVD $489, R12 + B callbackasm1(SB) + MOVD $490, R12 + B callbackasm1(SB) + MOVD $491, R12 + B callbackasm1(SB) + MOVD $492, R12 + B callbackasm1(SB) + MOVD $493, R12 + B callbackasm1(SB) + MOVD $494, R12 + B callbackasm1(SB) + MOVD $495, R12 + B callbackasm1(SB) + MOVD $496, R12 + B callbackasm1(SB) + MOVD $497, R12 + B callbackasm1(SB) + MOVD $498, R12 + B callbackasm1(SB) + MOVD $499, R12 + B callbackasm1(SB) + MOVD $500, R12 + B callbackasm1(SB) + MOVD $501, R12 + B callbackasm1(SB) + MOVD $502, R12 + B callbackasm1(SB) + MOVD $503, R12 + B callbackasm1(SB) + MOVD $504, R12 + B callbackasm1(SB) + MOVD $505, R12 + B callbackasm1(SB) + MOVD $506, R12 + B callbackasm1(SB) + MOVD $507, R12 + B callbackasm1(SB) + MOVD $508, R12 + B callbackasm1(SB) + MOVD $509, R12 + B callbackasm1(SB) + MOVD $510, R12 + B callbackasm1(SB) + MOVD $511, R12 + B callbackasm1(SB) + MOVD $512, R12 + B callbackasm1(SB) + MOVD $513, R12 + B callbackasm1(SB) + MOVD $514, R12 + B callbackasm1(SB) + MOVD $515, R12 + B callbackasm1(SB) + MOVD $516, R12 + B callbackasm1(SB) + MOVD $517, R12 + B callbackasm1(SB) + MOVD $518, R12 + B callbackasm1(SB) + MOVD $519, R12 + B callbackasm1(SB) + MOVD $520, R12 + B callbackasm1(SB) + MOVD $521, R12 + B callbackasm1(SB) + MOVD $522, R12 + B callbackasm1(SB) + MOVD $523, R12 + B callbackasm1(SB) + MOVD $524, R12 + B callbackasm1(SB) + MOVD $525, R12 + B callbackasm1(SB) + MOVD $526, R12 + B callbackasm1(SB) + MOVD $527, R12 + B callbackasm1(SB) + MOVD $528, R12 + B callbackasm1(SB) + MOVD $529, R12 + B callbackasm1(SB) + MOVD $530, R12 + B callbackasm1(SB) + MOVD $531, R12 + B callbackasm1(SB) + MOVD $532, R12 + B callbackasm1(SB) + MOVD $533, R12 + B callbackasm1(SB) + MOVD $534, R12 + B callbackasm1(SB) + MOVD $535, R12 + B callbackasm1(SB) + MOVD $536, R12 + B callbackasm1(SB) + MOVD $537, R12 + B callbackasm1(SB) + MOVD $538, R12 + B callbackasm1(SB) + MOVD $539, R12 + B callbackasm1(SB) + MOVD $540, R12 + B callbackasm1(SB) + MOVD $541, R12 + B callbackasm1(SB) + MOVD $542, R12 + B callbackasm1(SB) + MOVD $543, R12 + B callbackasm1(SB) + MOVD $544, R12 + B callbackasm1(SB) + MOVD $545, R12 + B callbackasm1(SB) + MOVD $546, R12 + B callbackasm1(SB) + MOVD $547, R12 + B callbackasm1(SB) + MOVD $548, R12 + B callbackasm1(SB) + MOVD $549, R12 + B callbackasm1(SB) + MOVD $550, R12 + B callbackasm1(SB) + MOVD $551, R12 + B callbackasm1(SB) + MOVD $552, R12 + B callbackasm1(SB) + MOVD $553, R12 + B callbackasm1(SB) + MOVD $554, R12 + B callbackasm1(SB) + MOVD $555, R12 + B callbackasm1(SB) + MOVD $556, R12 + B callbackasm1(SB) + MOVD $557, R12 + B callbackasm1(SB) + MOVD $558, R12 + B callbackasm1(SB) + MOVD $559, R12 + B callbackasm1(SB) + MOVD $560, R12 + B callbackasm1(SB) + MOVD $561, R12 + B callbackasm1(SB) + MOVD $562, R12 + B callbackasm1(SB) + MOVD $563, R12 + B callbackasm1(SB) + MOVD $564, R12 + B callbackasm1(SB) + MOVD $565, R12 + B callbackasm1(SB) + MOVD $566, R12 + B callbackasm1(SB) + MOVD $567, R12 + B callbackasm1(SB) + MOVD $568, R12 + B callbackasm1(SB) + MOVD $569, R12 + B callbackasm1(SB) + MOVD $570, R12 + B callbackasm1(SB) + MOVD $571, R12 + B callbackasm1(SB) + MOVD $572, R12 + B callbackasm1(SB) + MOVD $573, R12 + B callbackasm1(SB) + MOVD $574, R12 + B callbackasm1(SB) + MOVD $575, R12 + B callbackasm1(SB) + MOVD $576, R12 + B callbackasm1(SB) + MOVD $577, R12 + B callbackasm1(SB) + MOVD $578, R12 + B callbackasm1(SB) + MOVD $579, R12 + B callbackasm1(SB) + MOVD $580, R12 + B callbackasm1(SB) + MOVD $581, R12 + B callbackasm1(SB) + MOVD $582, R12 + B callbackasm1(SB) + MOVD $583, R12 + B callbackasm1(SB) + MOVD $584, R12 + B callbackasm1(SB) + MOVD $585, R12 + B callbackasm1(SB) + MOVD $586, R12 + B callbackasm1(SB) + MOVD $587, R12 + B callbackasm1(SB) + MOVD $588, R12 + B callbackasm1(SB) + MOVD $589, R12 + B callbackasm1(SB) + MOVD $590, R12 + B callbackasm1(SB) + MOVD $591, R12 + B callbackasm1(SB) + MOVD $592, R12 + B callbackasm1(SB) + MOVD $593, R12 + B callbackasm1(SB) + MOVD $594, R12 + B callbackasm1(SB) + MOVD $595, R12 + B callbackasm1(SB) + MOVD $596, R12 + B callbackasm1(SB) + MOVD $597, R12 + B callbackasm1(SB) + MOVD $598, R12 + B callbackasm1(SB) + MOVD $599, R12 + B callbackasm1(SB) + MOVD $600, R12 + B callbackasm1(SB) + MOVD $601, R12 + B callbackasm1(SB) + MOVD $602, R12 + B callbackasm1(SB) + MOVD $603, R12 + B callbackasm1(SB) + MOVD $604, R12 + B callbackasm1(SB) + MOVD $605, R12 + B callbackasm1(SB) + MOVD $606, R12 + B callbackasm1(SB) + MOVD $607, R12 + B callbackasm1(SB) + MOVD $608, R12 + B callbackasm1(SB) + MOVD $609, R12 + B callbackasm1(SB) + MOVD $610, R12 + B callbackasm1(SB) + MOVD $611, R12 + B callbackasm1(SB) + MOVD $612, R12 + B callbackasm1(SB) + MOVD $613, R12 + B callbackasm1(SB) + MOVD $614, R12 + B callbackasm1(SB) + MOVD $615, R12 + B callbackasm1(SB) + MOVD $616, R12 + B callbackasm1(SB) + MOVD $617, R12 + B callbackasm1(SB) + MOVD $618, R12 + B callbackasm1(SB) + MOVD $619, R12 + B callbackasm1(SB) + MOVD $620, R12 + B callbackasm1(SB) + MOVD $621, R12 + B callbackasm1(SB) + MOVD $622, R12 + B callbackasm1(SB) + MOVD $623, R12 + B callbackasm1(SB) + MOVD $624, R12 + B callbackasm1(SB) + MOVD $625, R12 + B callbackasm1(SB) + MOVD $626, R12 + B callbackasm1(SB) + MOVD $627, R12 + B callbackasm1(SB) + MOVD $628, R12 + B callbackasm1(SB) + MOVD $629, R12 + B callbackasm1(SB) + MOVD $630, R12 + B callbackasm1(SB) + MOVD $631, R12 + B callbackasm1(SB) + MOVD $632, R12 + B callbackasm1(SB) + MOVD $633, R12 + B callbackasm1(SB) + MOVD $634, R12 + B callbackasm1(SB) + MOVD $635, R12 + B callbackasm1(SB) + MOVD $636, R12 + B callbackasm1(SB) + MOVD $637, R12 + B callbackasm1(SB) + MOVD $638, R12 + B callbackasm1(SB) + MOVD $639, R12 + B callbackasm1(SB) + MOVD $640, R12 + B callbackasm1(SB) + MOVD $641, R12 + B callbackasm1(SB) + MOVD $642, R12 + B callbackasm1(SB) + MOVD $643, R12 + B callbackasm1(SB) + MOVD $644, R12 + B callbackasm1(SB) + MOVD $645, R12 + B callbackasm1(SB) + MOVD $646, R12 + B callbackasm1(SB) + MOVD $647, R12 + B callbackasm1(SB) + MOVD $648, R12 + B callbackasm1(SB) + MOVD $649, R12 + B callbackasm1(SB) + MOVD $650, R12 + B callbackasm1(SB) + MOVD $651, R12 + B callbackasm1(SB) + MOVD $652, R12 + B callbackasm1(SB) + MOVD $653, R12 + B callbackasm1(SB) + MOVD $654, R12 + B callbackasm1(SB) + MOVD $655, R12 + B callbackasm1(SB) + MOVD $656, R12 + B callbackasm1(SB) + MOVD $657, R12 + B callbackasm1(SB) + MOVD $658, R12 + B callbackasm1(SB) + MOVD $659, R12 + B callbackasm1(SB) + MOVD $660, R12 + B callbackasm1(SB) + MOVD $661, R12 + B callbackasm1(SB) + MOVD $662, R12 + B callbackasm1(SB) + MOVD $663, R12 + B callbackasm1(SB) + MOVD $664, R12 + B callbackasm1(SB) + MOVD $665, R12 + B callbackasm1(SB) + MOVD $666, R12 + B callbackasm1(SB) + MOVD $667, R12 + B callbackasm1(SB) + MOVD $668, R12 + B callbackasm1(SB) + MOVD $669, R12 + B callbackasm1(SB) + MOVD $670, R12 + B callbackasm1(SB) + MOVD $671, R12 + B callbackasm1(SB) + MOVD $672, R12 + B callbackasm1(SB) + MOVD $673, R12 + B callbackasm1(SB) + MOVD $674, R12 + B callbackasm1(SB) + MOVD $675, R12 + B callbackasm1(SB) + MOVD $676, R12 + B callbackasm1(SB) + MOVD $677, R12 + B callbackasm1(SB) + MOVD $678, R12 + B callbackasm1(SB) + MOVD $679, R12 + B callbackasm1(SB) + MOVD $680, R12 + B callbackasm1(SB) + MOVD $681, R12 + B callbackasm1(SB) + MOVD $682, R12 + B callbackasm1(SB) + MOVD $683, R12 + B callbackasm1(SB) + MOVD $684, R12 + B callbackasm1(SB) + MOVD $685, R12 + B callbackasm1(SB) + MOVD $686, R12 + B callbackasm1(SB) + MOVD $687, R12 + B callbackasm1(SB) + MOVD $688, R12 + B callbackasm1(SB) + MOVD $689, R12 + B callbackasm1(SB) + MOVD $690, R12 + B callbackasm1(SB) + MOVD $691, R12 + B callbackasm1(SB) + MOVD $692, R12 + B callbackasm1(SB) + MOVD $693, R12 + B callbackasm1(SB) + MOVD $694, R12 + B callbackasm1(SB) + MOVD $695, R12 + B callbackasm1(SB) + MOVD $696, R12 + B callbackasm1(SB) + MOVD $697, R12 + B callbackasm1(SB) + MOVD $698, R12 + B callbackasm1(SB) + MOVD $699, R12 + B callbackasm1(SB) + MOVD $700, R12 + B callbackasm1(SB) + MOVD $701, R12 + B callbackasm1(SB) + MOVD $702, R12 + B callbackasm1(SB) + MOVD $703, R12 + B callbackasm1(SB) + MOVD $704, R12 + B callbackasm1(SB) + MOVD $705, R12 + B callbackasm1(SB) + MOVD $706, R12 + B callbackasm1(SB) + MOVD $707, R12 + B callbackasm1(SB) + MOVD $708, R12 + B callbackasm1(SB) + MOVD $709, R12 + B callbackasm1(SB) + MOVD $710, R12 + B callbackasm1(SB) + MOVD $711, R12 + B callbackasm1(SB) + MOVD $712, R12 + B callbackasm1(SB) + MOVD $713, R12 + B callbackasm1(SB) + MOVD $714, R12 + B callbackasm1(SB) + MOVD $715, R12 + B callbackasm1(SB) + MOVD $716, R12 + B callbackasm1(SB) + MOVD $717, R12 + B callbackasm1(SB) + MOVD $718, R12 + B callbackasm1(SB) + MOVD $719, R12 + B callbackasm1(SB) + MOVD $720, R12 + B callbackasm1(SB) + MOVD $721, R12 + B callbackasm1(SB) + MOVD $722, R12 + B callbackasm1(SB) + MOVD $723, R12 + B callbackasm1(SB) + MOVD $724, R12 + B callbackasm1(SB) + MOVD $725, R12 + B callbackasm1(SB) + MOVD $726, R12 + B callbackasm1(SB) + MOVD $727, R12 + B callbackasm1(SB) + MOVD $728, R12 + B callbackasm1(SB) + MOVD $729, R12 + B callbackasm1(SB) + MOVD $730, R12 + B callbackasm1(SB) + MOVD $731, R12 + B callbackasm1(SB) + MOVD $732, R12 + B callbackasm1(SB) + MOVD $733, R12 + B callbackasm1(SB) + MOVD $734, R12 + B callbackasm1(SB) + MOVD $735, R12 + B callbackasm1(SB) + MOVD $736, R12 + B callbackasm1(SB) + MOVD $737, R12 + B callbackasm1(SB) + MOVD $738, R12 + B callbackasm1(SB) + MOVD $739, R12 + B callbackasm1(SB) + MOVD $740, R12 + B callbackasm1(SB) + MOVD $741, R12 + B callbackasm1(SB) + MOVD $742, R12 + B callbackasm1(SB) + MOVD $743, R12 + B callbackasm1(SB) + MOVD $744, R12 + B callbackasm1(SB) + MOVD $745, R12 + B callbackasm1(SB) + MOVD $746, R12 + B callbackasm1(SB) + MOVD $747, R12 + B callbackasm1(SB) + MOVD $748, R12 + B callbackasm1(SB) + MOVD $749, R12 + B callbackasm1(SB) + MOVD $750, R12 + B callbackasm1(SB) + MOVD $751, R12 + B callbackasm1(SB) + MOVD $752, R12 + B callbackasm1(SB) + MOVD $753, R12 + B callbackasm1(SB) + MOVD $754, R12 + B callbackasm1(SB) + MOVD $755, R12 + B callbackasm1(SB) + MOVD $756, R12 + B callbackasm1(SB) + MOVD $757, R12 + B callbackasm1(SB) + MOVD $758, R12 + B callbackasm1(SB) + MOVD $759, R12 + B callbackasm1(SB) + MOVD $760, R12 + B callbackasm1(SB) + MOVD $761, R12 + B callbackasm1(SB) + MOVD $762, R12 + B callbackasm1(SB) + MOVD $763, R12 + B callbackasm1(SB) + MOVD $764, R12 + B callbackasm1(SB) + MOVD $765, R12 + B callbackasm1(SB) + MOVD $766, R12 + B callbackasm1(SB) + MOVD $767, R12 + B callbackasm1(SB) + MOVD $768, R12 + B callbackasm1(SB) + MOVD $769, R12 + B callbackasm1(SB) + MOVD $770, R12 + B callbackasm1(SB) + MOVD $771, R12 + B callbackasm1(SB) + MOVD $772, R12 + B callbackasm1(SB) + MOVD $773, R12 + B callbackasm1(SB) + MOVD $774, R12 + B callbackasm1(SB) + MOVD $775, R12 + B callbackasm1(SB) + MOVD $776, R12 + B callbackasm1(SB) + MOVD $777, R12 + B callbackasm1(SB) + MOVD $778, R12 + B callbackasm1(SB) + MOVD $779, R12 + B callbackasm1(SB) + MOVD $780, R12 + B callbackasm1(SB) + MOVD $781, R12 + B callbackasm1(SB) + MOVD $782, R12 + B callbackasm1(SB) + MOVD $783, R12 + B callbackasm1(SB) + MOVD $784, R12 + B callbackasm1(SB) + MOVD $785, R12 + B callbackasm1(SB) + MOVD $786, R12 + B callbackasm1(SB) + MOVD $787, R12 + B callbackasm1(SB) + MOVD $788, R12 + B callbackasm1(SB) + MOVD $789, R12 + B callbackasm1(SB) + MOVD $790, R12 + B callbackasm1(SB) + MOVD $791, R12 + B callbackasm1(SB) + MOVD $792, R12 + B callbackasm1(SB) + MOVD $793, R12 + B callbackasm1(SB) + MOVD $794, R12 + B callbackasm1(SB) + MOVD $795, R12 + B callbackasm1(SB) + MOVD $796, R12 + B callbackasm1(SB) + MOVD $797, R12 + B callbackasm1(SB) + MOVD $798, R12 + B callbackasm1(SB) + MOVD $799, R12 + B callbackasm1(SB) + MOVD $800, R12 + B callbackasm1(SB) + MOVD $801, R12 + B callbackasm1(SB) + MOVD $802, R12 + B callbackasm1(SB) + MOVD $803, R12 + B callbackasm1(SB) + MOVD $804, R12 + B callbackasm1(SB) + MOVD $805, R12 + B callbackasm1(SB) + MOVD $806, R12 + B callbackasm1(SB) + MOVD $807, R12 + B callbackasm1(SB) + MOVD $808, R12 + B callbackasm1(SB) + MOVD $809, R12 + B callbackasm1(SB) + MOVD $810, R12 + B callbackasm1(SB) + MOVD $811, R12 + B callbackasm1(SB) + MOVD $812, R12 + B callbackasm1(SB) + MOVD $813, R12 + B callbackasm1(SB) + MOVD $814, R12 + B callbackasm1(SB) + MOVD $815, R12 + B callbackasm1(SB) + MOVD $816, R12 + B callbackasm1(SB) + MOVD $817, R12 + B callbackasm1(SB) + MOVD $818, R12 + B callbackasm1(SB) + MOVD $819, R12 + B callbackasm1(SB) + MOVD $820, R12 + B callbackasm1(SB) + MOVD $821, R12 + B callbackasm1(SB) + MOVD $822, R12 + B callbackasm1(SB) + MOVD $823, R12 + B callbackasm1(SB) + MOVD $824, R12 + B callbackasm1(SB) + MOVD $825, R12 + B callbackasm1(SB) + MOVD $826, R12 + B callbackasm1(SB) + MOVD $827, R12 + B callbackasm1(SB) + MOVD $828, R12 + B callbackasm1(SB) + MOVD $829, R12 + B callbackasm1(SB) + MOVD $830, R12 + B callbackasm1(SB) + MOVD $831, R12 + B callbackasm1(SB) + MOVD $832, R12 + B callbackasm1(SB) + MOVD $833, R12 + B callbackasm1(SB) + MOVD $834, R12 + B callbackasm1(SB) + MOVD $835, R12 + B callbackasm1(SB) + MOVD $836, R12 + B callbackasm1(SB) + MOVD $837, R12 + B callbackasm1(SB) + MOVD $838, R12 + B callbackasm1(SB) + MOVD $839, R12 + B callbackasm1(SB) + MOVD $840, R12 + B callbackasm1(SB) + MOVD $841, R12 + B callbackasm1(SB) + MOVD $842, R12 + B callbackasm1(SB) + MOVD $843, R12 + B callbackasm1(SB) + MOVD $844, R12 + B callbackasm1(SB) + MOVD $845, R12 + B callbackasm1(SB) + MOVD $846, R12 + B callbackasm1(SB) + MOVD $847, R12 + B callbackasm1(SB) + MOVD $848, R12 + B callbackasm1(SB) + MOVD $849, R12 + B callbackasm1(SB) + MOVD $850, R12 + B callbackasm1(SB) + MOVD $851, R12 + B callbackasm1(SB) + MOVD $852, R12 + B callbackasm1(SB) + MOVD $853, R12 + B callbackasm1(SB) + MOVD $854, R12 + B callbackasm1(SB) + MOVD $855, R12 + B callbackasm1(SB) + MOVD $856, R12 + B callbackasm1(SB) + MOVD $857, R12 + B callbackasm1(SB) + MOVD $858, R12 + B callbackasm1(SB) + MOVD $859, R12 + B callbackasm1(SB) + MOVD $860, R12 + B callbackasm1(SB) + MOVD $861, R12 + B callbackasm1(SB) + MOVD $862, R12 + B callbackasm1(SB) + MOVD $863, R12 + B callbackasm1(SB) + MOVD $864, R12 + B callbackasm1(SB) + MOVD $865, R12 + B callbackasm1(SB) + MOVD $866, R12 + B callbackasm1(SB) + MOVD $867, R12 + B callbackasm1(SB) + MOVD $868, R12 + B callbackasm1(SB) + MOVD $869, R12 + B callbackasm1(SB) + MOVD $870, R12 + B callbackasm1(SB) + MOVD $871, R12 + B callbackasm1(SB) + MOVD $872, R12 + B callbackasm1(SB) + MOVD $873, R12 + B callbackasm1(SB) + MOVD $874, R12 + B callbackasm1(SB) + MOVD $875, R12 + B callbackasm1(SB) + MOVD $876, R12 + B callbackasm1(SB) + MOVD $877, R12 + B callbackasm1(SB) + MOVD $878, R12 + B callbackasm1(SB) + MOVD $879, R12 + B callbackasm1(SB) + MOVD $880, R12 + B callbackasm1(SB) + MOVD $881, R12 + B callbackasm1(SB) + MOVD $882, R12 + B callbackasm1(SB) + MOVD $883, R12 + B callbackasm1(SB) + MOVD $884, R12 + B callbackasm1(SB) + MOVD $885, R12 + B callbackasm1(SB) + MOVD $886, R12 + B callbackasm1(SB) + MOVD $887, R12 + B callbackasm1(SB) + MOVD $888, R12 + B callbackasm1(SB) + MOVD $889, R12 + B callbackasm1(SB) + MOVD $890, R12 + B callbackasm1(SB) + MOVD $891, R12 + B callbackasm1(SB) + MOVD $892, R12 + B callbackasm1(SB) + MOVD $893, R12 + B callbackasm1(SB) + MOVD $894, R12 + B callbackasm1(SB) + MOVD $895, R12 + B callbackasm1(SB) + MOVD $896, R12 + B callbackasm1(SB) + MOVD $897, R12 + B callbackasm1(SB) + MOVD $898, R12 + B callbackasm1(SB) + MOVD $899, R12 + B callbackasm1(SB) + MOVD $900, R12 + B callbackasm1(SB) + MOVD $901, R12 + B callbackasm1(SB) + MOVD $902, R12 + B callbackasm1(SB) + MOVD $903, R12 + B callbackasm1(SB) + MOVD $904, R12 + B callbackasm1(SB) + MOVD $905, R12 + B callbackasm1(SB) + MOVD $906, R12 + B callbackasm1(SB) + MOVD $907, R12 + B callbackasm1(SB) + MOVD $908, R12 + B callbackasm1(SB) + MOVD $909, R12 + B callbackasm1(SB) + MOVD $910, R12 + B callbackasm1(SB) + MOVD $911, R12 + B callbackasm1(SB) + MOVD $912, R12 + B callbackasm1(SB) + MOVD $913, R12 + B callbackasm1(SB) + MOVD $914, R12 + B callbackasm1(SB) + MOVD $915, R12 + B callbackasm1(SB) + MOVD $916, R12 + B callbackasm1(SB) + MOVD $917, R12 + B callbackasm1(SB) + MOVD $918, R12 + B callbackasm1(SB) + MOVD $919, R12 + B callbackasm1(SB) + MOVD $920, R12 + B callbackasm1(SB) + MOVD $921, R12 + B callbackasm1(SB) + MOVD $922, R12 + B callbackasm1(SB) + MOVD $923, R12 + B callbackasm1(SB) + MOVD $924, R12 + B callbackasm1(SB) + MOVD $925, R12 + B callbackasm1(SB) + MOVD $926, R12 + B callbackasm1(SB) + MOVD $927, R12 + B callbackasm1(SB) + MOVD $928, R12 + B callbackasm1(SB) + MOVD $929, R12 + B callbackasm1(SB) + MOVD $930, R12 + B callbackasm1(SB) + MOVD $931, R12 + B callbackasm1(SB) + MOVD $932, R12 + B callbackasm1(SB) + MOVD $933, R12 + B callbackasm1(SB) + MOVD $934, R12 + B callbackasm1(SB) + MOVD $935, R12 + B callbackasm1(SB) + MOVD $936, R12 + B callbackasm1(SB) + MOVD $937, R12 + B callbackasm1(SB) + MOVD $938, R12 + B callbackasm1(SB) + MOVD $939, R12 + B callbackasm1(SB) + MOVD $940, R12 + B callbackasm1(SB) + MOVD $941, R12 + B callbackasm1(SB) + MOVD $942, R12 + B callbackasm1(SB) + MOVD $943, R12 + B callbackasm1(SB) + MOVD $944, R12 + B callbackasm1(SB) + MOVD $945, R12 + B callbackasm1(SB) + MOVD $946, R12 + B callbackasm1(SB) + MOVD $947, R12 + B callbackasm1(SB) + MOVD $948, R12 + B callbackasm1(SB) + MOVD $949, R12 + B callbackasm1(SB) + MOVD $950, R12 + B callbackasm1(SB) + MOVD $951, R12 + B callbackasm1(SB) + MOVD $952, R12 + B callbackasm1(SB) + MOVD $953, R12 + B callbackasm1(SB) + MOVD $954, R12 + B callbackasm1(SB) + MOVD $955, R12 + B callbackasm1(SB) + MOVD $956, R12 + B callbackasm1(SB) + MOVD $957, R12 + B callbackasm1(SB) + MOVD $958, R12 + B callbackasm1(SB) + MOVD $959, R12 + B callbackasm1(SB) + MOVD $960, R12 + B callbackasm1(SB) + MOVD $961, R12 + B callbackasm1(SB) + MOVD $962, R12 + B callbackasm1(SB) + MOVD $963, R12 + B callbackasm1(SB) + MOVD $964, R12 + B callbackasm1(SB) + MOVD $965, R12 + B callbackasm1(SB) + MOVD $966, R12 + B callbackasm1(SB) + MOVD $967, R12 + B callbackasm1(SB) + MOVD $968, R12 + B callbackasm1(SB) + MOVD $969, R12 + B callbackasm1(SB) + MOVD $970, R12 + B callbackasm1(SB) + MOVD $971, R12 + B callbackasm1(SB) + MOVD $972, R12 + B callbackasm1(SB) + MOVD $973, R12 + B callbackasm1(SB) + MOVD $974, R12 + B callbackasm1(SB) + MOVD $975, R12 + B callbackasm1(SB) + MOVD $976, R12 + B callbackasm1(SB) + MOVD $977, R12 + B callbackasm1(SB) + MOVD $978, R12 + B callbackasm1(SB) + MOVD $979, R12 + B callbackasm1(SB) + MOVD $980, R12 + B callbackasm1(SB) + MOVD $981, R12 + B callbackasm1(SB) + MOVD $982, R12 + B callbackasm1(SB) + MOVD $983, R12 + B callbackasm1(SB) + MOVD $984, R12 + B callbackasm1(SB) + MOVD $985, R12 + B callbackasm1(SB) + MOVD $986, R12 + B callbackasm1(SB) + MOVD $987, R12 + B callbackasm1(SB) + MOVD $988, R12 + B callbackasm1(SB) + MOVD $989, R12 + B callbackasm1(SB) + MOVD $990, R12 + B callbackasm1(SB) + MOVD $991, R12 + B callbackasm1(SB) + MOVD $992, R12 + B callbackasm1(SB) + MOVD $993, R12 + B callbackasm1(SB) + MOVD $994, R12 + B callbackasm1(SB) + MOVD $995, R12 + B callbackasm1(SB) + MOVD $996, R12 + B callbackasm1(SB) + MOVD $997, R12 + B callbackasm1(SB) + MOVD $998, R12 + B callbackasm1(SB) + MOVD $999, R12 + B callbackasm1(SB) + MOVD $1000, R12 + B callbackasm1(SB) + MOVD $1001, R12 + B callbackasm1(SB) + MOVD $1002, R12 + B callbackasm1(SB) + MOVD $1003, R12 + B callbackasm1(SB) + MOVD $1004, R12 + B callbackasm1(SB) + MOVD $1005, R12 + B callbackasm1(SB) + MOVD $1006, R12 + B callbackasm1(SB) + MOVD $1007, R12 + B callbackasm1(SB) + MOVD $1008, R12 + B callbackasm1(SB) + MOVD $1009, R12 + B callbackasm1(SB) + MOVD $1010, R12 + B callbackasm1(SB) + MOVD $1011, R12 + B callbackasm1(SB) + MOVD $1012, R12 + B callbackasm1(SB) + MOVD $1013, R12 + B callbackasm1(SB) + MOVD $1014, R12 + B callbackasm1(SB) + MOVD $1015, R12 + B callbackasm1(SB) + MOVD $1016, R12 + B callbackasm1(SB) + MOVD $1017, R12 + B callbackasm1(SB) + MOVD $1018, R12 + B callbackasm1(SB) + MOVD $1019, R12 + B callbackasm1(SB) + MOVD $1020, R12 + B callbackasm1(SB) + MOVD $1021, R12 + B callbackasm1(SB) + MOVD $1022, R12 + B callbackasm1(SB) + MOVD $1023, R12 + B callbackasm1(SB) + MOVD $1024, R12 + B callbackasm1(SB) + MOVD $1025, R12 + B callbackasm1(SB) + MOVD $1026, R12 + B callbackasm1(SB) + MOVD $1027, R12 + B callbackasm1(SB) + MOVD $1028, R12 + B callbackasm1(SB) + MOVD $1029, R12 + B callbackasm1(SB) + MOVD $1030, R12 + B callbackasm1(SB) + MOVD $1031, R12 + B callbackasm1(SB) + MOVD $1032, R12 + B callbackasm1(SB) + MOVD $1033, R12 + B callbackasm1(SB) + MOVD $1034, R12 + B callbackasm1(SB) + MOVD $1035, R12 + B callbackasm1(SB) + MOVD $1036, R12 + B callbackasm1(SB) + MOVD $1037, R12 + B callbackasm1(SB) + MOVD $1038, R12 + B callbackasm1(SB) + MOVD $1039, R12 + B callbackasm1(SB) + MOVD $1040, R12 + B callbackasm1(SB) + MOVD $1041, R12 + B callbackasm1(SB) + MOVD $1042, R12 + B callbackasm1(SB) + MOVD $1043, R12 + B callbackasm1(SB) + MOVD $1044, R12 + B callbackasm1(SB) + MOVD $1045, R12 + B callbackasm1(SB) + MOVD $1046, R12 + B callbackasm1(SB) + MOVD $1047, R12 + B callbackasm1(SB) + MOVD $1048, R12 + B callbackasm1(SB) + MOVD $1049, R12 + B callbackasm1(SB) + MOVD $1050, R12 + B callbackasm1(SB) + MOVD $1051, R12 + B callbackasm1(SB) + MOVD $1052, R12 + B callbackasm1(SB) + MOVD $1053, R12 + B callbackasm1(SB) + MOVD $1054, R12 + B callbackasm1(SB) + MOVD $1055, R12 + B callbackasm1(SB) + MOVD $1056, R12 + B callbackasm1(SB) + MOVD $1057, R12 + B callbackasm1(SB) + MOVD $1058, R12 + B callbackasm1(SB) + MOVD $1059, R12 + B callbackasm1(SB) + MOVD $1060, R12 + B callbackasm1(SB) + MOVD $1061, R12 + B callbackasm1(SB) + MOVD $1062, R12 + B callbackasm1(SB) + MOVD $1063, R12 + B callbackasm1(SB) + MOVD $1064, R12 + B callbackasm1(SB) + MOVD $1065, R12 + B callbackasm1(SB) + MOVD $1066, R12 + B callbackasm1(SB) + MOVD $1067, R12 + B callbackasm1(SB) + MOVD $1068, R12 + B callbackasm1(SB) + MOVD $1069, R12 + B callbackasm1(SB) + MOVD $1070, R12 + B callbackasm1(SB) + MOVD $1071, R12 + B callbackasm1(SB) + MOVD $1072, R12 + B callbackasm1(SB) + MOVD $1073, R12 + B callbackasm1(SB) + MOVD $1074, R12 + B callbackasm1(SB) + MOVD $1075, R12 + B callbackasm1(SB) + MOVD $1076, R12 + B callbackasm1(SB) + MOVD $1077, R12 + B callbackasm1(SB) + MOVD $1078, R12 + B callbackasm1(SB) + MOVD $1079, R12 + B callbackasm1(SB) + MOVD $1080, R12 + B callbackasm1(SB) + MOVD $1081, R12 + B callbackasm1(SB) + MOVD $1082, R12 + B callbackasm1(SB) + MOVD $1083, R12 + B callbackasm1(SB) + MOVD $1084, R12 + B callbackasm1(SB) + MOVD $1085, R12 + B callbackasm1(SB) + MOVD $1086, R12 + B callbackasm1(SB) + MOVD $1087, R12 + B callbackasm1(SB) + MOVD $1088, R12 + B callbackasm1(SB) + MOVD $1089, R12 + B callbackasm1(SB) + MOVD $1090, R12 + B callbackasm1(SB) + MOVD $1091, R12 + B callbackasm1(SB) + MOVD $1092, R12 + B callbackasm1(SB) + MOVD $1093, R12 + B callbackasm1(SB) + MOVD $1094, R12 + B callbackasm1(SB) + MOVD $1095, R12 + B callbackasm1(SB) + MOVD $1096, R12 + B callbackasm1(SB) + MOVD $1097, R12 + B callbackasm1(SB) + MOVD $1098, R12 + B callbackasm1(SB) + MOVD $1099, R12 + B callbackasm1(SB) + MOVD $1100, R12 + B callbackasm1(SB) + MOVD $1101, R12 + B callbackasm1(SB) + MOVD $1102, R12 + B callbackasm1(SB) + MOVD $1103, R12 + B callbackasm1(SB) + MOVD $1104, R12 + B callbackasm1(SB) + MOVD $1105, R12 + B callbackasm1(SB) + MOVD $1106, R12 + B callbackasm1(SB) + MOVD $1107, R12 + B callbackasm1(SB) + MOVD $1108, R12 + B callbackasm1(SB) + MOVD $1109, R12 + B callbackasm1(SB) + MOVD $1110, R12 + B callbackasm1(SB) + MOVD $1111, R12 + B callbackasm1(SB) + MOVD $1112, R12 + B callbackasm1(SB) + MOVD $1113, R12 + B callbackasm1(SB) + MOVD $1114, R12 + B callbackasm1(SB) + MOVD $1115, R12 + B callbackasm1(SB) + MOVD $1116, R12 + B callbackasm1(SB) + MOVD $1117, R12 + B callbackasm1(SB) + MOVD $1118, R12 + B callbackasm1(SB) + MOVD $1119, R12 + B callbackasm1(SB) + MOVD $1120, R12 + B callbackasm1(SB) + MOVD $1121, R12 + B callbackasm1(SB) + MOVD $1122, R12 + B callbackasm1(SB) + MOVD $1123, R12 + B callbackasm1(SB) + MOVD $1124, R12 + B callbackasm1(SB) + MOVD $1125, R12 + B callbackasm1(SB) + MOVD $1126, R12 + B callbackasm1(SB) + MOVD $1127, R12 + B callbackasm1(SB) + MOVD $1128, R12 + B callbackasm1(SB) + MOVD $1129, R12 + B callbackasm1(SB) + MOVD $1130, R12 + B callbackasm1(SB) + MOVD $1131, R12 + B callbackasm1(SB) + MOVD $1132, R12 + B callbackasm1(SB) + MOVD $1133, R12 + B callbackasm1(SB) + MOVD $1134, R12 + B callbackasm1(SB) + MOVD $1135, R12 + B callbackasm1(SB) + MOVD $1136, R12 + B callbackasm1(SB) + MOVD $1137, R12 + B callbackasm1(SB) + MOVD $1138, R12 + B callbackasm1(SB) + MOVD $1139, R12 + B callbackasm1(SB) + MOVD $1140, R12 + B callbackasm1(SB) + MOVD $1141, R12 + B callbackasm1(SB) + MOVD $1142, R12 + B callbackasm1(SB) + MOVD $1143, R12 + B callbackasm1(SB) + MOVD $1144, R12 + B callbackasm1(SB) + MOVD $1145, R12 + B callbackasm1(SB) + MOVD $1146, R12 + B callbackasm1(SB) + MOVD $1147, R12 + B callbackasm1(SB) + MOVD $1148, R12 + B callbackasm1(SB) + MOVD $1149, R12 + B callbackasm1(SB) + MOVD $1150, R12 + B callbackasm1(SB) + MOVD $1151, R12 + B callbackasm1(SB) + MOVD $1152, R12 + B callbackasm1(SB) + MOVD $1153, R12 + B callbackasm1(SB) + MOVD $1154, R12 + B callbackasm1(SB) + MOVD $1155, R12 + B callbackasm1(SB) + MOVD $1156, R12 + B callbackasm1(SB) + MOVD $1157, R12 + B callbackasm1(SB) + MOVD $1158, R12 + B callbackasm1(SB) + MOVD $1159, R12 + B callbackasm1(SB) + MOVD $1160, R12 + B callbackasm1(SB) + MOVD $1161, R12 + B callbackasm1(SB) + MOVD $1162, R12 + B callbackasm1(SB) + MOVD $1163, R12 + B callbackasm1(SB) + MOVD $1164, R12 + B callbackasm1(SB) + MOVD $1165, R12 + B callbackasm1(SB) + MOVD $1166, R12 + B callbackasm1(SB) + MOVD $1167, R12 + B callbackasm1(SB) + MOVD $1168, R12 + B callbackasm1(SB) + MOVD $1169, R12 + B callbackasm1(SB) + MOVD $1170, R12 + B callbackasm1(SB) + MOVD $1171, R12 + B callbackasm1(SB) + MOVD $1172, R12 + B callbackasm1(SB) + MOVD $1173, R12 + B callbackasm1(SB) + MOVD $1174, R12 + B callbackasm1(SB) + MOVD $1175, R12 + B callbackasm1(SB) + MOVD $1176, R12 + B callbackasm1(SB) + MOVD $1177, R12 + B callbackasm1(SB) + MOVD $1178, R12 + B callbackasm1(SB) + MOVD $1179, R12 + B callbackasm1(SB) + MOVD $1180, R12 + B callbackasm1(SB) + MOVD $1181, R12 + B callbackasm1(SB) + MOVD $1182, R12 + B callbackasm1(SB) + MOVD $1183, R12 + B callbackasm1(SB) + MOVD $1184, R12 + B callbackasm1(SB) + MOVD $1185, R12 + B callbackasm1(SB) + MOVD $1186, R12 + B callbackasm1(SB) + MOVD $1187, R12 + B callbackasm1(SB) + MOVD $1188, R12 + B callbackasm1(SB) + MOVD $1189, R12 + B callbackasm1(SB) + MOVD $1190, R12 + B callbackasm1(SB) + MOVD $1191, R12 + B callbackasm1(SB) + MOVD $1192, R12 + B callbackasm1(SB) + MOVD $1193, R12 + B callbackasm1(SB) + MOVD $1194, R12 + B callbackasm1(SB) + MOVD $1195, R12 + B callbackasm1(SB) + MOVD $1196, R12 + B callbackasm1(SB) + MOVD $1197, R12 + B callbackasm1(SB) + MOVD $1198, R12 + B callbackasm1(SB) + MOVD $1199, R12 + B callbackasm1(SB) + MOVD $1200, R12 + B callbackasm1(SB) + MOVD $1201, R12 + B callbackasm1(SB) + MOVD $1202, R12 + B callbackasm1(SB) + MOVD $1203, R12 + B callbackasm1(SB) + MOVD $1204, R12 + B callbackasm1(SB) + MOVD $1205, R12 + B callbackasm1(SB) + MOVD $1206, R12 + B callbackasm1(SB) + MOVD $1207, R12 + B callbackasm1(SB) + MOVD $1208, R12 + B callbackasm1(SB) + MOVD $1209, R12 + B callbackasm1(SB) + MOVD $1210, R12 + B callbackasm1(SB) + MOVD $1211, R12 + B callbackasm1(SB) + MOVD $1212, R12 + B callbackasm1(SB) + MOVD $1213, R12 + B callbackasm1(SB) + MOVD $1214, R12 + B callbackasm1(SB) + MOVD $1215, R12 + B callbackasm1(SB) + MOVD $1216, R12 + B callbackasm1(SB) + MOVD $1217, R12 + B callbackasm1(SB) + MOVD $1218, R12 + B callbackasm1(SB) + MOVD $1219, R12 + B callbackasm1(SB) + MOVD $1220, R12 + B callbackasm1(SB) + MOVD $1221, R12 + B callbackasm1(SB) + MOVD $1222, R12 + B callbackasm1(SB) + MOVD $1223, R12 + B callbackasm1(SB) + MOVD $1224, R12 + B callbackasm1(SB) + MOVD $1225, R12 + B callbackasm1(SB) + MOVD $1226, R12 + B callbackasm1(SB) + MOVD $1227, R12 + B callbackasm1(SB) + MOVD $1228, R12 + B callbackasm1(SB) + MOVD $1229, R12 + B callbackasm1(SB) + MOVD $1230, R12 + B callbackasm1(SB) + MOVD $1231, R12 + B callbackasm1(SB) + MOVD $1232, R12 + B callbackasm1(SB) + MOVD $1233, R12 + B callbackasm1(SB) + MOVD $1234, R12 + B callbackasm1(SB) + MOVD $1235, R12 + B callbackasm1(SB) + MOVD $1236, R12 + B callbackasm1(SB) + MOVD $1237, R12 + B callbackasm1(SB) + MOVD $1238, R12 + B callbackasm1(SB) + MOVD $1239, R12 + B callbackasm1(SB) + MOVD $1240, R12 + B callbackasm1(SB) + MOVD $1241, R12 + B callbackasm1(SB) + MOVD $1242, R12 + B callbackasm1(SB) + MOVD $1243, R12 + B callbackasm1(SB) + MOVD $1244, R12 + B callbackasm1(SB) + MOVD $1245, R12 + B callbackasm1(SB) + MOVD $1246, R12 + B callbackasm1(SB) + MOVD $1247, R12 + B callbackasm1(SB) + MOVD $1248, R12 + B callbackasm1(SB) + MOVD $1249, R12 + B callbackasm1(SB) + MOVD $1250, R12 + B callbackasm1(SB) + MOVD $1251, R12 + B callbackasm1(SB) + MOVD $1252, R12 + B callbackasm1(SB) + MOVD $1253, R12 + B callbackasm1(SB) + MOVD $1254, R12 + B callbackasm1(SB) + MOVD $1255, R12 + B callbackasm1(SB) + MOVD $1256, R12 + B callbackasm1(SB) + MOVD $1257, R12 + B callbackasm1(SB) + MOVD $1258, R12 + B callbackasm1(SB) + MOVD $1259, R12 + B callbackasm1(SB) + MOVD $1260, R12 + B callbackasm1(SB) + MOVD $1261, R12 + B callbackasm1(SB) + MOVD $1262, R12 + B callbackasm1(SB) + MOVD $1263, R12 + B callbackasm1(SB) + MOVD $1264, R12 + B callbackasm1(SB) + MOVD $1265, R12 + B callbackasm1(SB) + MOVD $1266, R12 + B callbackasm1(SB) + MOVD $1267, R12 + B callbackasm1(SB) + MOVD $1268, R12 + B callbackasm1(SB) + MOVD $1269, R12 + B callbackasm1(SB) + MOVD $1270, R12 + B callbackasm1(SB) + MOVD $1271, R12 + B callbackasm1(SB) + MOVD $1272, R12 + B callbackasm1(SB) + MOVD $1273, R12 + B callbackasm1(SB) + MOVD $1274, R12 + B callbackasm1(SB) + MOVD $1275, R12 + B callbackasm1(SB) + MOVD $1276, R12 + B callbackasm1(SB) + MOVD $1277, R12 + B callbackasm1(SB) + MOVD $1278, R12 + B callbackasm1(SB) + MOVD $1279, R12 + B callbackasm1(SB) + MOVD $1280, R12 + B callbackasm1(SB) + MOVD $1281, R12 + B callbackasm1(SB) + MOVD $1282, R12 + B callbackasm1(SB) + MOVD $1283, R12 + B callbackasm1(SB) + MOVD $1284, R12 + B callbackasm1(SB) + MOVD $1285, R12 + B callbackasm1(SB) + MOVD $1286, R12 + B callbackasm1(SB) + MOVD $1287, R12 + B callbackasm1(SB) + MOVD $1288, R12 + B callbackasm1(SB) + MOVD $1289, R12 + B callbackasm1(SB) + MOVD $1290, R12 + B callbackasm1(SB) + MOVD $1291, R12 + B callbackasm1(SB) + MOVD $1292, R12 + B callbackasm1(SB) + MOVD $1293, R12 + B callbackasm1(SB) + MOVD $1294, R12 + B callbackasm1(SB) + MOVD $1295, R12 + B callbackasm1(SB) + MOVD $1296, R12 + B callbackasm1(SB) + MOVD $1297, R12 + B callbackasm1(SB) + MOVD $1298, R12 + B callbackasm1(SB) + MOVD $1299, R12 + B callbackasm1(SB) + MOVD $1300, R12 + B callbackasm1(SB) + MOVD $1301, R12 + B callbackasm1(SB) + MOVD $1302, R12 + B callbackasm1(SB) + MOVD $1303, R12 + B callbackasm1(SB) + MOVD $1304, R12 + B callbackasm1(SB) + MOVD $1305, R12 + B callbackasm1(SB) + MOVD $1306, R12 + B callbackasm1(SB) + MOVD $1307, R12 + B callbackasm1(SB) + MOVD $1308, R12 + B callbackasm1(SB) + MOVD $1309, R12 + B callbackasm1(SB) + MOVD $1310, R12 + B callbackasm1(SB) + MOVD $1311, R12 + B callbackasm1(SB) + MOVD $1312, R12 + B callbackasm1(SB) + MOVD $1313, R12 + B callbackasm1(SB) + MOVD $1314, R12 + B callbackasm1(SB) + MOVD $1315, R12 + B callbackasm1(SB) + MOVD $1316, R12 + B callbackasm1(SB) + MOVD $1317, R12 + B callbackasm1(SB) + MOVD $1318, R12 + B callbackasm1(SB) + MOVD $1319, R12 + B callbackasm1(SB) + MOVD $1320, R12 + B callbackasm1(SB) + MOVD $1321, R12 + B callbackasm1(SB) + MOVD $1322, R12 + B callbackasm1(SB) + MOVD $1323, R12 + B callbackasm1(SB) + MOVD $1324, R12 + B callbackasm1(SB) + MOVD $1325, R12 + B callbackasm1(SB) + MOVD $1326, R12 + B callbackasm1(SB) + MOVD $1327, R12 + B callbackasm1(SB) + MOVD $1328, R12 + B callbackasm1(SB) + MOVD $1329, R12 + B callbackasm1(SB) + MOVD $1330, R12 + B callbackasm1(SB) + MOVD $1331, R12 + B callbackasm1(SB) + MOVD $1332, R12 + B callbackasm1(SB) + MOVD $1333, R12 + B callbackasm1(SB) + MOVD $1334, R12 + B callbackasm1(SB) + MOVD $1335, R12 + B callbackasm1(SB) + MOVD $1336, R12 + B callbackasm1(SB) + MOVD $1337, R12 + B callbackasm1(SB) + MOVD $1338, R12 + B callbackasm1(SB) + MOVD $1339, R12 + B callbackasm1(SB) + MOVD $1340, R12 + B callbackasm1(SB) + MOVD $1341, R12 + B callbackasm1(SB) + MOVD $1342, R12 + B callbackasm1(SB) + MOVD $1343, R12 + B callbackasm1(SB) + MOVD $1344, R12 + B callbackasm1(SB) + MOVD $1345, R12 + B callbackasm1(SB) + MOVD $1346, R12 + B callbackasm1(SB) + MOVD $1347, R12 + B callbackasm1(SB) + MOVD $1348, R12 + B callbackasm1(SB) + MOVD $1349, R12 + B callbackasm1(SB) + MOVD $1350, R12 + B callbackasm1(SB) + MOVD $1351, R12 + B callbackasm1(SB) + MOVD $1352, R12 + B callbackasm1(SB) + MOVD $1353, R12 + B callbackasm1(SB) + MOVD $1354, R12 + B callbackasm1(SB) + MOVD $1355, R12 + B callbackasm1(SB) + MOVD $1356, R12 + B callbackasm1(SB) + MOVD $1357, R12 + B callbackasm1(SB) + MOVD $1358, R12 + B callbackasm1(SB) + MOVD $1359, R12 + B callbackasm1(SB) + MOVD $1360, R12 + B callbackasm1(SB) + MOVD $1361, R12 + B callbackasm1(SB) + MOVD $1362, R12 + B callbackasm1(SB) + MOVD $1363, R12 + B callbackasm1(SB) + MOVD $1364, R12 + B callbackasm1(SB) + MOVD $1365, R12 + B callbackasm1(SB) + MOVD $1366, R12 + B callbackasm1(SB) + MOVD $1367, R12 + B callbackasm1(SB) + MOVD $1368, R12 + B callbackasm1(SB) + MOVD $1369, R12 + B callbackasm1(SB) + MOVD $1370, R12 + B callbackasm1(SB) + MOVD $1371, R12 + B callbackasm1(SB) + MOVD $1372, R12 + B callbackasm1(SB) + MOVD $1373, R12 + B callbackasm1(SB) + MOVD $1374, R12 + B callbackasm1(SB) + MOVD $1375, R12 + B callbackasm1(SB) + MOVD $1376, R12 + B callbackasm1(SB) + MOVD $1377, R12 + B callbackasm1(SB) + MOVD $1378, R12 + B callbackasm1(SB) + MOVD $1379, R12 + B callbackasm1(SB) + MOVD $1380, R12 + B callbackasm1(SB) + MOVD $1381, R12 + B callbackasm1(SB) + MOVD $1382, R12 + B callbackasm1(SB) + MOVD $1383, R12 + B callbackasm1(SB) + MOVD $1384, R12 + B callbackasm1(SB) + MOVD $1385, R12 + B callbackasm1(SB) + MOVD $1386, R12 + B callbackasm1(SB) + MOVD $1387, R12 + B callbackasm1(SB) + MOVD $1388, R12 + B callbackasm1(SB) + MOVD $1389, R12 + B callbackasm1(SB) + MOVD $1390, R12 + B callbackasm1(SB) + MOVD $1391, R12 + B callbackasm1(SB) + MOVD $1392, R12 + B callbackasm1(SB) + MOVD $1393, R12 + B callbackasm1(SB) + MOVD $1394, R12 + B callbackasm1(SB) + MOVD $1395, R12 + B callbackasm1(SB) + MOVD $1396, R12 + B callbackasm1(SB) + MOVD $1397, R12 + B callbackasm1(SB) + MOVD $1398, R12 + B callbackasm1(SB) + MOVD $1399, R12 + B callbackasm1(SB) + MOVD $1400, R12 + B callbackasm1(SB) + MOVD $1401, R12 + B callbackasm1(SB) + MOVD $1402, R12 + B callbackasm1(SB) + MOVD $1403, R12 + B callbackasm1(SB) + MOVD $1404, R12 + B callbackasm1(SB) + MOVD $1405, R12 + B callbackasm1(SB) + MOVD $1406, R12 + B callbackasm1(SB) + MOVD $1407, R12 + B callbackasm1(SB) + MOVD $1408, R12 + B callbackasm1(SB) + MOVD $1409, R12 + B callbackasm1(SB) + MOVD $1410, R12 + B callbackasm1(SB) + MOVD $1411, R12 + B callbackasm1(SB) + MOVD $1412, R12 + B callbackasm1(SB) + MOVD $1413, R12 + B callbackasm1(SB) + MOVD $1414, R12 + B callbackasm1(SB) + MOVD $1415, R12 + B callbackasm1(SB) + MOVD $1416, R12 + B callbackasm1(SB) + MOVD $1417, R12 + B callbackasm1(SB) + MOVD $1418, R12 + B callbackasm1(SB) + MOVD $1419, R12 + B callbackasm1(SB) + MOVD $1420, R12 + B callbackasm1(SB) + MOVD $1421, R12 + B callbackasm1(SB) + MOVD $1422, R12 + B callbackasm1(SB) + MOVD $1423, R12 + B callbackasm1(SB) + MOVD $1424, R12 + B callbackasm1(SB) + MOVD $1425, R12 + B callbackasm1(SB) + MOVD $1426, R12 + B callbackasm1(SB) + MOVD $1427, R12 + B callbackasm1(SB) + MOVD $1428, R12 + B callbackasm1(SB) + MOVD $1429, R12 + B callbackasm1(SB) + MOVD $1430, R12 + B callbackasm1(SB) + MOVD $1431, R12 + B callbackasm1(SB) + MOVD $1432, R12 + B callbackasm1(SB) + MOVD $1433, R12 + B callbackasm1(SB) + MOVD $1434, R12 + B callbackasm1(SB) + MOVD $1435, R12 + B callbackasm1(SB) + MOVD $1436, R12 + B callbackasm1(SB) + MOVD $1437, R12 + B callbackasm1(SB) + MOVD $1438, R12 + B callbackasm1(SB) + MOVD $1439, R12 + B callbackasm1(SB) + MOVD $1440, R12 + B callbackasm1(SB) + MOVD $1441, R12 + B callbackasm1(SB) + MOVD $1442, R12 + B callbackasm1(SB) + MOVD $1443, R12 + B callbackasm1(SB) + MOVD $1444, R12 + B callbackasm1(SB) + MOVD $1445, R12 + B callbackasm1(SB) + MOVD $1446, R12 + B callbackasm1(SB) + MOVD $1447, R12 + B callbackasm1(SB) + MOVD $1448, R12 + B callbackasm1(SB) + MOVD $1449, R12 + B callbackasm1(SB) + MOVD $1450, R12 + B callbackasm1(SB) + MOVD $1451, R12 + B callbackasm1(SB) + MOVD $1452, R12 + B callbackasm1(SB) + MOVD $1453, R12 + B callbackasm1(SB) + MOVD $1454, R12 + B callbackasm1(SB) + MOVD $1455, R12 + B callbackasm1(SB) + MOVD $1456, R12 + B callbackasm1(SB) + MOVD $1457, R12 + B callbackasm1(SB) + MOVD $1458, R12 + B callbackasm1(SB) + MOVD $1459, R12 + B callbackasm1(SB) + MOVD $1460, R12 + B callbackasm1(SB) + MOVD $1461, R12 + B callbackasm1(SB) + MOVD $1462, R12 + B callbackasm1(SB) + MOVD $1463, R12 + B callbackasm1(SB) + MOVD $1464, R12 + B callbackasm1(SB) + MOVD $1465, R12 + B callbackasm1(SB) + MOVD $1466, R12 + B callbackasm1(SB) + MOVD $1467, R12 + B callbackasm1(SB) + MOVD $1468, R12 + B callbackasm1(SB) + MOVD $1469, R12 + B callbackasm1(SB) + MOVD $1470, R12 + B callbackasm1(SB) + MOVD $1471, R12 + B callbackasm1(SB) + MOVD $1472, R12 + B callbackasm1(SB) + MOVD $1473, R12 + B callbackasm1(SB) + MOVD $1474, R12 + B callbackasm1(SB) + MOVD $1475, R12 + B callbackasm1(SB) + MOVD $1476, R12 + B callbackasm1(SB) + MOVD $1477, R12 + B callbackasm1(SB) + MOVD $1478, R12 + B callbackasm1(SB) + MOVD $1479, R12 + B callbackasm1(SB) + MOVD $1480, R12 + B callbackasm1(SB) + MOVD $1481, R12 + B callbackasm1(SB) + MOVD $1482, R12 + B callbackasm1(SB) + MOVD $1483, R12 + B callbackasm1(SB) + MOVD $1484, R12 + B callbackasm1(SB) + MOVD $1485, R12 + B callbackasm1(SB) + MOVD $1486, R12 + B callbackasm1(SB) + MOVD $1487, R12 + B callbackasm1(SB) + MOVD $1488, R12 + B callbackasm1(SB) + MOVD $1489, R12 + B callbackasm1(SB) + MOVD $1490, R12 + B callbackasm1(SB) + MOVD $1491, R12 + B callbackasm1(SB) + MOVD $1492, R12 + B callbackasm1(SB) + MOVD $1493, R12 + B callbackasm1(SB) + MOVD $1494, R12 + B callbackasm1(SB) + MOVD $1495, R12 + B callbackasm1(SB) + MOVD $1496, R12 + B callbackasm1(SB) + MOVD $1497, R12 + B callbackasm1(SB) + MOVD $1498, R12 + B callbackasm1(SB) + MOVD $1499, R12 + B callbackasm1(SB) + MOVD $1500, R12 + B callbackasm1(SB) + MOVD $1501, R12 + B callbackasm1(SB) + MOVD $1502, R12 + B callbackasm1(SB) + MOVD $1503, R12 + B callbackasm1(SB) + MOVD $1504, R12 + B callbackasm1(SB) + MOVD $1505, R12 + B callbackasm1(SB) + MOVD $1506, R12 + B callbackasm1(SB) + MOVD $1507, R12 + B callbackasm1(SB) + MOVD $1508, R12 + B callbackasm1(SB) + MOVD $1509, R12 + B callbackasm1(SB) + MOVD $1510, R12 + B callbackasm1(SB) + MOVD $1511, R12 + B callbackasm1(SB) + MOVD $1512, R12 + B callbackasm1(SB) + MOVD $1513, R12 + B callbackasm1(SB) + MOVD $1514, R12 + B callbackasm1(SB) + MOVD $1515, R12 + B callbackasm1(SB) + MOVD $1516, R12 + B callbackasm1(SB) + MOVD $1517, R12 + B callbackasm1(SB) + MOVD $1518, R12 + B callbackasm1(SB) + MOVD $1519, R12 + B callbackasm1(SB) + MOVD $1520, R12 + B callbackasm1(SB) + MOVD $1521, R12 + B callbackasm1(SB) + MOVD $1522, R12 + B callbackasm1(SB) + MOVD $1523, R12 + B callbackasm1(SB) + MOVD $1524, R12 + B callbackasm1(SB) + MOVD $1525, R12 + B callbackasm1(SB) + MOVD $1526, R12 + B callbackasm1(SB) + MOVD $1527, R12 + B callbackasm1(SB) + MOVD $1528, R12 + B callbackasm1(SB) + MOVD $1529, R12 + B callbackasm1(SB) + MOVD $1530, R12 + B callbackasm1(SB) + MOVD $1531, R12 + B callbackasm1(SB) + MOVD $1532, R12 + B callbackasm1(SB) + MOVD $1533, R12 + B callbackasm1(SB) + MOVD $1534, R12 + B callbackasm1(SB) + MOVD $1535, R12 + B callbackasm1(SB) + MOVD $1536, R12 + B callbackasm1(SB) + MOVD $1537, R12 + B callbackasm1(SB) + MOVD $1538, R12 + B callbackasm1(SB) + MOVD $1539, R12 + B callbackasm1(SB) + MOVD $1540, R12 + B callbackasm1(SB) + MOVD $1541, R12 + B callbackasm1(SB) + MOVD $1542, R12 + B callbackasm1(SB) + MOVD $1543, R12 + B callbackasm1(SB) + MOVD $1544, R12 + B callbackasm1(SB) + MOVD $1545, R12 + B callbackasm1(SB) + MOVD $1546, R12 + B callbackasm1(SB) + MOVD $1547, R12 + B callbackasm1(SB) + MOVD $1548, R12 + B callbackasm1(SB) + MOVD $1549, R12 + B callbackasm1(SB) + MOVD $1550, R12 + B callbackasm1(SB) + MOVD $1551, R12 + B callbackasm1(SB) + MOVD $1552, R12 + B callbackasm1(SB) + MOVD $1553, R12 + B callbackasm1(SB) + MOVD $1554, R12 + B callbackasm1(SB) + MOVD $1555, R12 + B callbackasm1(SB) + MOVD $1556, R12 + B callbackasm1(SB) + MOVD $1557, R12 + B callbackasm1(SB) + MOVD $1558, R12 + B callbackasm1(SB) + MOVD $1559, R12 + B callbackasm1(SB) + MOVD $1560, R12 + B callbackasm1(SB) + MOVD $1561, R12 + B callbackasm1(SB) + MOVD $1562, R12 + B callbackasm1(SB) + MOVD $1563, R12 + B callbackasm1(SB) + MOVD $1564, R12 + B callbackasm1(SB) + MOVD $1565, R12 + B callbackasm1(SB) + MOVD $1566, R12 + B callbackasm1(SB) + MOVD $1567, R12 + B callbackasm1(SB) + MOVD $1568, R12 + B callbackasm1(SB) + MOVD $1569, R12 + B callbackasm1(SB) + MOVD $1570, R12 + B callbackasm1(SB) + MOVD $1571, R12 + B callbackasm1(SB) + MOVD $1572, R12 + B callbackasm1(SB) + MOVD $1573, R12 + B callbackasm1(SB) + MOVD $1574, R12 + B callbackasm1(SB) + MOVD $1575, R12 + B callbackasm1(SB) + MOVD $1576, R12 + B callbackasm1(SB) + MOVD $1577, R12 + B callbackasm1(SB) + MOVD $1578, R12 + B callbackasm1(SB) + MOVD $1579, R12 + B callbackasm1(SB) + MOVD $1580, R12 + B callbackasm1(SB) + MOVD $1581, R12 + B callbackasm1(SB) + MOVD $1582, R12 + B callbackasm1(SB) + MOVD $1583, R12 + B callbackasm1(SB) + MOVD $1584, R12 + B callbackasm1(SB) + MOVD $1585, R12 + B callbackasm1(SB) + MOVD $1586, R12 + B callbackasm1(SB) + MOVD $1587, R12 + B callbackasm1(SB) + MOVD $1588, R12 + B callbackasm1(SB) + MOVD $1589, R12 + B callbackasm1(SB) + MOVD $1590, R12 + B callbackasm1(SB) + MOVD $1591, R12 + B callbackasm1(SB) + MOVD $1592, R12 + B callbackasm1(SB) + MOVD $1593, R12 + B callbackasm1(SB) + MOVD $1594, R12 + B callbackasm1(SB) + MOVD $1595, R12 + B callbackasm1(SB) + MOVD $1596, R12 + B callbackasm1(SB) + MOVD $1597, R12 + B callbackasm1(SB) + MOVD $1598, R12 + B callbackasm1(SB) + MOVD $1599, R12 + B callbackasm1(SB) + MOVD $1600, R12 + B callbackasm1(SB) + MOVD $1601, R12 + B callbackasm1(SB) + MOVD $1602, R12 + B callbackasm1(SB) + MOVD $1603, R12 + B callbackasm1(SB) + MOVD $1604, R12 + B callbackasm1(SB) + MOVD $1605, R12 + B callbackasm1(SB) + MOVD $1606, R12 + B callbackasm1(SB) + MOVD $1607, R12 + B callbackasm1(SB) + MOVD $1608, R12 + B callbackasm1(SB) + MOVD $1609, R12 + B callbackasm1(SB) + MOVD $1610, R12 + B callbackasm1(SB) + MOVD $1611, R12 + B callbackasm1(SB) + MOVD $1612, R12 + B callbackasm1(SB) + MOVD $1613, R12 + B callbackasm1(SB) + MOVD $1614, R12 + B callbackasm1(SB) + MOVD $1615, R12 + B callbackasm1(SB) + MOVD $1616, R12 + B callbackasm1(SB) + MOVD $1617, R12 + B callbackasm1(SB) + MOVD $1618, R12 + B callbackasm1(SB) + MOVD $1619, R12 + B callbackasm1(SB) + MOVD $1620, R12 + B callbackasm1(SB) + MOVD $1621, R12 + B callbackasm1(SB) + MOVD $1622, R12 + B callbackasm1(SB) + MOVD $1623, R12 + B callbackasm1(SB) + MOVD $1624, R12 + B callbackasm1(SB) + MOVD $1625, R12 + B callbackasm1(SB) + MOVD $1626, R12 + B callbackasm1(SB) + MOVD $1627, R12 + B callbackasm1(SB) + MOVD $1628, R12 + B callbackasm1(SB) + MOVD $1629, R12 + B callbackasm1(SB) + MOVD $1630, R12 + B callbackasm1(SB) + MOVD $1631, R12 + B callbackasm1(SB) + MOVD $1632, R12 + B callbackasm1(SB) + MOVD $1633, R12 + B callbackasm1(SB) + MOVD $1634, R12 + B callbackasm1(SB) + MOVD $1635, R12 + B callbackasm1(SB) + MOVD $1636, R12 + B callbackasm1(SB) + MOVD $1637, R12 + B callbackasm1(SB) + MOVD $1638, R12 + B callbackasm1(SB) + MOVD $1639, R12 + B callbackasm1(SB) + MOVD $1640, R12 + B callbackasm1(SB) + MOVD $1641, R12 + B callbackasm1(SB) + MOVD $1642, R12 + B callbackasm1(SB) + MOVD $1643, R12 + B callbackasm1(SB) + MOVD $1644, R12 + B callbackasm1(SB) + MOVD $1645, R12 + B callbackasm1(SB) + MOVD $1646, R12 + B callbackasm1(SB) + MOVD $1647, R12 + B callbackasm1(SB) + MOVD $1648, R12 + B callbackasm1(SB) + MOVD $1649, R12 + B callbackasm1(SB) + MOVD $1650, R12 + B callbackasm1(SB) + MOVD $1651, R12 + B callbackasm1(SB) + MOVD $1652, R12 + B callbackasm1(SB) + MOVD $1653, R12 + B callbackasm1(SB) + MOVD $1654, R12 + B callbackasm1(SB) + MOVD $1655, R12 + B callbackasm1(SB) + MOVD $1656, R12 + B callbackasm1(SB) + MOVD $1657, R12 + B callbackasm1(SB) + MOVD $1658, R12 + B callbackasm1(SB) + MOVD $1659, R12 + B callbackasm1(SB) + MOVD $1660, R12 + B callbackasm1(SB) + MOVD $1661, R12 + B callbackasm1(SB) + MOVD $1662, R12 + B callbackasm1(SB) + MOVD $1663, R12 + B callbackasm1(SB) + MOVD $1664, R12 + B callbackasm1(SB) + MOVD $1665, R12 + B callbackasm1(SB) + MOVD $1666, R12 + B callbackasm1(SB) + MOVD $1667, R12 + B callbackasm1(SB) + MOVD $1668, R12 + B callbackasm1(SB) + MOVD $1669, R12 + B callbackasm1(SB) + MOVD $1670, R12 + B callbackasm1(SB) + MOVD $1671, R12 + B callbackasm1(SB) + MOVD $1672, R12 + B callbackasm1(SB) + MOVD $1673, R12 + B callbackasm1(SB) + MOVD $1674, R12 + B callbackasm1(SB) + MOVD $1675, R12 + B callbackasm1(SB) + MOVD $1676, R12 + B callbackasm1(SB) + MOVD $1677, R12 + B callbackasm1(SB) + MOVD $1678, R12 + B callbackasm1(SB) + MOVD $1679, R12 + B callbackasm1(SB) + MOVD $1680, R12 + B callbackasm1(SB) + MOVD $1681, R12 + B callbackasm1(SB) + MOVD $1682, R12 + B callbackasm1(SB) + MOVD $1683, R12 + B callbackasm1(SB) + MOVD $1684, R12 + B callbackasm1(SB) + MOVD $1685, R12 + B callbackasm1(SB) + MOVD $1686, R12 + B callbackasm1(SB) + MOVD $1687, R12 + B callbackasm1(SB) + MOVD $1688, R12 + B callbackasm1(SB) + MOVD $1689, R12 + B callbackasm1(SB) + MOVD $1690, R12 + B callbackasm1(SB) + MOVD $1691, R12 + B callbackasm1(SB) + MOVD $1692, R12 + B callbackasm1(SB) + MOVD $1693, R12 + B callbackasm1(SB) + MOVD $1694, R12 + B callbackasm1(SB) + MOVD $1695, R12 + B callbackasm1(SB) + MOVD $1696, R12 + B callbackasm1(SB) + MOVD $1697, R12 + B callbackasm1(SB) + MOVD $1698, R12 + B callbackasm1(SB) + MOVD $1699, R12 + B callbackasm1(SB) + MOVD $1700, R12 + B callbackasm1(SB) + MOVD $1701, R12 + B callbackasm1(SB) + MOVD $1702, R12 + B callbackasm1(SB) + MOVD $1703, R12 + B callbackasm1(SB) + MOVD $1704, R12 + B callbackasm1(SB) + MOVD $1705, R12 + B callbackasm1(SB) + MOVD $1706, R12 + B callbackasm1(SB) + MOVD $1707, R12 + B callbackasm1(SB) + MOVD $1708, R12 + B callbackasm1(SB) + MOVD $1709, R12 + B callbackasm1(SB) + MOVD $1710, R12 + B callbackasm1(SB) + MOVD $1711, R12 + B callbackasm1(SB) + MOVD $1712, R12 + B callbackasm1(SB) + MOVD $1713, R12 + B callbackasm1(SB) + MOVD $1714, R12 + B callbackasm1(SB) + MOVD $1715, R12 + B callbackasm1(SB) + MOVD $1716, R12 + B callbackasm1(SB) + MOVD $1717, R12 + B callbackasm1(SB) + MOVD $1718, R12 + B callbackasm1(SB) + MOVD $1719, R12 + B callbackasm1(SB) + MOVD $1720, R12 + B callbackasm1(SB) + MOVD $1721, R12 + B callbackasm1(SB) + MOVD $1722, R12 + B callbackasm1(SB) + MOVD $1723, R12 + B callbackasm1(SB) + MOVD $1724, R12 + B callbackasm1(SB) + MOVD $1725, R12 + B callbackasm1(SB) + MOVD $1726, R12 + B callbackasm1(SB) + MOVD $1727, R12 + B callbackasm1(SB) + MOVD $1728, R12 + B callbackasm1(SB) + MOVD $1729, R12 + B callbackasm1(SB) + MOVD $1730, R12 + B callbackasm1(SB) + MOVD $1731, R12 + B callbackasm1(SB) + MOVD $1732, R12 + B callbackasm1(SB) + MOVD $1733, R12 + B callbackasm1(SB) + MOVD $1734, R12 + B callbackasm1(SB) + MOVD $1735, R12 + B callbackasm1(SB) + MOVD $1736, R12 + B callbackasm1(SB) + MOVD $1737, R12 + B callbackasm1(SB) + MOVD $1738, R12 + B callbackasm1(SB) + MOVD $1739, R12 + B callbackasm1(SB) + MOVD $1740, R12 + B callbackasm1(SB) + MOVD $1741, R12 + B callbackasm1(SB) + MOVD $1742, R12 + B callbackasm1(SB) + MOVD $1743, R12 + B callbackasm1(SB) + MOVD $1744, R12 + B callbackasm1(SB) + MOVD $1745, R12 + B callbackasm1(SB) + MOVD $1746, R12 + B callbackasm1(SB) + MOVD $1747, R12 + B callbackasm1(SB) + MOVD $1748, R12 + B callbackasm1(SB) + MOVD $1749, R12 + B callbackasm1(SB) + MOVD $1750, R12 + B callbackasm1(SB) + MOVD $1751, R12 + B callbackasm1(SB) + MOVD $1752, R12 + B callbackasm1(SB) + MOVD $1753, R12 + B callbackasm1(SB) + MOVD $1754, R12 + B callbackasm1(SB) + MOVD $1755, R12 + B callbackasm1(SB) + MOVD $1756, R12 + B callbackasm1(SB) + MOVD $1757, R12 + B callbackasm1(SB) + MOVD $1758, R12 + B callbackasm1(SB) + MOVD $1759, R12 + B callbackasm1(SB) + MOVD $1760, R12 + B callbackasm1(SB) + MOVD $1761, R12 + B callbackasm1(SB) + MOVD $1762, R12 + B callbackasm1(SB) + MOVD $1763, R12 + B callbackasm1(SB) + MOVD $1764, R12 + B callbackasm1(SB) + MOVD $1765, R12 + B callbackasm1(SB) + MOVD $1766, R12 + B callbackasm1(SB) + MOVD $1767, R12 + B callbackasm1(SB) + MOVD $1768, R12 + B callbackasm1(SB) + MOVD $1769, R12 + B callbackasm1(SB) + MOVD $1770, R12 + B callbackasm1(SB) + MOVD $1771, R12 + B callbackasm1(SB) + MOVD $1772, R12 + B callbackasm1(SB) + MOVD $1773, R12 + B callbackasm1(SB) + MOVD $1774, R12 + B callbackasm1(SB) + MOVD $1775, R12 + B callbackasm1(SB) + MOVD $1776, R12 + B callbackasm1(SB) + MOVD $1777, R12 + B callbackasm1(SB) + MOVD $1778, R12 + B callbackasm1(SB) + MOVD $1779, R12 + B callbackasm1(SB) + MOVD $1780, R12 + B callbackasm1(SB) + MOVD $1781, R12 + B callbackasm1(SB) + MOVD $1782, R12 + B callbackasm1(SB) + MOVD $1783, R12 + B callbackasm1(SB) + MOVD $1784, R12 + B callbackasm1(SB) + MOVD $1785, R12 + B callbackasm1(SB) + MOVD $1786, R12 + B callbackasm1(SB) + MOVD $1787, R12 + B callbackasm1(SB) + MOVD $1788, R12 + B callbackasm1(SB) + MOVD $1789, R12 + B callbackasm1(SB) + MOVD $1790, R12 + B callbackasm1(SB) + MOVD $1791, R12 + B callbackasm1(SB) + MOVD $1792, R12 + B callbackasm1(SB) + MOVD $1793, R12 + B callbackasm1(SB) + MOVD $1794, R12 + B callbackasm1(SB) + MOVD $1795, R12 + B callbackasm1(SB) + MOVD $1796, R12 + B callbackasm1(SB) + MOVD $1797, R12 + B callbackasm1(SB) + MOVD $1798, R12 + B callbackasm1(SB) + MOVD $1799, R12 + B callbackasm1(SB) + MOVD $1800, R12 + B callbackasm1(SB) + MOVD $1801, R12 + B callbackasm1(SB) + MOVD $1802, R12 + B callbackasm1(SB) + MOVD $1803, R12 + B callbackasm1(SB) + MOVD $1804, R12 + B callbackasm1(SB) + MOVD $1805, R12 + B callbackasm1(SB) + MOVD $1806, R12 + B callbackasm1(SB) + MOVD $1807, R12 + B callbackasm1(SB) + MOVD $1808, R12 + B callbackasm1(SB) + MOVD $1809, R12 + B callbackasm1(SB) + MOVD $1810, R12 + B callbackasm1(SB) + MOVD $1811, R12 + B callbackasm1(SB) + MOVD $1812, R12 + B callbackasm1(SB) + MOVD $1813, R12 + B callbackasm1(SB) + MOVD $1814, R12 + B callbackasm1(SB) + MOVD $1815, R12 + B callbackasm1(SB) + MOVD $1816, R12 + B callbackasm1(SB) + MOVD $1817, R12 + B callbackasm1(SB) + MOVD $1818, R12 + B callbackasm1(SB) + MOVD $1819, R12 + B callbackasm1(SB) + MOVD $1820, R12 + B callbackasm1(SB) + MOVD $1821, R12 + B callbackasm1(SB) + MOVD $1822, R12 + B callbackasm1(SB) + MOVD $1823, R12 + B callbackasm1(SB) + MOVD $1824, R12 + B callbackasm1(SB) + MOVD $1825, R12 + B callbackasm1(SB) + MOVD $1826, R12 + B callbackasm1(SB) + MOVD $1827, R12 + B callbackasm1(SB) + MOVD $1828, R12 + B callbackasm1(SB) + MOVD $1829, R12 + B callbackasm1(SB) + MOVD $1830, R12 + B callbackasm1(SB) + MOVD $1831, R12 + B callbackasm1(SB) + MOVD $1832, R12 + B callbackasm1(SB) + MOVD $1833, R12 + B callbackasm1(SB) + MOVD $1834, R12 + B callbackasm1(SB) + MOVD $1835, R12 + B callbackasm1(SB) + MOVD $1836, R12 + B callbackasm1(SB) + MOVD $1837, R12 + B callbackasm1(SB) + MOVD $1838, R12 + B callbackasm1(SB) + MOVD $1839, R12 + B callbackasm1(SB) + MOVD $1840, R12 + B callbackasm1(SB) + MOVD $1841, R12 + B callbackasm1(SB) + MOVD $1842, R12 + B callbackasm1(SB) + MOVD $1843, R12 + B callbackasm1(SB) + MOVD $1844, R12 + B callbackasm1(SB) + MOVD $1845, R12 + B callbackasm1(SB) + MOVD $1846, R12 + B callbackasm1(SB) + MOVD $1847, R12 + B callbackasm1(SB) + MOVD $1848, R12 + B callbackasm1(SB) + MOVD $1849, R12 + B callbackasm1(SB) + MOVD $1850, R12 + B callbackasm1(SB) + MOVD $1851, R12 + B callbackasm1(SB) + MOVD $1852, R12 + B callbackasm1(SB) + MOVD $1853, R12 + B callbackasm1(SB) + MOVD $1854, R12 + B callbackasm1(SB) + MOVD $1855, R12 + B callbackasm1(SB) + MOVD $1856, R12 + B callbackasm1(SB) + MOVD $1857, R12 + B callbackasm1(SB) + MOVD $1858, R12 + B callbackasm1(SB) + MOVD $1859, R12 + B callbackasm1(SB) + MOVD $1860, R12 + B callbackasm1(SB) + MOVD $1861, R12 + B callbackasm1(SB) + MOVD $1862, R12 + B callbackasm1(SB) + MOVD $1863, R12 + B callbackasm1(SB) + MOVD $1864, R12 + B callbackasm1(SB) + MOVD $1865, R12 + B callbackasm1(SB) + MOVD $1866, R12 + B callbackasm1(SB) + MOVD $1867, R12 + B callbackasm1(SB) + MOVD $1868, R12 + B callbackasm1(SB) + MOVD $1869, R12 + B callbackasm1(SB) + MOVD $1870, R12 + B callbackasm1(SB) + MOVD $1871, R12 + B callbackasm1(SB) + MOVD $1872, R12 + B callbackasm1(SB) + MOVD $1873, R12 + B callbackasm1(SB) + MOVD $1874, R12 + B callbackasm1(SB) + MOVD $1875, R12 + B callbackasm1(SB) + MOVD $1876, R12 + B callbackasm1(SB) + MOVD $1877, R12 + B callbackasm1(SB) + MOVD $1878, R12 + B callbackasm1(SB) + MOVD $1879, R12 + B callbackasm1(SB) + MOVD $1880, R12 + B callbackasm1(SB) + MOVD $1881, R12 + B callbackasm1(SB) + MOVD $1882, R12 + B callbackasm1(SB) + MOVD $1883, R12 + B callbackasm1(SB) + MOVD $1884, R12 + B callbackasm1(SB) + MOVD $1885, R12 + B callbackasm1(SB) + MOVD $1886, R12 + B callbackasm1(SB) + MOVD $1887, R12 + B callbackasm1(SB) + MOVD $1888, R12 + B callbackasm1(SB) + MOVD $1889, R12 + B callbackasm1(SB) + MOVD $1890, R12 + B callbackasm1(SB) + MOVD $1891, R12 + B callbackasm1(SB) + MOVD $1892, R12 + B callbackasm1(SB) + MOVD $1893, R12 + B callbackasm1(SB) + MOVD $1894, R12 + B callbackasm1(SB) + MOVD $1895, R12 + B callbackasm1(SB) + MOVD $1896, R12 + B callbackasm1(SB) + MOVD $1897, R12 + B callbackasm1(SB) + MOVD $1898, R12 + B callbackasm1(SB) + MOVD $1899, R12 + B callbackasm1(SB) + MOVD $1900, R12 + B callbackasm1(SB) + MOVD $1901, R12 + B callbackasm1(SB) + MOVD $1902, R12 + B callbackasm1(SB) + MOVD $1903, R12 + B callbackasm1(SB) + MOVD $1904, R12 + B callbackasm1(SB) + MOVD $1905, R12 + B callbackasm1(SB) + MOVD $1906, R12 + B callbackasm1(SB) + MOVD $1907, R12 + B callbackasm1(SB) + MOVD $1908, R12 + B callbackasm1(SB) + MOVD $1909, R12 + B callbackasm1(SB) + MOVD $1910, R12 + B callbackasm1(SB) + MOVD $1911, R12 + B callbackasm1(SB) + MOVD $1912, R12 + B callbackasm1(SB) + MOVD $1913, R12 + B callbackasm1(SB) + MOVD $1914, R12 + B callbackasm1(SB) + MOVD $1915, R12 + B callbackasm1(SB) + MOVD $1916, R12 + B callbackasm1(SB) + MOVD $1917, R12 + B callbackasm1(SB) + MOVD $1918, R12 + B callbackasm1(SB) + MOVD $1919, R12 + B callbackasm1(SB) + MOVD $1920, R12 + B callbackasm1(SB) + MOVD $1921, R12 + B callbackasm1(SB) + MOVD $1922, R12 + B callbackasm1(SB) + MOVD $1923, R12 + B callbackasm1(SB) + MOVD $1924, R12 + B callbackasm1(SB) + MOVD $1925, R12 + B callbackasm1(SB) + MOVD $1926, R12 + B callbackasm1(SB) + MOVD $1927, R12 + B callbackasm1(SB) + MOVD $1928, R12 + B callbackasm1(SB) + MOVD $1929, R12 + B callbackasm1(SB) + MOVD $1930, R12 + B callbackasm1(SB) + MOVD $1931, R12 + B callbackasm1(SB) + MOVD $1932, R12 + B callbackasm1(SB) + MOVD $1933, R12 + B callbackasm1(SB) + MOVD $1934, R12 + B callbackasm1(SB) + MOVD $1935, R12 + B callbackasm1(SB) + MOVD $1936, R12 + B callbackasm1(SB) + MOVD $1937, R12 + B callbackasm1(SB) + MOVD $1938, R12 + B callbackasm1(SB) + MOVD $1939, R12 + B callbackasm1(SB) + MOVD $1940, R12 + B callbackasm1(SB) + MOVD $1941, R12 + B callbackasm1(SB) + MOVD $1942, R12 + B callbackasm1(SB) + MOVD $1943, R12 + B callbackasm1(SB) + MOVD $1944, R12 + B callbackasm1(SB) + MOVD $1945, R12 + B callbackasm1(SB) + MOVD $1946, R12 + B callbackasm1(SB) + MOVD $1947, R12 + B callbackasm1(SB) + MOVD $1948, R12 + B callbackasm1(SB) + MOVD $1949, R12 + B callbackasm1(SB) + MOVD $1950, R12 + B callbackasm1(SB) + MOVD $1951, R12 + B callbackasm1(SB) + MOVD $1952, R12 + B callbackasm1(SB) + MOVD $1953, R12 + B callbackasm1(SB) + MOVD $1954, R12 + B callbackasm1(SB) + MOVD $1955, R12 + B callbackasm1(SB) + MOVD $1956, R12 + B callbackasm1(SB) + MOVD $1957, R12 + B callbackasm1(SB) + MOVD $1958, R12 + B callbackasm1(SB) + MOVD $1959, R12 + B callbackasm1(SB) + MOVD $1960, R12 + B callbackasm1(SB) + MOVD $1961, R12 + B callbackasm1(SB) + MOVD $1962, R12 + B callbackasm1(SB) + MOVD $1963, R12 + B callbackasm1(SB) + MOVD $1964, R12 + B callbackasm1(SB) + MOVD $1965, R12 + B callbackasm1(SB) + MOVD $1966, R12 + B callbackasm1(SB) + MOVD $1967, R12 + B callbackasm1(SB) + MOVD $1968, R12 + B callbackasm1(SB) + MOVD $1969, R12 + B callbackasm1(SB) + MOVD $1970, R12 + B callbackasm1(SB) + MOVD $1971, R12 + B callbackasm1(SB) + MOVD $1972, R12 + B callbackasm1(SB) + MOVD $1973, R12 + B callbackasm1(SB) + MOVD $1974, R12 + B callbackasm1(SB) + MOVD $1975, R12 + B callbackasm1(SB) + MOVD $1976, R12 + B callbackasm1(SB) + MOVD $1977, R12 + B callbackasm1(SB) + MOVD $1978, R12 + B callbackasm1(SB) + MOVD $1979, R12 + B callbackasm1(SB) + MOVD $1980, R12 + B callbackasm1(SB) + MOVD $1981, R12 + B callbackasm1(SB) + MOVD $1982, R12 + B callbackasm1(SB) + MOVD $1983, R12 + B callbackasm1(SB) + MOVD $1984, R12 + B callbackasm1(SB) + MOVD $1985, R12 + B callbackasm1(SB) + MOVD $1986, R12 + B callbackasm1(SB) + MOVD $1987, R12 + B callbackasm1(SB) + MOVD $1988, R12 + B callbackasm1(SB) + MOVD $1989, R12 + B callbackasm1(SB) + MOVD $1990, R12 + B callbackasm1(SB) + MOVD $1991, R12 + B callbackasm1(SB) + MOVD $1992, R12 + B callbackasm1(SB) + MOVD $1993, R12 + B callbackasm1(SB) + MOVD $1994, R12 + B callbackasm1(SB) + MOVD $1995, R12 + B callbackasm1(SB) + MOVD $1996, R12 + B callbackasm1(SB) + MOVD $1997, R12 + B callbackasm1(SB) + MOVD $1998, R12 + B callbackasm1(SB) + MOVD $1999, R12 + B callbackasm1(SB) diff --git a/vendor/github.com/emicklei/go-restful/v3/CHANGES.md b/vendor/github.com/emicklei/go-restful/v3/CHANGES.md index 74a378157..5edd5a7ca 100644 --- a/vendor/github.com/emicklei/go-restful/v3/CHANGES.md +++ b/vendor/github.com/emicklei/go-restful/v3/CHANGES.md @@ -1,10 +1,30 @@ # Change history of go-restful -## [v3.9.0] - 20221-07-21 +## [v3.11.0] - 2023-08-19 + +- restored behavior as <= v3.9.0 with option to change path strategy using TrimRightSlashEnabled. + +## [v3.10.2] - 2023-03-09 - DO NOT USE + +- introduced MergePathStrategy to be able to revert behaviour of path concatenation to 3.9.0 + see comment in Readme how to customize this behaviour. + +## [v3.10.1] - 2022-11-19 - DO NOT USE + +- fix broken 3.10.0 by using path package for joining paths + +## [v3.10.0] - 2022-10-11 - BROKEN + +- changed tokenizer to match std route match behavior; do not trimright the path (#511) +- Add MIME_ZIP (#512) +- Add MIME_ZIP and HEADER_ContentDisposition (#513) +- Changed how to get query parameter issue #510 + +## [v3.9.0] - 2022-07-21 - add support for http.Handler implementations to work as FilterFunction, issue #504 (thanks to https://github.com/ggicci) -## [v3.8.0] - 20221-06-06 +## [v3.8.0] - 2022-06-06 - use exact matching of allowed domain entries, issue #489 (#493) - this changes fixes [security] Authorization Bypass Through User-Controlled Key diff --git a/vendor/github.com/emicklei/go-restful/v3/README.md b/vendor/github.com/emicklei/go-restful/v3/README.md index 0625359dc..e3e30080e 100644 --- a/vendor/github.com/emicklei/go-restful/v3/README.md +++ b/vendor/github.com/emicklei/go-restful/v3/README.md @@ -79,7 +79,7 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo - Content encoding (gzip,deflate) of request and response payloads - Automatic responses on OPTIONS (using a filter) - Automatic CORS request handling (using a filter) -- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi), see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12)) +- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi)) - Panic recovery to produce HTTP 500, customizable using RecoverHandler(...) - Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...) - Configurable (trace) logging @@ -96,6 +96,7 @@ There are several hooks to customize the behavior of the go-restful package. - Compression - Encoders for other serializers - Use [jsoniter](https://github.com/json-iterator/go) by building this package using a build tag, e.g. `go build -tags=jsoniter .` +- Use the package variable `TrimRightSlashEnabled` (default true) to control the behavior of matching routes that end with a slash `/` ## Resources @@ -108,4 +109,4 @@ There are several hooks to customize the behavior of the go-restful package. Type ```git shortlog -s``` for a full list of contributors. -© 2012 - 2022, http://ernestmicklei.com. MIT License. Contributions are welcome. +© 2012 - 2023, http://ernestmicklei.com. MIT License. Contributions are welcome. diff --git a/vendor/github.com/emicklei/go-restful/v3/constants.go b/vendor/github.com/emicklei/go-restful/v3/constants.go index 203439c5e..2328bde6c 100644 --- a/vendor/github.com/emicklei/go-restful/v3/constants.go +++ b/vendor/github.com/emicklei/go-restful/v3/constants.go @@ -7,12 +7,14 @@ package restful const ( MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces() MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces() + MIME_ZIP = "application/zip" // Accept or Content-Type used in Consumes() and/or Produces() MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default HEADER_Allow = "Allow" HEADER_Accept = "Accept" HEADER_Origin = "Origin" HEADER_ContentType = "Content-Type" + HEADER_ContentDisposition = "Content-Disposition" HEADER_LastModified = "Last-Modified" HEADER_AcceptEncoding = "Accept-Encoding" HEADER_ContentEncoding = "Content-Encoding" diff --git a/vendor/github.com/emicklei/go-restful/v3/request.go b/vendor/github.com/emicklei/go-restful/v3/request.go index 5725a0759..0020095e8 100644 --- a/vendor/github.com/emicklei/go-restful/v3/request.go +++ b/vendor/github.com/emicklei/go-restful/v3/request.go @@ -31,7 +31,8 @@ func NewRequest(httpRequest *http.Request) *Request { // a "Unable to unmarshal content of type:" response is returned. // Valid values are restful.MIME_JSON and restful.MIME_XML // Example: -// restful.DefaultRequestContentType(restful.MIME_JSON) +// +// restful.DefaultRequestContentType(restful.MIME_JSON) func DefaultRequestContentType(mime string) { defaultRequestContentType = mime } @@ -48,7 +49,7 @@ func (r *Request) PathParameters() map[string]string { // QueryParameter returns the (first) Query parameter value by its name func (r *Request) QueryParameter(name string) string { - return r.Request.FormValue(name) + return r.Request.URL.Query().Get(name) } // QueryParameters returns the all the query parameters values by name diff --git a/vendor/github.com/emicklei/go-restful/v3/response.go b/vendor/github.com/emicklei/go-restful/v3/response.go index 8f0b56aa2..a41a92cc2 100644 --- a/vendor/github.com/emicklei/go-restful/v3/response.go +++ b/vendor/github.com/emicklei/go-restful/v3/response.go @@ -109,6 +109,9 @@ func (r *Response) EntityWriter() (EntityReaderWriter, bool) { if DefaultResponseMimeType == MIME_XML { return entityAccessRegistry.accessorAt(MIME_XML) } + if DefaultResponseMimeType == MIME_ZIP { + return entityAccessRegistry.accessorAt(MIME_ZIP) + } // Fallback to whatever the route says it can produce. // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for _, each := range r.routeProduces { diff --git a/vendor/github.com/emicklei/go-restful/v3/route.go b/vendor/github.com/emicklei/go-restful/v3/route.go index 193f4a6b0..306c44be7 100644 --- a/vendor/github.com/emicklei/go-restful/v3/route.go +++ b/vendor/github.com/emicklei/go-restful/v3/route.go @@ -40,7 +40,8 @@ type Route struct { ParameterDocs []*Parameter ResponseErrors map[int]ResponseError DefaultResponse *ResponseError - ReadSample, WriteSample interface{} // structs that model an example request or response payload + ReadSample, WriteSample interface{} // structs that model an example request or response payload + WriteSamples []interface{} // if more than one return types is possible (oneof) then this will contain multiple values // Extra information used to store custom information about the route. Metadata map[string]interface{} @@ -164,7 +165,13 @@ func tokenizePath(path string) []string { if "/" == path { return nil } - return strings.Split(strings.Trim(path, "/"), "/") + if TrimRightSlashEnabled { + // 3.9.0 + return strings.Split(strings.Trim(path, "/"), "/") + } else { + // 3.10.2 + return strings.Split(strings.TrimLeft(path, "/"), "/") + } } // for debugging @@ -176,3 +183,9 @@ func (r *Route) String() string { func (r *Route) EnableContentEncoding(enabled bool) { r.contentEncodingEnabled = &enabled } + +// TrimRightSlashEnabled controls whether +// - path on route building is using path.Join +// - the path of the incoming request is trimmed of its slash suffux. +// Value of true matches the behavior of <= 3.9.0 +var TrimRightSlashEnabled = true diff --git a/vendor/github.com/emicklei/go-restful/v3/route_builder.go b/vendor/github.com/emicklei/go-restful/v3/route_builder.go index 23641b6dd..75168c12e 100644 --- a/vendor/github.com/emicklei/go-restful/v3/route_builder.go +++ b/vendor/github.com/emicklei/go-restful/v3/route_builder.go @@ -7,6 +7,7 @@ package restful import ( "fmt" "os" + "path" "reflect" "runtime" "strings" @@ -30,27 +31,29 @@ type RouteBuilder struct { typeNameHandleFunc TypeNameHandleFunction // required // documentation - doc string - notes string - operation string - readSample, writeSample interface{} - parameters []*Parameter - errorMap map[int]ResponseError - defaultResponse *ResponseError - metadata map[string]interface{} - extensions map[string]interface{} - deprecated bool - contentEncodingEnabled *bool + doc string + notes string + operation string + readSample interface{} + writeSamples []interface{} + parameters []*Parameter + errorMap map[int]ResponseError + defaultResponse *ResponseError + metadata map[string]interface{} + extensions map[string]interface{} + deprecated bool + contentEncodingEnabled *bool } // Do evaluates each argument with the RouteBuilder itself. // This allows you to follow DRY principles without breaking the fluent programming style. // Example: -// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500)) // -// func Returns500(b *RouteBuilder) { -// b.Returns(500, "Internal Server Error", restful.ServiceError{}) -// } +// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500)) +// +// func Returns500(b *RouteBuilder) { +// b.Returns(500, "Internal Server Error", restful.ServiceError{}) +// } func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder { for _, each := range oneArgBlocks { each(b) @@ -133,9 +136,9 @@ func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) { return p } -// Writes tells what resource type will be written as the response payload. Optional. -func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder { - b.writeSample = sample +// Writes tells which one of the resource types will be written as the response payload. Optional. +func (b *RouteBuilder) Writes(samples ...interface{}) *RouteBuilder { + b.writeSamples = samples // oneof return b } @@ -340,19 +343,29 @@ func (b *RouteBuilder) Build() Route { ResponseErrors: b.errorMap, DefaultResponse: b.defaultResponse, ReadSample: b.readSample, - WriteSample: b.writeSample, + WriteSamples: b.writeSamples, Metadata: b.metadata, Deprecated: b.deprecated, contentEncodingEnabled: b.contentEncodingEnabled, allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType, } + // set WriteSample if one specified + if len(b.writeSamples) == 1 { + route.WriteSample = b.writeSamples[0] + } route.Extensions = b.extensions route.postBuild() return route } -func concatPath(path1, path2 string) string { - return strings.TrimRight(path1, "/") + "/" + strings.TrimLeft(path2, "/") +// merge two paths using the current (package global) merge path strategy. +func concatPath(rootPath, routePath string) string { + + if TrimRightSlashEnabled { + return strings.TrimRight(rootPath, "/") + "/" + strings.TrimLeft(routePath, "/") + } else { + return path.Join(rootPath, routePath) + } } var anonymousFuncCount int32 diff --git a/vendor/github.com/evanphx/json-patch/patch.go b/vendor/github.com/evanphx/json-patch/patch.go index dc2b7e51e..4bce5936d 100644 --- a/vendor/github.com/evanphx/json-patch/patch.go +++ b/vendor/github.com/evanphx/json-patch/patch.go @@ -568,29 +568,6 @@ func (p Patch) replace(doc *container, op Operation) error { return errors.Wrapf(err, "replace operation failed to decode path") } - if path == "" { - val := op.value() - - if val.which == eRaw { - if !val.tryDoc() { - if !val.tryAry() { - return errors.Wrapf(err, "replace operation value must be object or array") - } - } - } - - switch val.which { - case eAry: - *doc = &val.ary - case eDoc: - *doc = &val.doc - case eRaw: - return errors.Wrapf(err, "replace operation hit impossible case") - } - - return nil - } - con, key := findObject(doc, path) if con == nil { @@ -657,25 +634,6 @@ func (p Patch) test(doc *container, op Operation) error { return errors.Wrapf(err, "test operation failed to decode path") } - if path == "" { - var self lazyNode - - switch sv := (*doc).(type) { - case *partialDoc: - self.doc = *sv - self.which = eDoc - case *partialArray: - self.ary = *sv - self.which = eAry - } - - if self.equal(op.value()) { - return nil - } - - return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) - } - con, key := findObject(doc, path) if con == nil { diff --git a/vendor/github.com/go-logr/logr/.golangci.yaml b/vendor/github.com/go-logr/logr/.golangci.yaml index 94ff801df..0cffafa7b 100644 --- a/vendor/github.com/go-logr/logr/.golangci.yaml +++ b/vendor/github.com/go-logr/logr/.golangci.yaml @@ -6,7 +6,6 @@ linters: disable-all: true enable: - asciicheck - - deadcode - errcheck - forcetypeassert - gocritic @@ -18,10 +17,8 @@ linters: - misspell - revive - staticcheck - - structcheck - typecheck - unused - - varcheck issues: exclude-use-default: false diff --git a/vendor/github.com/go-logr/logr/README.md b/vendor/github.com/go-logr/logr/README.md index ab5931181..8969526a6 100644 --- a/vendor/github.com/go-logr/logr/README.md +++ b/vendor/github.com/go-logr/logr/README.md @@ -1,6 +1,7 @@ # A minimal logging API for Go [![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-logr/logr/badge)](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 without becoming coupled to a particular logging implementation. This is not @@ -73,6 +74,30 @@ received: If the Go standard library had defined an interface for logging, this project probably would not be needed. Alas, here we are. +When the Go developers started developing such an interface with +[slog](https://github.com/golang/go/issues/56345), they adopted some of the +logr design but also left out some parts and changed others: + +| Feature | logr | slog | +|---------|------|------| +| High-level API | `Logger` (passed by value) | `Logger` (passed by [pointer](https://github.com/golang/go/issues/59126)) | +| Low-level API | `LogSink` | `Handler` | +| Stack unwinding | done by `LogSink` | done by `Logger` | +| Skipping helper functions | `WithCallDepth`, `WithCallStackHelper` | [not supported by Logger](https://github.com/golang/go/issues/59145) | +| Generating a value for logging on demand | `Marshaler` | `LogValuer` | +| Log levels | >= 0, higher meaning "less important" | positive and negative, with 0 for "info" and higher meaning "more important" | +| Error log entries | always logged, don't have a verbosity level | normal log entries with level >= `LevelError` | +| Passing logger via context | `NewContext`, `FromContext` | no API | +| Adding a name to a logger | `WithName` | no API | +| Modify verbosity of log entries in a call chain | `V` | no API | +| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` | +| Pass context for extracting additional values | no API | API variants like `InfoCtx` | + +The high-level slog API is explicitly meant to be one of many different APIs +that can be layered on top of a shared `slog.Handler`. logr is one such +alternative API, with [interoperability](#slog-interoperability) provided by +some conversion functions. + ### Inspiration Before you consider this package, please read [this blog post by the @@ -118,6 +143,103 @@ There are implementations for the following logging libraries: - **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0) - **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing) +## slog interoperability + +Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler` +and using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and +`ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`. +As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level +slog API. + +### Using a `logr.LogSink` as backend for slog + +Ideally, a logr sink implementation should support both logr and slog by +implementing both the normal logr interface(s) and `SlogSink`. Because +of a conflict in the parameters of the common `Enabled` method, it is [not +possible to implement both slog.Handler and logr.Sink in the same +type](https://github.com/golang/go/issues/59110). + +If both are supported, log calls can go from the high-level APIs to the backend +without the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can +convert back and forth without adding additional wrappers, with one exception: +when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then +`ToSlogHandler` has to use a wrapper which adjusts the verbosity for future +log calls. + +Such an implementation should also support values that implement specific +interfaces from both packages for logging (`logr.Marshaler`, `slog.LogValuer`, +`slog.GroupValue`). logr does not convert those. + +Not supporting slog has several drawbacks: +- Recording source code locations works correctly if the handler gets called + through `slog.Logger`, but may be wrong in other cases. That's because a + `logr.Sink` does its own stack unwinding instead of using the program counter + provided by the high-level API. +- slog levels <= 0 can be mapped to logr levels by negating the level without a + loss of information. But all slog levels > 0 (e.g. `slog.LevelWarning` as + used by `slog.Logger.Warn`) must be mapped to 0 before calling the sink + because logr does not support "more important than info" levels. +- The slog group concept is supported by prefixing each key in a key/value + pair with the group names, separated by a dot. For structured output like + JSON it would be better to group the key/value pairs inside an object. +- Special slog values and interfaces don't work as expected. +- The overhead is likely to be higher. + +These drawbacks are severe enough that applications using a mixture of slog and +logr should switch to a different backend. + +### Using a `slog.Handler` as backend for logr + +Using a plain `slog.Handler` without support for logr works better than the +other direction: +- All logr verbosity levels can be mapped 1:1 to their corresponding slog level + by negating them. +- Stack unwinding is done by the `SlogSink` and the resulting program + counter is passed to the `slog.Handler`. +- Names added via `Logger.WithName` are gathered and recorded in an additional + attribute with `logger` as key and the names separated by slash as value. +- `Logger.Error` is turned into a log record with `slog.LevelError` as level + and an additional attribute with `err` as key, if an error was provided. + +The main drawback is that `logr.Marshaler` will not be supported. Types should +ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility +with logr implementations without slog support is not important, then +`slog.Valuer` is sufficient. + +### Context support for slog + +Storing a logger in a `context.Context` is not supported by +slog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be +used to fill this gap. They store and retrieve a `slog.Logger` pointer +under the same context key that is also used by `NewContext` and +`FromContext` for `logr.Logger` value. + +When `NewContextWithSlogLogger` is followed by `FromContext`, the latter will +automatically convert the `slog.Logger` to a +`logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction. + +With this approach, binaries which use either slog or logr are as efficient as +possible with no unnecessary allocations. This is also why the API stores a +`slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger` +on retrieval would need to allocate one. + +The downside is that switching back and forth needs more allocations. Because +logr is the API that is already in use by different packages, in particular +Kubernetes, the recommendation is to use the `logr.Logger` API in code which +uses contextual logging. + +An alternative to adding values to a logger and storing that logger in the +context is to store the values in the context and to configure a logging +backend to extract those values when emitting log entries. This only works when +log calls are passed the context, which is not supported by the logr API. + +With the slog API, it is possible, but not +required. https://github.com/veqryn/slog-context is a package for slog which +provides additional support code for this approach. It also contains wrappers +for the context functions in logr, so developers who prefer to not use the logr +APIs directly can use those instead and the resulting code will still be +interoperable with logr. + ## FAQ ### Conceptual @@ -241,7 +363,9 @@ Otherwise, you can start out with `0` as "you always want to see this", Then gradually choose levels in between as you need them, working your way down from 10 (for debug and trace style logs) and up from 1 (for chattier -info-type logs.) +info-type logs). For reference, slog pre-defines -4 for debug logs +(corresponds to 4 in logr), which matches what is +[recommended for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use). #### How do I choose my keys? diff --git a/vendor/github.com/go-logr/logr/SECURITY.md b/vendor/github.com/go-logr/logr/SECURITY.md new file mode 100644 index 000000000..1ca756fc7 --- /dev/null +++ b/vendor/github.com/go-logr/logr/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives us time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to go-logr-security@googlegroups.com +- send us a [private vulnerability report](https://github.com/go-logr/logr/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +We ask that you give us 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/go-logr/logr/context.go b/vendor/github.com/go-logr/logr/context.go new file mode 100644 index 000000000..de8bcc3ad --- /dev/null +++ b/vendor/github.com/go-logr/logr/context.go @@ -0,0 +1,33 @@ +/* +Copyright 2023 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +// contextKey is how we find Loggers in a context.Context. With Go < 1.21, +// the value is always a Logger value. With Go >= 1.21, the value can be a +// Logger value or a slog.Logger pointer. +type contextKey struct{} + +// notFoundError exists to carry an IsNotFound method. +type notFoundError struct{} + +func (notFoundError) Error() string { + return "no logr.Logger was present" +} + +func (notFoundError) IsNotFound() bool { + return true +} diff --git a/vendor/github.com/go-logr/logr/context_noslog.go b/vendor/github.com/go-logr/logr/context_noslog.go new file mode 100644 index 000000000..f012f9a18 --- /dev/null +++ b/vendor/github.com/go-logr/logr/context_noslog.go @@ -0,0 +1,49 @@ +//go:build !go1.21 +// +build !go1.21 + +/* +Copyright 2019 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" +) + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v, nil + } + + return Logger{}, notFoundError{} +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v + } + + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} diff --git a/vendor/github.com/go-logr/logr/context_slog.go b/vendor/github.com/go-logr/logr/context_slog.go new file mode 100644 index 000000000..065ef0b82 --- /dev/null +++ b/vendor/github.com/go-logr/logr/context_slog.go @@ -0,0 +1,83 @@ +//go:build go1.21 +// +build go1.21 + +/* +Copyright 2019 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" + "fmt" + "log/slog" +) + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + v := ctx.Value(contextKey{}) + if v == nil { + return Logger{}, notFoundError{} + } + + switch v := v.(type) { + case Logger: + return v, nil + case *slog.Logger: + return FromSlogHandler(v.Handler()), nil + default: + // Not reached. + panic(fmt.Sprintf("unexpected value type for logr context key: %T", v)) + } +} + +// FromContextAsSlogLogger returns a slog.Logger from ctx or nil if no such Logger is found. +func FromContextAsSlogLogger(ctx context.Context) *slog.Logger { + v := ctx.Value(contextKey{}) + if v == nil { + return nil + } + + switch v := v.(type) { + case Logger: + return slog.New(ToSlogHandler(v)) + case *slog.Logger: + return v + default: + // Not reached. + panic(fmt.Sprintf("unexpected value type for logr context key: %T", v)) + } +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if logger, err := FromContext(ctx); err == nil { + return logger + } + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} + +// NewContextWithSlogLogger returns a new Context, derived from ctx, which carries the +// provided slog.Logger. +func NewContextWithSlogLogger(ctx context.Context, logger *slog.Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} diff --git a/vendor/github.com/go-logr/logr/discard.go b/vendor/github.com/go-logr/logr/discard.go index 9d92a38f1..99fe8be93 100644 --- a/vendor/github.com/go-logr/logr/discard.go +++ b/vendor/github.com/go-logr/logr/discard.go @@ -20,35 +20,5 @@ package logr // used whenever the caller is not interested in the logs. Logger instances // produced by this function always compare as equal. func Discard() Logger { - return Logger{ - level: 0, - sink: discardLogSink{}, - } -} - -// discardLogSink is a LogSink that discards all messages. -type discardLogSink struct{} - -// Verify that it actually implements the interface -var _ LogSink = discardLogSink{} - -func (l discardLogSink) Init(RuntimeInfo) { -} - -func (l discardLogSink) Enabled(int) bool { - return false -} - -func (l discardLogSink) Info(int, string, ...interface{}) { -} - -func (l discardLogSink) Error(error, string, ...interface{}) { -} - -func (l discardLogSink) WithValues(...interface{}) LogSink { - return l -} - -func (l discardLogSink) WithName(string) LogSink { - return l + return New(nil) } diff --git a/vendor/github.com/go-logr/logr/logr.go b/vendor/github.com/go-logr/logr/logr.go index c3b56b3d2..b4428e105 100644 --- a/vendor/github.com/go-logr/logr/logr.go +++ b/vendor/github.com/go-logr/logr/logr.go @@ -21,7 +21,7 @@ limitations under the License. // to back that API. Packages in the Go ecosystem can depend on this package, // while callers can implement logging with whatever backend is appropriate. // -// Usage +// # Usage // // Logging is done using a Logger instance. Logger is a concrete type with // methods, which defers the actual logging to a LogSink interface. The main @@ -30,16 +30,20 @@ limitations under the License. // "structured logging". // // With Go's standard log package, we might write: -// log.Printf("setting target value %s", targetValue) +// +// log.Printf("setting target value %s", targetValue) // // With logr's structured logging, we'd write: -// logger.Info("setting target", "value", targetValue) +// +// logger.Info("setting target", "value", targetValue) // // Errors are much the same. Instead of: -// log.Printf("failed to open the pod bay door for user %s: %v", user, err) +// +// log.Printf("failed to open the pod bay door for user %s: %v", user, err) // // We'd write: -// logger.Error(err, "failed to open the pod bay door", "user", user) +// +// logger.Error(err, "failed to open the pod bay door", "user", user) // // Info() and Error() are very similar, but they are separate methods so that // LogSink implementations can choose to do things like attach additional @@ -47,7 +51,7 @@ limitations under the License. // always logged, regardless of the current verbosity. If there is no error // instance available, passing nil is valid. // -// Verbosity +// # Verbosity // // Often we want to log information only when the application in "verbose // mode". To write log lines that are more verbose, Logger has a V() method. @@ -58,20 +62,22 @@ limitations under the License. // Error messages do not have a verbosity level and are always logged. // // Where we might have written: -// if flVerbose >= 2 { -// log.Printf("an unusual thing happened") -// } +// +// if flVerbose >= 2 { +// log.Printf("an unusual thing happened") +// } // // We can write: -// logger.V(2).Info("an unusual thing happened") // -// Logger Names +// logger.V(2).Info("an unusual thing happened") +// +// # Logger Names // // Logger instances can have name strings so that all messages logged through // that instance have additional context. For example, you might want to add // a subsystem name: // -// logger.WithName("compactor").Info("started", "time", time.Now()) +// logger.WithName("compactor").Info("started", "time", time.Now()) // // The WithName() method returns a new Logger, which can be passed to // constructors or other functions for further use. Repeated use of WithName() @@ -82,25 +88,27 @@ limitations under the License. // joining operation (e.g. whitespace, commas, periods, slashes, brackets, // quotes, etc). // -// Saved Values +// # Saved Values // // Logger instances can store any number of key/value pairs, which will be // logged alongside all messages logged through that instance. For example, // you might want to create a Logger instance per managed object: // // With the standard log package, we might write: -// log.Printf("decided to set field foo to value %q for object %s/%s", -// targetValue, object.Namespace, object.Name) +// +// log.Printf("decided to set field foo to value %q for object %s/%s", +// targetValue, object.Namespace, object.Name) // // With logr we'd write: -// // Elsewhere: set up the logger to log the object name. -// obj.logger = mainLogger.WithValues( -// "name", obj.name, "namespace", obj.namespace) // -// // later on... -// obj.logger.Info("setting foo", "value", targetValue) +// // Elsewhere: set up the logger to log the object name. +// obj.logger = mainLogger.WithValues( +// "name", obj.name, "namespace", obj.namespace) // -// Best Practices +// // later on... +// obj.logger.Info("setting foo", "value", targetValue) +// +// # Best Practices // // Logger has very few hard rules, with the goal that LogSink implementations // might have a lot of freedom to differentiate. There are, however, some @@ -119,20 +127,20 @@ limitations under the License. // such a value can call its methods without having to check whether the // instance is ready for use. // -// Calling methods with the null logger (Logger{}) as instance will crash -// because it has no LogSink. Therefore this null logger should never be passed -// around. For cases where passing a logger is optional, a pointer to Logger +// The zero logger (= Logger{}) is identical to Discard() and discards all log +// entries. Code that receives a Logger by value can simply call it, the methods +// will never crash. For cases where passing a logger is optional, a pointer to Logger // should be used. // -// Key Naming Conventions +// # Key Naming Conventions // // Keys are not strictly required to conform to any specification or regex, but // it is recommended that they: -// * be human-readable and meaningful (not auto-generated or simple ordinals) -// * be constant (not dependent on input data) -// * contain only printable characters -// * not contain whitespace or punctuation -// * use lower case for simple keys and lowerCamelCase for more complex ones +// - be human-readable and meaningful (not auto-generated or simple ordinals) +// - be constant (not dependent on input data) +// - contain only printable characters +// - not contain whitespace or punctuation +// - use lower case for simple keys and lowerCamelCase for more complex ones // // These guidelines help ensure that log data is processed properly regardless // of the log implementation. For example, log implementations will try to @@ -141,51 +149,54 @@ limitations under the License. // While users are generally free to use key names of their choice, it's // generally best to avoid using the following keys, as they're frequently used // by implementations: -// * "caller": the calling information (file/line) of a particular log line -// * "error": the underlying error value in the `Error` method -// * "level": the log level -// * "logger": the name of the associated logger -// * "msg": the log message -// * "stacktrace": the stack trace associated with a particular log line or -// error (often from the `Error` message) -// * "ts": the timestamp for a log line +// - "caller": the calling information (file/line) of a particular log line +// - "error": the underlying error value in the `Error` method +// - "level": the log level +// - "logger": the name of the associated logger +// - "msg": the log message +// - "stacktrace": the stack trace associated with a particular log line or +// error (often from the `Error` message) +// - "ts": the timestamp for a log line // // Implementations are encouraged to make use of these keys to represent the // above concepts, when necessary (for example, in a pure-JSON output form, it // would be necessary to represent at least message and timestamp as ordinary // named values). // -// Break Glass +// # Break Glass // // Implementations may choose to give callers access to the underlying // logging implementation. The recommended pattern for this is: -// // Underlier exposes access to the underlying logging implementation. -// // Since callers only have a logr.Logger, they have to know which -// // implementation is in use, so this interface is less of an abstraction -// // and more of way to test type conversion. -// type Underlier interface { -// GetUnderlying() -// } +// +// // Underlier exposes access to the underlying logging implementation. +// // Since callers only have a logr.Logger, they have to know which +// // implementation is in use, so this interface is less of an abstraction +// // and more of way to test type conversion. +// type Underlier interface { +// GetUnderlying() +// } // // Logger grants access to the sink to enable type assertions like this: -// func DoSomethingWithImpl(log logr.Logger) { -// if underlier, ok := log.GetSink()(impl.Underlier) { -// implLogger := underlier.GetUnderlying() -// ... -// } -// } +// +// func DoSomethingWithImpl(log logr.Logger) { +// if underlier, ok := log.GetSink().(impl.Underlier); ok { +// implLogger := underlier.GetUnderlying() +// ... +// } +// } // // Custom `With*` functions can be implemented by copying the complete // Logger struct and replacing the sink in the copy: -// // WithFooBar changes the foobar parameter in the log sink and returns a -// // new logger with that modified sink. It does nothing for loggers where -// // the sink doesn't support that parameter. -// func WithFoobar(log logr.Logger, foobar int) logr.Logger { -// if foobarLogSink, ok := log.GetSink()(FoobarSink); ok { -// log = log.WithSink(foobarLogSink.WithFooBar(foobar)) -// } -// return log -// } +// +// // WithFooBar changes the foobar parameter in the log sink and returns a +// // new logger with that modified sink. It does nothing for loggers where +// // the sink doesn't support that parameter. +// func WithFoobar(log logr.Logger, foobar int) logr.Logger { +// if foobarLogSink, ok := log.GetSink().(FoobarSink); ok { +// log = log.WithSink(foobarLogSink.WithFooBar(foobar)) +// } +// return log +// } // // Don't use New to construct a new Logger with a LogSink retrieved from an // existing Logger. Source code attribution might not work correctly and @@ -196,16 +207,15 @@ limitations under the License. // those. package logr -import ( - "context" -) - // New returns a new Logger instance. This is primarily used by libraries -// implementing LogSink, rather than end users. +// implementing LogSink, rather than end users. Passing a nil sink will create +// a Logger which discards all log lines. func New(sink LogSink) Logger { logger := Logger{} logger.setSink(sink) - sink.Init(runtimeInfo) + if sink != nil { + sink.Init(runtimeInfo) + } return logger } @@ -244,7 +254,13 @@ type Logger struct { // Enabled tests whether this Logger is enabled. For example, commandline // flags might be used to set the logging verbosity and disable some info logs. func (l Logger) Enabled() bool { - return l.sink.Enabled(l.level) + // Some implementations of LogSink look at the caller in Enabled (e.g. + // different verbosity levels per package or file), but we only pass one + // CallDepth in (via Init). This means that all calls from Logger to the + // LogSink's Enabled, Info, and Error methods must have the same number of + // frames. In other words, Logger methods can't call other Logger methods + // which call these LogSink methods unless we do it the same in all paths. + return l.sink != nil && l.sink.Enabled(l.level) } // Info logs a non-error message with the given key/value pairs as context. @@ -253,8 +269,11 @@ func (l Logger) Enabled() bool { // line. The key/value pairs can then be used to add additional variable // information. The key/value pairs must alternate string keys and arbitrary // values. -func (l Logger) Info(msg string, keysAndValues ...interface{}) { - if l.Enabled() { +func (l Logger) Info(msg string, keysAndValues ...any) { + if l.sink == nil { + return + } + if l.sink.Enabled(l.level) { // see comment in Enabled if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { withHelper.GetCallStackHelper()() } @@ -272,7 +291,10 @@ func (l Logger) Info(msg string, keysAndValues ...interface{}) { // while the err argument should be used to attach the actual error that // triggered this log line, if present. The err parameter is optional // and nil may be passed instead of an error instance. -func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { +func (l Logger) Error(err error, msg string, keysAndValues ...any) { + if l.sink == nil { + return + } if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { withHelper.GetCallStackHelper()() } @@ -284,6 +306,9 @@ func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { // level means a log message is less important. Negative V-levels are treated // as 0. func (l Logger) V(level int) Logger { + if l.sink == nil { + return l + } if level < 0 { level = 0 } @@ -291,9 +316,19 @@ func (l Logger) V(level int) Logger { return l } +// GetV returns the verbosity level of the logger. If the logger's LogSink is +// nil as in the Discard logger, this will always return 0. +func (l Logger) GetV() int { + // 0 if l.sink nil because of the if check in V above. + return l.level +} + // WithValues returns a new Logger instance with additional key/value pairs. // See Info for documentation on how key/value pairs work. -func (l Logger) WithValues(keysAndValues ...interface{}) Logger { +func (l Logger) WithValues(keysAndValues ...any) Logger { + if l.sink == nil { + return l + } l.setSink(l.sink.WithValues(keysAndValues...)) return l } @@ -304,6 +339,9 @@ func (l Logger) WithValues(keysAndValues ...interface{}) Logger { // contain only letters, digits, and hyphens (see the package documentation for // more information). func (l Logger) WithName(name string) Logger { + if l.sink == nil { + return l + } l.setSink(l.sink.WithName(name)) return l } @@ -324,6 +362,9 @@ func (l Logger) WithName(name string) Logger { // WithCallDepth(1) because it works with implementions that support the // CallDepthLogSink and/or CallStackHelperLogSink interfaces. func (l Logger) WithCallDepth(depth int) Logger { + if l.sink == nil { + return l + } if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { l.setSink(withCallDepth.WithCallDepth(depth)) } @@ -345,6 +386,9 @@ func (l Logger) WithCallDepth(depth int) Logger { // implementation does not support either of these, the original Logger will be // returned. func (l Logger) WithCallStackHelper() (func(), Logger) { + if l.sink == nil { + return func() {}, l + } var helper func() if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { l.setSink(withCallDepth.WithCallDepth(1)) @@ -357,43 +401,9 @@ func (l Logger) WithCallStackHelper() (func(), Logger) { return helper, l } -// contextKey is how we find Loggers in a context.Context. -type contextKey struct{} - -// FromContext returns a Logger from ctx or an error if no Logger is found. -func FromContext(ctx context.Context) (Logger, error) { - if v, ok := ctx.Value(contextKey{}).(Logger); ok { - return v, nil - } - - return Logger{}, notFoundError{} -} - -// notFoundError exists to carry an IsNotFound method. -type notFoundError struct{} - -func (notFoundError) Error() string { - return "no logr.Logger was present" -} - -func (notFoundError) IsNotFound() bool { - return true -} - -// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this -// returns a Logger that discards all log messages. -func FromContextOrDiscard(ctx context.Context) Logger { - if v, ok := ctx.Value(contextKey{}).(Logger); ok { - return v - } - - return Discard() -} - -// NewContext returns a new Context, derived from ctx, which carries the -// provided Logger. -func NewContext(ctx context.Context, logger Logger) context.Context { - return context.WithValue(ctx, contextKey{}, logger) +// IsZero returns true if this logger is an uninitialized zero value +func (l Logger) IsZero() bool { + return l.sink == nil } // RuntimeInfo holds information that the logr "core" library knows which @@ -427,22 +437,22 @@ type LogSink interface { // The level argument is provided for optional logging. This method will // only be called when Enabled(level) is true. See Logger.Info for more // details. - Info(level int, msg string, keysAndValues ...interface{}) + Info(level int, msg string, keysAndValues ...any) // Error logs an error, with the given message and key/value pairs as // context. See Logger.Error for more details. - Error(err error, msg string, keysAndValues ...interface{}) + Error(err error, msg string, keysAndValues ...any) // WithValues returns a new LogSink with additional key/value pairs. See // Logger.WithValues for more details. - WithValues(keysAndValues ...interface{}) LogSink + WithValues(keysAndValues ...any) LogSink // WithName returns a new LogSink with the specified name appended. See // Logger.WithName for more details. WithName(name string) LogSink } -// CallDepthLogSink represents a Logger that knows how to climb the call stack +// CallDepthLogSink represents a LogSink that knows how to climb the call stack // to identify the original call site and can offset the depth by a specified // number of frames. This is useful for users who have helper functions // between the "real" call site and the actual calls to Logger methods. @@ -467,7 +477,7 @@ type CallDepthLogSink interface { WithCallDepth(depth int) LogSink } -// CallStackHelperLogSink represents a Logger that knows how to climb +// CallStackHelperLogSink represents a LogSink that knows how to climb // the call stack to identify the original call site and can skip // intermediate helper functions if they mark themselves as // helper. Go's testing package uses that approach. @@ -506,5 +516,5 @@ type Marshaler interface { // with exported fields // // It may return any value of any type. - MarshalLog() interface{} + MarshalLog() any } diff --git a/vendor/github.com/go-logr/logr/sloghandler.go b/vendor/github.com/go-logr/logr/sloghandler.go new file mode 100644 index 000000000..82d1ba494 --- /dev/null +++ b/vendor/github.com/go-logr/logr/sloghandler.go @@ -0,0 +1,192 @@ +//go:build go1.21 +// +build go1.21 + +/* +Copyright 2023 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" + "log/slog" +) + +type slogHandler struct { + // May be nil, in which case all logs get discarded. + sink LogSink + // Non-nil if sink is non-nil and implements SlogSink. + slogSink SlogSink + + // groupPrefix collects values from WithGroup calls. It gets added as + // prefix to value keys when handling a log record. + groupPrefix string + + // levelBias can be set when constructing the handler to influence the + // slog.Level of log records. A positive levelBias reduces the + // slog.Level value. slog has no API to influence this value after the + // handler got created, so it can only be set indirectly through + // Logger.V. + levelBias slog.Level +} + +var _ slog.Handler = &slogHandler{} + +// groupSeparator is used to concatenate WithGroup names and attribute keys. +const groupSeparator = "." + +// GetLevel is used for black box unit testing. +func (l *slogHandler) GetLevel() slog.Level { + return l.levelBias +} + +func (l *slogHandler) Enabled(_ context.Context, level slog.Level) bool { + return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level))) +} + +func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error { + if l.slogSink != nil { + // Only adjust verbosity level of log entries < slog.LevelError. + if record.Level < slog.LevelError { + record.Level -= l.levelBias + } + return l.slogSink.Handle(ctx, record) + } + + // No need to check for nil sink here because Handle will only be called + // when Enabled returned true. + + kvList := make([]any, 0, 2*record.NumAttrs()) + record.Attrs(func(attr slog.Attr) bool { + kvList = attrToKVs(attr, l.groupPrefix, kvList) + return true + }) + if record.Level >= slog.LevelError { + l.sinkWithCallDepth().Error(nil, record.Message, kvList...) + } else { + level := l.levelFromSlog(record.Level) + l.sinkWithCallDepth().Info(level, record.Message, kvList...) + } + return nil +} + +// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info +// are called by Handle, code in slog gets skipped. +// +// This offset currently (Go 1.21.0) works for calls through +// slog.New(ToSlogHandler(...)). There's no guarantee that the call +// chain won't change. Wrapping the handler will also break unwinding. It's +// still better than not adjusting at all.... +// +// This cannot be done when constructing the handler because FromSlogHandler needs +// access to the original sink without this adjustment. A second copy would +// work, but then WithAttrs would have to be called for both of them. +func (l *slogHandler) sinkWithCallDepth() LogSink { + if sink, ok := l.sink.(CallDepthLogSink); ok { + return sink.WithCallDepth(2) + } + return l.sink +} + +func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + if l.sink == nil || len(attrs) == 0 { + return l + } + + clone := *l + if l.slogSink != nil { + clone.slogSink = l.slogSink.WithAttrs(attrs) + clone.sink = clone.slogSink + } else { + kvList := make([]any, 0, 2*len(attrs)) + for _, attr := range attrs { + kvList = attrToKVs(attr, l.groupPrefix, kvList) + } + clone.sink = l.sink.WithValues(kvList...) + } + return &clone +} + +func (l *slogHandler) WithGroup(name string) slog.Handler { + if l.sink == nil { + return l + } + if name == "" { + // slog says to inline empty groups + return l + } + clone := *l + if l.slogSink != nil { + clone.slogSink = l.slogSink.WithGroup(name) + clone.sink = clone.slogSink + } else { + clone.groupPrefix = addPrefix(clone.groupPrefix, name) + } + return &clone +} + +// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups +// and other details of slog. +func attrToKVs(attr slog.Attr, groupPrefix string, kvList []any) []any { + attrVal := attr.Value.Resolve() + if attrVal.Kind() == slog.KindGroup { + groupVal := attrVal.Group() + grpKVs := make([]any, 0, 2*len(groupVal)) + prefix := groupPrefix + if attr.Key != "" { + prefix = addPrefix(groupPrefix, attr.Key) + } + for _, attr := range groupVal { + grpKVs = attrToKVs(attr, prefix, grpKVs) + } + kvList = append(kvList, grpKVs...) + } else if attr.Key != "" { + kvList = append(kvList, addPrefix(groupPrefix, attr.Key), attrVal.Any()) + } + + return kvList +} + +func addPrefix(prefix, name string) string { + if prefix == "" { + return name + } + if name == "" { + return prefix + } + return prefix + groupSeparator + name +} + +// levelFromSlog adjusts the level by the logger's verbosity and negates it. +// It ensures that the result is >= 0. This is necessary because the result is +// passed to a LogSink and that API did not historically document whether +// levels could be negative or what that meant. +// +// Some example usage: +// +// logrV0 := getMyLogger() +// logrV2 := logrV0.V(2) +// slogV2 := slog.New(logr.ToSlogHandler(logrV2)) +// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6) +// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2) +// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0) +func (l *slogHandler) levelFromSlog(level slog.Level) int { + result := -level + result += l.levelBias // in case the original Logger had a V level + if result < 0 { + result = 0 // because LogSink doesn't expect negative V levels + } + return int(result) +} diff --git a/vendor/github.com/go-logr/logr/slogr.go b/vendor/github.com/go-logr/logr/slogr.go new file mode 100644 index 000000000..28a83d024 --- /dev/null +++ b/vendor/github.com/go-logr/logr/slogr.go @@ -0,0 +1,100 @@ +//go:build go1.21 +// +build go1.21 + +/* +Copyright 2023 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" + "log/slog" +) + +// FromSlogHandler returns a Logger which writes to the slog.Handler. +// +// The logr verbosity level is mapped to slog levels such that V(0) becomes +// slog.LevelInfo and V(4) becomes slog.LevelDebug. +func FromSlogHandler(handler slog.Handler) Logger { + if handler, ok := handler.(*slogHandler); ok { + if handler.sink == nil { + return Discard() + } + return New(handler.sink).V(int(handler.levelBias)) + } + return New(&slogSink{handler: handler}) +} + +// ToSlogHandler returns a slog.Handler which writes to the same sink as the Logger. +// +// The returned logger writes all records with level >= slog.LevelError as +// error log entries with LogSink.Error, regardless of the verbosity level of +// the Logger: +// +// logger := +// slog.New(ToSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...) +// +// The level of all other records gets reduced by the verbosity +// level of the Logger and the result is negated. If it happens +// to be negative, then it gets replaced by zero because a LogSink +// is not expected to handled negative levels: +// +// slog.New(ToSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...) +// slog.New(ToSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...) +// slog.New(ToSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...) +// slog.New(ToSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...) +func ToSlogHandler(logger Logger) slog.Handler { + if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 { + return sink.handler + } + + handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())} + if slogSink, ok := handler.sink.(SlogSink); ok { + handler.slogSink = slogSink + } + return handler +} + +// SlogSink is an optional interface that a LogSink can implement to support +// logging through the slog.Logger or slog.Handler APIs better. It then should +// also support special slog values like slog.Group. When used as a +// slog.Handler, the advantages are: +// +// - stack unwinding gets avoided in favor of logging the pre-recorded PC, +// as intended by slog +// - proper grouping of key/value pairs via WithGroup +// - verbosity levels > slog.LevelInfo can be recorded +// - less overhead +// +// Both APIs (Logger and slog.Logger/Handler) then are supported equally +// well. Developers can pick whatever API suits them better and/or mix +// packages which use either API in the same binary with a common logging +// implementation. +// +// This interface is necessary because the type implementing the LogSink +// interface cannot also implement the slog.Handler interface due to the +// different prototype of the common Enabled method. +// +// An implementation could support both interfaces in two different types, but then +// additional interfaces would be needed to convert between those types in FromSlogHandler +// and ToSlogHandler. +type SlogSink interface { + LogSink + + Handle(ctx context.Context, record slog.Record) error + WithAttrs(attrs []slog.Attr) SlogSink + WithGroup(name string) SlogSink +} diff --git a/vendor/github.com/go-logr/logr/slogsink.go b/vendor/github.com/go-logr/logr/slogsink.go new file mode 100644 index 000000000..4060fcbc2 --- /dev/null +++ b/vendor/github.com/go-logr/logr/slogsink.go @@ -0,0 +1,120 @@ +//go:build go1.21 +// +build go1.21 + +/* +Copyright 2023 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" + "log/slog" + "runtime" + "time" +) + +var ( + _ LogSink = &slogSink{} + _ CallDepthLogSink = &slogSink{} + _ Underlier = &slogSink{} +) + +// Underlier is implemented by the LogSink returned by NewFromLogHandler. +type Underlier interface { + // GetUnderlying returns the Handler used by the LogSink. + GetUnderlying() slog.Handler +} + +const ( + // nameKey is used to log the `WithName` values as an additional attribute. + nameKey = "logger" + + // errKey is used to log the error parameter of Error as an additional attribute. + errKey = "err" +) + +type slogSink struct { + callDepth int + name string + handler slog.Handler +} + +func (l *slogSink) Init(info RuntimeInfo) { + l.callDepth = info.CallDepth +} + +func (l *slogSink) GetUnderlying() slog.Handler { + return l.handler +} + +func (l *slogSink) WithCallDepth(depth int) LogSink { + newLogger := *l + newLogger.callDepth += depth + return &newLogger +} + +func (l *slogSink) Enabled(level int) bool { + return l.handler.Enabled(context.Background(), slog.Level(-level)) +} + +func (l *slogSink) Info(level int, msg string, kvList ...interface{}) { + l.log(nil, msg, slog.Level(-level), kvList...) +} + +func (l *slogSink) Error(err error, msg string, kvList ...interface{}) { + l.log(err, msg, slog.LevelError, kvList...) +} + +func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) { + var pcs [1]uintptr + // skip runtime.Callers, this function, Info/Error, and all helper functions above that. + runtime.Callers(3+l.callDepth, pcs[:]) + + record := slog.NewRecord(time.Now(), level, msg, pcs[0]) + if l.name != "" { + record.AddAttrs(slog.String(nameKey, l.name)) + } + if err != nil { + record.AddAttrs(slog.Any(errKey, err)) + } + record.Add(kvList...) + _ = l.handler.Handle(context.Background(), record) +} + +func (l slogSink) WithName(name string) LogSink { + if l.name != "" { + l.name += "/" + } + l.name += name + return &l +} + +func (l slogSink) WithValues(kvList ...interface{}) LogSink { + l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...)) + return &l +} + +func kvListToAttrs(kvList ...interface{}) []slog.Attr { + // We don't need the record itself, only its Add method. + record := slog.NewRecord(time.Time{}, 0, "", 0) + record.Add(kvList...) + attrs := make([]slog.Attr, 0, record.NumAttrs()) + record.Attrs(func(attr slog.Attr) bool { + attrs = append(attrs, attr) + return true + }) + return attrs +} diff --git a/vendor/github.com/go-openapi/jsonpointer/.travis.yml b/vendor/github.com/go-openapi/jsonpointer/.travis.yml deleted file mode 100644 index 03a22fe06..000000000 --- a/vendor/github.com/go-openapi/jsonpointer/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -after_success: -- bash <(curl -s https://codecov.io/bash) -go: -- 1.14.x -- 1.15.x -install: -- GO111MODULE=off go get -u gotest.tools/gotestsum -env: -- GO111MODULE=on -language: go -notifications: - slack: - secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw= -script: -- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonreference/.golangci.yml b/vendor/github.com/go-openapi/jsonreference/.golangci.yml index f9381aee5..013fc1943 100644 --- a/vendor/github.com/go-openapi/jsonreference/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonreference/.golangci.yml @@ -1,8 +1,6 @@ linters-settings: govet: check-shadowing: true - golint: - min-confidence: 0 gocyclo: min-complexity: 30 maligned: @@ -12,6 +10,8 @@ linters-settings: goconst: min-len: 2 min-occurrences: 4 + paralleltest: + ignore-missing: true linters: enable-all: true disable: @@ -39,3 +39,12 @@ linters: - nestif - godot - errorlint + - varcheck + - interfacer + - deadcode + - golint + - ifshort + - structcheck + - nosnakecase + - varnamelen + - exhaustruct diff --git a/vendor/github.com/go-openapi/jsonreference/.travis.yml b/vendor/github.com/go-openapi/jsonreference/.travis.yml deleted file mode 100644 index 05482f4b9..000000000 --- a/vendor/github.com/go-openapi/jsonreference/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -after_success: -- bash <(curl -s https://codecov.io/bash) -go: -- 1.14.x -- 1.x -install: -- go get gotest.tools/gotestsum -jobs: - include: - # include linting job, but only for latest go version and amd64 arch - - go: 1.x - arch: amd64 - install: - go get github.com/golangci/golangci-lint/cmd/golangci-lint - script: - - golangci-lint run --new-from-rev master -env: -- GO111MODULE=on -language: go -notifications: - slack: - secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ= -script: -- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go index 8956c3088..f0610cf1e 100644 --- a/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go +++ b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go @@ -7,8 +7,8 @@ import ( ) const ( - defaultHttpPort = ":80" - defaultHttpsPort = ":443" + defaultHTTPPort = ":80" + defaultHTTPSPort = ":443" ) // Regular expressions used by the normalizations @@ -18,18 +18,24 @@ var rxDupSlashes = regexp.MustCompile(`/{2,}`) // NormalizeURL will normalize the specified URL // This was added to replace a previous call to the no longer maintained purell library: // The call that was used looked like the following: -// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) +// +// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) // // To explain all that was included in the call above, purell.FlagsSafe was really just the following: -// - FlagLowercaseScheme -// - FlagLowercaseHost -// - FlagRemoveDefaultPort -// - FlagRemoveDuplicateSlashes (and this was mixed in with the |) +// - FlagLowercaseScheme +// - FlagLowercaseHost +// - FlagRemoveDefaultPort +// - FlagRemoveDuplicateSlashes (and this was mixed in with the |) +// +// This also normalizes the URL into its urlencoded form by removing RawPath and RawFragment. func NormalizeURL(u *url.URL) { lowercaseScheme(u) lowercaseHost(u) removeDefaultPort(u) removeDuplicateSlashes(u) + + u.RawPath = "" + u.RawFragment = "" } func lowercaseScheme(u *url.URL) { @@ -48,7 +54,7 @@ func removeDefaultPort(u *url.URL) { if len(u.Host) > 0 { scheme := strings.ToLower(u.Scheme) u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { - if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) { + if (scheme == "http" && val == defaultHTTPPort) || (scheme == "https" && val == defaultHTTPSPort) { return "" } return val diff --git a/vendor/github.com/go-openapi/swag/.gitattributes b/vendor/github.com/go-openapi/swag/.gitattributes new file mode 100644 index 000000000..49ad52766 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/.gitattributes @@ -0,0 +1,2 @@ +# gofmt always uses LF, whereas Git uses CRLF on Windows. +*.go text eol=lf diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml index 813c47aa6..bf503e400 100644 --- a/vendor/github.com/go-openapi/swag/.golangci.yml +++ b/vendor/github.com/go-openapi/swag/.golangci.yml @@ -37,3 +37,18 @@ linters: - gci - gocognit - paralleltest + - thelper + - ifshort + - gomoddirectives + - cyclop + - forcetypeassert + - ireturn + - tagliatelle + - varnamelen + - goimports + - tenv + - golint + - exhaustruct + - nilnil + - nonamedreturns + - nosnakecase diff --git a/vendor/github.com/go-openapi/swag/.travis.yml b/vendor/github.com/go-openapi/swag/.travis.yml deleted file mode 100644 index fc25a8872..000000000 --- a/vendor/github.com/go-openapi/swag/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -after_success: -- bash <(curl -s https://codecov.io/bash) -go: -- 1.14.x -- 1.x -arch: -- amd64 -jobs: - include: - # include arch ppc, but only for latest go version - skip testing for race - - go: 1.x - arch: ppc64le - install: ~ - script: - - go test -v - - #- go: 1.x - # arch: arm - # install: ~ - # script: - # - go test -v - - # include linting job, but only for latest go version and amd64 arch - - go: 1.x - arch: amd64 - install: - go get github.com/golangci/golangci-lint/cmd/golangci-lint - script: - - golangci-lint run --new-from-rev master -install: -- GO111MODULE=off go get -u gotest.tools/gotestsum -language: go -notifications: - slack: - secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= -script: -- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/swag/doc.go b/vendor/github.com/go-openapi/swag/doc.go index 8d2c8c501..55094cb74 100644 --- a/vendor/github.com/go-openapi/swag/doc.go +++ b/vendor/github.com/go-openapi/swag/doc.go @@ -17,16 +17,15 @@ Package swag contains a bunch of helper functions for go-openapi and go-swagger You may also use it standalone for your projects. - * convert between value and pointers for builtin types - * convert from string to builtin types (wraps strconv) - * fast json concatenation - * search in path - * load from file or http - * name mangling - + - convert between value and pointers for builtin types + - convert from string to builtin types (wraps strconv) + - fast json concatenation + - search in path + - load from file or http + - name mangling This repo has only few dependencies outside of the standard library: - * YAML utilities depend on gopkg.in/yaml.v2 + - YAML utilities depend on gopkg.in/yaml.v2 */ package swag diff --git a/vendor/github.com/go-openapi/swag/file.go b/vendor/github.com/go-openapi/swag/file.go new file mode 100644 index 000000000..16accc55f --- /dev/null +++ b/vendor/github.com/go-openapi/swag/file.go @@ -0,0 +1,33 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package swag + +import "mime/multipart" + +// File represents an uploaded file. +type File struct { + Data multipart.File + Header *multipart.FileHeader +} + +// Read bytes from the file +func (f *File) Read(p []byte) (n int, err error) { + return f.Data.Read(p) +} + +// Close the file +func (f *File) Close() error { + return f.Data.Close() +} diff --git a/vendor/github.com/go-openapi/swag/loading.go b/vendor/github.com/go-openapi/swag/loading.go index 9a6040972..00038c377 100644 --- a/vendor/github.com/go-openapi/swag/loading.go +++ b/vendor/github.com/go-openapi/swag/loading.go @@ -16,10 +16,11 @@ package swag import ( "fmt" - "io/ioutil" + "io" "log" "net/http" "net/url" + "os" "path/filepath" "runtime" "strings" @@ -40,13 +41,13 @@ var LoadHTTPCustomHeaders = map[string]string{} // LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in func LoadFromFileOrHTTP(path string) ([]byte, error) { - return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path) + return LoadStrategy(path, os.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path) } // LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in // timeout arg allows for per request overriding of the request timeout func LoadFromFileOrHTTPWithTimeout(path string, timeout time.Duration) ([]byte, error) { - return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(timeout))(path) + return LoadStrategy(path, os.ReadFile, loadHTTPBytes(timeout))(path) } // LoadStrategy returns a loader function for a given path or uri @@ -86,7 +87,7 @@ func LoadStrategy(path string, local, remote func(string) ([]byte, error)) func( func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { return func(path string) ([]byte, error) { client := &http.Client{Timeout: timeout} - req, err := http.NewRequest("GET", path, nil) // nolint: noctx + req, err := http.NewRequest(http.MethodGet, path, nil) //nolint:noctx if err != nil { return nil, err } @@ -115,6 +116,6 @@ func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status) } - return ioutil.ReadAll(resp.Body) + return io.ReadAll(resp.Body) } } diff --git a/vendor/github.com/go-openapi/swag/post_go18.go b/vendor/github.com/go-openapi/swag/post_go18.go index c2e686d31..f5228b82c 100644 --- a/vendor/github.com/go-openapi/swag/post_go18.go +++ b/vendor/github.com/go-openapi/swag/post_go18.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build go1.8 // +build go1.8 package swag diff --git a/vendor/github.com/go-openapi/swag/post_go19.go b/vendor/github.com/go-openapi/swag/post_go19.go index eb2f2d8bc..7c7da9c08 100644 --- a/vendor/github.com/go-openapi/swag/post_go19.go +++ b/vendor/github.com/go-openapi/swag/post_go19.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build go1.9 // +build go1.9 package swag diff --git a/vendor/github.com/go-openapi/swag/pre_go18.go b/vendor/github.com/go-openapi/swag/pre_go18.go index 6607f3393..2757d9b95 100644 --- a/vendor/github.com/go-openapi/swag/pre_go18.go +++ b/vendor/github.com/go-openapi/swag/pre_go18.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !go1.8 // +build !go1.8 package swag diff --git a/vendor/github.com/go-openapi/swag/pre_go19.go b/vendor/github.com/go-openapi/swag/pre_go19.go index 4bae187d1..0565db377 100644 --- a/vendor/github.com/go-openapi/swag/pre_go19.go +++ b/vendor/github.com/go-openapi/swag/pre_go19.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !go1.9 // +build !go1.9 package swag diff --git a/vendor/github.com/go-openapi/swag/util.go b/vendor/github.com/go-openapi/swag/util.go index 193702f2c..f78ab684a 100644 --- a/vendor/github.com/go-openapi/swag/util.go +++ b/vendor/github.com/go-openapi/swag/util.go @@ -99,10 +99,11 @@ const ( ) // JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute): -// ssv: space separated value -// tsv: tab separated value -// pipes: pipe (|) separated value -// csv: comma separated value (default) +// +// ssv: space separated value +// tsv: tab separated value +// pipes: pipe (|) separated value +// csv: comma separated value (default) func JoinByFormat(data []string, format string) []string { if len(data) == 0 { return data @@ -124,11 +125,11 @@ func JoinByFormat(data []string, format string) []string { } // SplitByFormat splits a string by a known format: -// ssv: space separated value -// tsv: tab separated value -// pipes: pipe (|) separated value -// csv: comma separated value (default) // +// ssv: space separated value +// tsv: tab separated value +// pipes: pipe (|) separated value +// csv: comma separated value (default) func SplitByFormat(data, format string) []string { if data == "" { return nil diff --git a/vendor/github.com/go-openapi/swag/yaml.go b/vendor/github.com/go-openapi/swag/yaml.go index ec9691440..f09ee609f 100644 --- a/vendor/github.com/go-openapi/swag/yaml.go +++ b/vendor/github.com/go-openapi/swag/yaml.go @@ -22,7 +22,7 @@ import ( "github.com/mailru/easyjson/jlexer" "github.com/mailru/easyjson/jwriter" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" ) // YAMLMatcher matches yaml @@ -43,16 +43,126 @@ func YAMLToJSON(data interface{}) (json.RawMessage, error) { // BytesToYAMLDoc converts a byte slice into a YAML document func BytesToYAMLDoc(data []byte) (interface{}, error) { - var canary map[interface{}]interface{} // validate this is an object and not a different type - if err := yaml.Unmarshal(data, &canary); err != nil { + var document yaml.Node // preserve order that is present in the document + if err := yaml.Unmarshal(data, &document); err != nil { return nil, err } + if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode { + return nil, fmt.Errorf("only YAML documents that are objects are supported") + } + return &document, nil +} - var document yaml.MapSlice // preserve order that is present in the document - if err := yaml.Unmarshal(data, &document); err != nil { - return nil, err +func yamlNode(root *yaml.Node) (interface{}, error) { + switch root.Kind { + case yaml.DocumentNode: + return yamlDocument(root) + case yaml.SequenceNode: + return yamlSequence(root) + case yaml.MappingNode: + return yamlMapping(root) + case yaml.ScalarNode: + return yamlScalar(root) + case yaml.AliasNode: + return yamlNode(root.Alias) + default: + return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind) + } +} + +func yamlDocument(node *yaml.Node) (interface{}, error) { + if len(node.Content) != 1 { + return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content)) + } + return yamlNode(node.Content[0]) +} + +func yamlMapping(node *yaml.Node) (interface{}, error) { + m := make(JSONMapSlice, len(node.Content)/2) + + var j int + for i := 0; i < len(node.Content); i += 2 { + var nmi JSONMapItem + k, err := yamlStringScalarC(node.Content[i]) + if err != nil { + return nil, fmt.Errorf("unable to decode YAML map key: %w", err) + } + nmi.Key = k + v, err := yamlNode(node.Content[i+1]) + if err != nil { + return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err) + } + nmi.Value = v + m[j] = nmi + j++ + } + return m, nil +} + +func yamlSequence(node *yaml.Node) (interface{}, error) { + s := make([]interface{}, 0) + + for i := 0; i < len(node.Content); i++ { + + v, err := yamlNode(node.Content[i]) + if err != nil { + return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err) + } + s = append(s, v) + } + return s, nil +} + +const ( // See https://yaml.org/type/ + yamlStringScalar = "tag:yaml.org,2002:str" + yamlIntScalar = "tag:yaml.org,2002:int" + yamlBoolScalar = "tag:yaml.org,2002:bool" + yamlFloatScalar = "tag:yaml.org,2002:float" + yamlTimestamp = "tag:yaml.org,2002:timestamp" + yamlNull = "tag:yaml.org,2002:null" +) + +func yamlScalar(node *yaml.Node) (interface{}, error) { + switch node.LongTag() { + case yamlStringScalar: + return node.Value, nil + case yamlBoolScalar: + b, err := strconv.ParseBool(node.Value) + if err != nil { + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err) + } + return b, nil + case yamlIntScalar: + i, err := strconv.ParseInt(node.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err) + } + return i, nil + case yamlFloatScalar: + f, err := strconv.ParseFloat(node.Value, 64) + if err != nil { + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err) + } + return f, nil + case yamlTimestamp: + return node.Value, nil + case yamlNull: + return nil, nil + default: + return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag()) + } +} + +func yamlStringScalarC(node *yaml.Node) (string, error) { + if node.Kind != yaml.ScalarNode { + return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind) + } + switch node.LongTag() { + case yamlStringScalar, yamlIntScalar, yamlFloatScalar: + return node.Value, nil + default: + return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag()) } - return document, nil } // JSONMapSlice represent a JSON object, with the order of keys maintained @@ -105,6 +215,113 @@ func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) { *s = result } +func (s JSONMapSlice) MarshalYAML() (interface{}, error) { + var n yaml.Node + n.Kind = yaml.DocumentNode + var nodes []*yaml.Node + for _, item := range s { + nn, err := json2yaml(item.Value) + if err != nil { + return nil, err + } + ns := []*yaml.Node{ + { + Kind: yaml.ScalarNode, + Tag: yamlStringScalar, + Value: item.Key, + }, + nn, + } + nodes = append(nodes, ns...) + } + + n.Content = []*yaml.Node{ + { + Kind: yaml.MappingNode, + Content: nodes, + }, + } + + return yaml.Marshal(&n) +} + +func json2yaml(item interface{}) (*yaml.Node, error) { + switch val := item.(type) { + case JSONMapSlice: + var n yaml.Node + n.Kind = yaml.MappingNode + for i := range val { + childNode, err := json2yaml(&val[i].Value) + if err != nil { + return nil, err + } + n.Content = append(n.Content, &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlStringScalar, + Value: val[i].Key, + }, childNode) + } + return &n, nil + case map[string]interface{}: + var n yaml.Node + n.Kind = yaml.MappingNode + for k, v := range val { + childNode, err := json2yaml(v) + if err != nil { + return nil, err + } + n.Content = append(n.Content, &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlStringScalar, + Value: k, + }, childNode) + } + return &n, nil + case []interface{}: + var n yaml.Node + n.Kind = yaml.SequenceNode + for i := range val { + childNode, err := json2yaml(val[i]) + if err != nil { + return nil, err + } + n.Content = append(n.Content, childNode) + } + return &n, nil + case string: + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlStringScalar, + Value: val, + }, nil + case float64: + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlFloatScalar, + Value: strconv.FormatFloat(val, 'f', -1, 64), + }, nil + case int64: + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlIntScalar, + Value: strconv.FormatInt(val, 10), + }, nil + case uint64: + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlIntScalar, + Value: strconv.FormatUint(val, 10), + }, nil + case bool: + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: yamlBoolScalar, + Value: strconv.FormatBool(val), + }, nil + } + return nil, nil +} + // JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice type JSONMapItem struct { Key string @@ -173,23 +390,10 @@ func transformData(input interface{}) (out interface{}, err error) { } switch in := input.(type) { - case yaml.MapSlice: - - o := make(JSONMapSlice, len(in)) - for i, mi := range in { - var nmi JSONMapItem - if nmi.Key, err = format(mi.Key); err != nil { - return nil, err - } - - v, ert := transformData(mi.Value) - if ert != nil { - return nil, ert - } - nmi.Value = v - o[i] = nmi - } - return o, nil + case yaml.Node: + return yamlNode(&in) + case *yaml.Node: + return yamlNode(in) case map[interface{}]interface{}: o := make(JSONMapSlice, 0, len(in)) for ke, va := range in { diff --git a/vendor/github.com/golang/glog/LICENSE b/vendor/github.com/golang/glog/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/vendor/github.com/golang/glog/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/golang/glog/README.md b/vendor/github.com/golang/glog/README.md deleted file mode 100644 index a4f73883b..000000000 --- a/vendor/github.com/golang/glog/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# glog - -[![PkgGoDev](https://pkg.go.dev/badge/github.com/golang/glog)](https://pkg.go.dev/github.com/golang/glog) - -Leveled execution logs for Go. - -This is an efficient pure Go implementation of leveled logs in the -manner of the open source C++ package [_glog_](https://github.com/google/glog). - -By binding methods to booleans it is possible to use the log package without paying the expense of evaluating the arguments to the log. Through the `-vmodule` flag, the package also provides fine-grained -control over logging at the file level. - -The comment from `glog.go` introduces the ideas: - -Package _glog_ implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. It provides the functions Info, Warning, Error, Fatal, plus formatting variants such as Infof. It also provides V-style loggingcontrolled by the `-v` and `-vmodule=file=2` flags. - -Basic examples: - -```go -glog.Info("Prepare to repel boarders") - -glog.Fatalf("Initialization failed: %s", err) -``` - -See the documentation for the V function for an explanation of these examples: - -```go -if glog.V(2) { - glog.Info("Starting transaction...") -} -glog.V(2).Infoln("Processed", nItems, "elements") -``` - -The repository contains an open source version of the log package used inside Google. The master copy of the source lives inside Google, not here. The code in this repo is for export only and is not itself under development. Feature requests will be ignored. - -Send bug reports to golang-nuts@googlegroups.com. diff --git a/vendor/github.com/golang/glog/glog.go b/vendor/github.com/golang/glog/glog.go deleted file mode 100644 index e108ae8b4..000000000 --- a/vendor/github.com/golang/glog/glog.go +++ /dev/null @@ -1,621 +0,0 @@ -// Go support for leveled logs, analogous to https://github.com/google/glog. -// -// Copyright 2023 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. -// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as -// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. -// -// Basic examples: -// -// glog.Info("Prepare to repel boarders") -// -// glog.Fatalf("Initialization failed: %s", err) -// -// See the documentation for the V function for an explanation of these examples: -// -// if glog.V(2) { -// glog.Info("Starting transaction...") -// } -// -// glog.V(2).Infoln("Processed", nItems, "elements") -// -// Log output is buffered and written periodically using Flush. Programs -// should call Flush before exiting to guarantee all log output is written. -// -// By default, all log statements write to files in a temporary directory. -// This package provides several flags that modify this behavior. -// As a result, flag.Parse must be called before any logging is done. -// -// -logtostderr=false -// Logs are written to standard error instead of to files. -// -alsologtostderr=false -// Logs are written to standard error as well as to files. -// -stderrthreshold=ERROR -// Log events at or above this severity are logged to standard -// error as well as to files. -// -log_dir="" -// Log files will be written to this directory instead of the -// default temporary directory. -// -// Other flags provide aids to debugging. -// -// -log_backtrace_at="" -// A comma-separated list of file and line numbers holding a logging -// statement, such as -// -log_backtrace_at=gopherflakes.go:234 -// A stack trace will be written to the Info log whenever execution -// hits one of these statements. (Unlike with -vmodule, the ".go" -// must bepresent.) -// -v=0 -// Enable V-leveled logging at the specified level. -// -vmodule="" -// The syntax of the argument is a comma-separated list of pattern=N, -// where pattern is a literal file name (minus the ".go" suffix) or -// "glob" pattern and N is a V level. For instance, -// -vmodule=gopher*=3 -// sets the V level to 3 in all Go files whose names begin with "gopher", -// and -// -vmodule=/path/to/glog/glog_test=1 -// sets the V level to 1 in the Go file /path/to/glog/glog_test.go. -// If a glob pattern contains a slash, it is matched against the full path, -// and the file name. Otherwise, the pattern is -// matched only against the file's basename. When both -vmodule and -v -// are specified, the -vmodule values take precedence for the specified -// modules. -package glog - -// This file contains the parts of the log package that are shared among all -// implementations (file, envelope, and appengine). - -import ( - "bytes" - "errors" - "fmt" - stdLog "log" - "os" - "reflect" - "runtime" - "runtime/pprof" - "strconv" - "sync" - "sync/atomic" - "syscall" - "time" - - "github.com/golang/glog/internal/logsink" - "github.com/golang/glog/internal/stackdump" -) - -var timeNow = time.Now // Stubbed out for testing. - -// MaxSize is the maximum size of a log file in bytes. -var MaxSize uint64 = 1024 * 1024 * 1800 - -// ErrNoLog is the error we return if no log file has yet been created -// for the specified log type. -var ErrNoLog = errors.New("log file not yet created") - -// OutputStats tracks the number of output lines and bytes written. -type OutputStats struct { - lines int64 - bytes int64 -} - -// Lines returns the number of lines written. -func (s *OutputStats) Lines() int64 { - return atomic.LoadInt64(&s.lines) -} - -// Bytes returns the number of bytes written. -func (s *OutputStats) Bytes() int64 { - return atomic.LoadInt64(&s.bytes) -} - -// Stats tracks the number of lines of output and number of bytes -// per severity level. Values must be read with atomic.LoadInt64. -var Stats struct { - Info, Warning, Error OutputStats -} - -var severityStats = [...]*OutputStats{ - logsink.Info: &Stats.Info, - logsink.Warning: &Stats.Warning, - logsink.Error: &Stats.Error, - logsink.Fatal: nil, -} - -// Level specifies a level of verbosity for V logs. The -v flag is of type -// Level and should be modified only through the flag.Value interface. -type Level int32 - -var metaPool sync.Pool // Pool of *logsink.Meta. - -// metaPoolGet returns a *logsink.Meta from metaPool as both an interface and a -// pointer, allocating a new one if necessary. (Returning the interface value -// directly avoids an allocation if there was an existing pointer in the pool.) -func metaPoolGet() (any, *logsink.Meta) { - if metai := metaPool.Get(); metai != nil { - return metai, metai.(*logsink.Meta) - } - meta := new(logsink.Meta) - return meta, meta -} - -type stack bool - -const ( - noStack = stack(false) - withStack = stack(true) -) - -func appendBacktrace(depth int, format string, args []any) (string, []any) { - // Capture a backtrace as a stackdump.Stack (both text and PC slice). - // Structured log sinks can extract the backtrace in whichever format they - // prefer (PCs or text), and Text sinks will include it as just another part - // of the log message. - // - // Use depth instead of depth+1 so that the backtrace always includes the - // log function itself - otherwise the reason for the trace appearing in the - // log may not be obvious to the reader. - dump := stackdump.Caller(depth) - - // Add an arg and an entry in the format string for the stack dump. - // - // Copy the "args" slice to avoid a rare but serious aliasing bug - // (corrupting the caller's slice if they passed it to a non-Fatal call - // using "..."). - format = format + "\n\n%v\n" - args = append(append([]any(nil), args...), dump) - - return format, args -} - -// logf writes a log message for a log function call (or log function wrapper) -// at the given depth in the current goroutine's stack. -func logf(depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) { - now := timeNow() - _, file, line, ok := runtime.Caller(depth + 1) - if !ok { - file = "???" - line = 1 - } - - if stack == withStack || backtraceAt(file, line) { - format, args = appendBacktrace(depth+1, format, args) - } - - metai, meta := metaPoolGet() - *meta = logsink.Meta{ - Time: now, - File: file, - Line: line, - Depth: depth + 1, - Severity: severity, - Verbose: verbose, - Thread: int64(pid), - } - sinkf(meta, format, args...) - metaPool.Put(metai) -} - -func sinkf(meta *logsink.Meta, format string, args ...any) { - meta.Depth++ - n, err := logsink.Printf(meta, format, args...) - if stats := severityStats[meta.Severity]; stats != nil { - atomic.AddInt64(&stats.lines, 1) - atomic.AddInt64(&stats.bytes, int64(n)) - } - - if err != nil { - logsink.Printf(meta, "glog: exiting because of error: %s", err) - sinks.file.Flush() - os.Exit(2) - } -} - -// CopyStandardLogTo arranges for messages written to the Go "log" package's -// default logs to also appear in the Google logs for the named and lower -// severities. Subsequent changes to the standard log's default output location -// or format may break this behavior. -// -// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not -// recognized, CopyStandardLogTo panics. -func CopyStandardLogTo(name string) { - sev, err := logsink.ParseSeverity(name) - if err != nil { - panic(fmt.Sprintf("log.CopyStandardLogTo(%q): %v", name, err)) - } - // Set a log format that captures the user's file and line: - // d.go:23: message - stdLog.SetFlags(stdLog.Lshortfile) - stdLog.SetOutput(logBridge(sev)) -} - -// NewStandardLogger returns a Logger that writes to the Google logs for the -// named and lower severities. -// -// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not -// recognized, NewStandardLogger panics. -func NewStandardLogger(name string) *stdLog.Logger { - sev, err := logsink.ParseSeverity(name) - if err != nil { - panic(fmt.Sprintf("log.NewStandardLogger(%q): %v", name, err)) - } - return stdLog.New(logBridge(sev), "", stdLog.Lshortfile) -} - -// logBridge provides the Write method that enables CopyStandardLogTo to connect -// Go's standard logs to the logs provided by this package. -type logBridge logsink.Severity - -// Write parses the standard logging line and passes its components to the -// logger for severity(lb). -func (lb logBridge) Write(b []byte) (n int, err error) { - var ( - file = "???" - line = 1 - text string - ) - // Split "d.go:23: message" into "d.go", "23", and "message". - if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { - text = fmt.Sprintf("bad log format: %s", b) - } else { - file = string(parts[0]) - text = string(parts[2][1:]) // skip leading space - line, err = strconv.Atoi(string(parts[1])) - if err != nil { - text = fmt.Sprintf("bad line number: %s", b) - line = 1 - } - } - - // The depth below hard-codes details of how stdlog gets here. The alternative would be to walk - // up the stack looking for src/log/log.go but that seems like it would be - // unfortunately slow. - const stdLogDepth = 4 - - metai, meta := metaPoolGet() - *meta = logsink.Meta{ - Time: timeNow(), - File: file, - Line: line, - Depth: stdLogDepth, - Severity: logsink.Severity(lb), - Thread: int64(pid), - } - - format := "%s" - args := []any{text} - if backtraceAt(file, line) { - format, args = appendBacktrace(meta.Depth, format, args) - } - - sinkf(meta, format, args...) - metaPool.Put(metai) - - return len(b), nil -} - -// defaultFormat returns a fmt.Printf format specifier that formats its -// arguments as if they were passed to fmt.Print. -func defaultFormat(args []any) string { - n := len(args) - switch n { - case 0: - return "" - case 1: - return "%v" - } - - b := make([]byte, 0, n*3-1) - wasString := true // Suppress leading space. - for _, arg := range args { - isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String - if wasString || isString { - b = append(b, "%v"...) - } else { - b = append(b, " %v"...) - } - wasString = isString - } - return string(b) -} - -// lnFormat returns a fmt.Printf format specifier that formats its arguments -// as if they were passed to fmt.Println. -func lnFormat(args []any) string { - if len(args) == 0 { - return "\n" - } - - b := make([]byte, 0, len(args)*3) - for range args { - b = append(b, "%v "...) - } - b[len(b)-1] = '\n' // Replace the last space with a newline. - return string(b) -} - -// Verbose is a boolean type that implements Infof (like Printf) etc. -// See the documentation of V for more information. -type Verbose bool - -// V reports whether verbosity at the call site is at least the requested level. -// The returned value is a boolean of type Verbose, which implements Info, Infoln -// and Infof. These methods will write to the Info log if called. -// Thus, one may write either -// -// if glog.V(2) { glog.Info("log this") } -// -// or -// -// glog.V(2).Info("log this") -// -// The second form is shorter but the first is cheaper if logging is off because it does -// not evaluate its arguments. -// -// Whether an individual call to V generates a log record depends on the setting of -// the -v and --vmodule flags; both are off by default. If the level in the call to -// V is at most the value of -v, or of -vmodule for the source file containing the -// call, the V call will log. -func V(level Level) Verbose { - return VDepth(1, level) -} - -// VDepth acts as V but uses depth to determine which call frame to check vmodule for. -// VDepth(0, level) is the same as V(level). -func VDepth(depth int, level Level) Verbose { - return Verbose(verboseEnabled(depth+1, level)) -} - -// Info is equivalent to the global Info function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Info(args ...any) { - v.InfoDepth(1, args...) -} - -// InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) InfoDepth(depth int, args ...any) { - if v { - logf(depth+1, logsink.Info, true, noStack, defaultFormat(args), args...) - } -} - -// InfoDepthf is equivalent to the global InfoDepthf function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) InfoDepthf(depth int, format string, args ...any) { - if v { - logf(depth+1, logsink.Info, true, noStack, format, args...) - } -} - -// Infoln is equivalent to the global Infoln function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infoln(args ...any) { - if v { - logf(1, logsink.Info, true, noStack, lnFormat(args), args...) - } -} - -// Infof is equivalent to the global Infof function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infof(format string, args ...any) { - if v { - logf(1, logsink.Info, true, noStack, format, args...) - } -} - -// Info logs to the INFO log. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Info(args ...any) { - InfoDepth(1, args...) -} - -// InfoDepth calls Info from a different depth in the call stack. -// This enables a callee to emit logs that use the callsite information of its caller -// or any other callers in the stack. When depth == 0, the original callee's line -// information is emitted. When depth > 0, depth frames are skipped in the call stack -// and the final frame is treated like the original callee to Info. -func InfoDepth(depth int, args ...any) { - logf(depth+1, logsink.Info, false, noStack, defaultFormat(args), args...) -} - -// InfoDepthf acts as InfoDepth but with format string. -func InfoDepthf(depth int, format string, args ...any) { - logf(depth+1, logsink.Info, false, noStack, format, args...) -} - -// Infoln logs to the INFO log. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Infoln(args ...any) { - logf(1, logsink.Info, false, noStack, lnFormat(args), args...) -} - -// Infof logs to the INFO log. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Infof(format string, args ...any) { - logf(1, logsink.Info, false, noStack, format, args...) -} - -// Warning logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Warning(args ...any) { - WarningDepth(1, args...) -} - -// WarningDepth acts as Warning but uses depth to determine which call frame to log. -// WarningDepth(0, "msg") is the same as Warning("msg"). -func WarningDepth(depth int, args ...any) { - logf(depth+1, logsink.Warning, false, noStack, defaultFormat(args), args...) -} - -// WarningDepthf acts as Warningf but uses depth to determine which call frame to log. -// WarningDepthf(0, "msg") is the same as Warningf("msg"). -func WarningDepthf(depth int, format string, args ...any) { - logf(depth+1, logsink.Warning, false, noStack, format, args...) -} - -// Warningln logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Warningln(args ...any) { - logf(1, logsink.Warning, false, noStack, lnFormat(args), args...) -} - -// Warningf logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Warningf(format string, args ...any) { - logf(1, logsink.Warning, false, noStack, format, args...) -} - -// Error logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Error(args ...any) { - ErrorDepth(1, args...) -} - -// ErrorDepth acts as Error but uses depth to determine which call frame to log. -// ErrorDepth(0, "msg") is the same as Error("msg"). -func ErrorDepth(depth int, args ...any) { - logf(depth+1, logsink.Error, false, noStack, defaultFormat(args), args...) -} - -// ErrorDepthf acts as Errorf but uses depth to determine which call frame to log. -// ErrorDepthf(0, "msg") is the same as Errorf("msg"). -func ErrorDepthf(depth int, format string, args ...any) { - logf(depth+1, logsink.Error, false, noStack, format, args...) -} - -// Errorln logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Errorln(args ...any) { - logf(1, logsink.Error, false, noStack, lnFormat(args), args...) -} - -// Errorf logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Errorf(format string, args ...any) { - logf(1, logsink.Error, false, noStack, format, args...) -} - -func fatalf(depth int, format string, args ...any) { - logf(depth+1, logsink.Fatal, false, withStack, format, args...) - sinks.file.Flush() - - err := abortProcess() // Should not return. - - // Failed to abort the process using signals. Dump a stack trace and exit. - Errorf("abortProcess returned unexpectedly: %v", err) - sinks.file.Flush() - pprof.Lookup("goroutine").WriteTo(os.Stderr, 1) - os.Exit(2) // Exit with the same code as the default SIGABRT handler. -} - -// abortProcess attempts to kill the current process in a way that will dump the -// currently-running goroutines someplace useful (Coroner or stderr). -// -// It does this by sending SIGABRT to the current process. Unfortunately, the -// signal may or may not be delivered to the current thread; in order to do that -// portably, we would need to add a cgo dependency and call pthread_kill. -// -// If successful, abortProcess does not return. -func abortProcess() error { - p, err := os.FindProcess(os.Getpid()) - if err != nil { - return err - } - if err := p.Signal(syscall.SIGABRT); err != nil { - return err - } - - // Sent the signal. Now we wait for it to arrive and any SIGABRT handlers to - // run (and eventually terminate the process themselves). - // - // We could just "select{}" here, but there's an outside chance that would - // trigger the runtime's deadlock detector if there happen not to be any - // background goroutines running. So we'll sleep a while first to give - // the signal some time. - time.Sleep(10 * time.Second) - select {} -} - -// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(2). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Fatal(args ...any) { - FatalDepth(1, args...) -} - -// FatalDepth acts as Fatal but uses depth to determine which call frame to log. -// FatalDepth(0, "msg") is the same as Fatal("msg"). -func FatalDepth(depth int, args ...any) { - fatalf(depth+1, defaultFormat(args), args...) -} - -// FatalDepthf acts as Fatalf but uses depth to determine which call frame to log. -// FatalDepthf(0, "msg") is the same as Fatalf("msg"). -func FatalDepthf(depth int, format string, args ...any) { - fatalf(depth+1, format, args...) -} - -// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(2). -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Fatalln(args ...any) { - fatalf(1, lnFormat(args), args...) -} - -// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(2). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Fatalf(format string, args ...any) { - fatalf(1, format, args...) -} - -func exitf(depth int, format string, args ...any) { - logf(depth+1, logsink.Fatal, false, noStack, format, args...) - sinks.file.Flush() - os.Exit(1) -} - -// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Exit(args ...any) { - ExitDepth(1, args...) -} - -// ExitDepth acts as Exit but uses depth to determine which call frame to log. -// ExitDepth(0, "msg") is the same as Exit("msg"). -func ExitDepth(depth int, args ...any) { - exitf(depth+1, defaultFormat(args), args...) -} - -// ExitDepthf acts as Exitf but uses depth to determine which call frame to log. -// ExitDepthf(0, "msg") is the same as Exitf("msg"). -func ExitDepthf(depth int, format string, args ...any) { - exitf(depth+1, format, args...) -} - -// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -func Exitln(args ...any) { - exitf(1, lnFormat(args), args...) -} - -// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Exitf(format string, args ...any) { - exitf(1, format, args...) -} diff --git a/vendor/github.com/golang/glog/glog_file.go b/vendor/github.com/golang/glog/glog_file.go deleted file mode 100644 index af1c934b8..000000000 --- a/vendor/github.com/golang/glog/glog_file.go +++ /dev/null @@ -1,407 +0,0 @@ -// Go support for leveled logs, analogous to https://github.com/google/glog. -// -// Copyright 2023 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// File I/O for logs. - -package glog - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "io" - "os" - "os/user" - "path/filepath" - "runtime" - "strings" - "sync" - "time" - - "github.com/golang/glog/internal/logsink" -) - -// logDirs lists the candidate directories for new log files. -var logDirs []string - -var ( - // If non-empty, overrides the choice of directory in which to write logs. - // See createLogDirs for the full list of possible destinations. - logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") - logLink = flag.String("log_link", "", "If non-empty, add symbolic links in this directory to the log files") - logBufLevel = flag.Int("logbuflevel", int(logsink.Info), "Buffer log messages logged at this level or lower"+ - " (-1 means don't buffer; 0 means buffer INFO only; ...). Has limited applicability on non-prod platforms.") -) - -func createLogDirs() { - if *logDir != "" { - logDirs = append(logDirs, *logDir) - } - logDirs = append(logDirs, os.TempDir()) -} - -var ( - pid = os.Getpid() - program = filepath.Base(os.Args[0]) - host = "unknownhost" - userName = "unknownuser" -) - -func init() { - h, err := os.Hostname() - if err == nil { - host = shortHostname(h) - } - - current, err := user.Current() - if err == nil { - userName = current.Username - } - // Sanitize userName since it is used to construct file paths. - userName = strings.Map(func(r rune) rune { - switch { - case r >= 'a' && r <= 'z': - case r >= 'A' && r <= 'Z': - case r >= '0' && r <= '9': - default: - return '_' - } - return r - }, userName) -} - -// shortHostname returns its argument, truncating at the first period. -// For instance, given "www.google.com" it returns "www". -func shortHostname(hostname string) string { - if i := strings.Index(hostname, "."); i >= 0 { - return hostname[:i] - } - return hostname -} - -// logName returns a new log file name containing tag, with start time t, and -// the name for the symlink for tag. -func logName(tag string, t time.Time) (name, link string) { - name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", - program, - host, - userName, - tag, - t.Year(), - t.Month(), - t.Day(), - t.Hour(), - t.Minute(), - t.Second(), - pid) - return name, program + "." + tag -} - -var onceLogDirs sync.Once - -// create creates a new log file and returns the file and its filename, which -// contains tag ("INFO", "FATAL", etc.) and t. If the file is created -// successfully, create also attempts to update the symlink for that tag, ignoring -// errors. -func create(tag string, t time.Time) (f *os.File, filename string, err error) { - onceLogDirs.Do(createLogDirs) - if len(logDirs) == 0 { - return nil, "", errors.New("log: no log dirs") - } - name, link := logName(tag, t) - var lastErr error - for _, dir := range logDirs { - fname := filepath.Join(dir, name) - f, err := os.Create(fname) - if err == nil { - symlink := filepath.Join(dir, link) - os.Remove(symlink) // ignore err - os.Symlink(name, symlink) // ignore err - return f, fname, nil - } - lastErr = err - } - return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) -} - -// flushSyncWriter is the interface satisfied by logging destinations. -type flushSyncWriter interface { - Flush() error - Sync() error - io.Writer - filenames() []string -} - -var sinks struct { - stderr stderrSink - file fileSink -} - -func init() { - sinks.stderr.w = os.Stderr - - // Register stderr first: that way if we crash during file-writing at least - // the log will have gone somewhere. - logsink.TextSinks = append(logsink.TextSinks, &sinks.stderr, &sinks.file) - - sinks.file.flushChan = make(chan logsink.Severity, 1) - go sinks.file.flushDaemon() -} - -// stderrSink is a logsink.Text that writes log entries to stderr -// if they meet certain conditions. -type stderrSink struct { - mu sync.Mutex - w io.Writer -} - -// Enabled implements logsink.Text.Enabled. It returns true if any of the -// various stderr flags are enabled for logs of the given severity, if the log -// message is from the standard "log" package, or if google.Init has not yet run -// (and hence file logging is not yet initialized). -func (s *stderrSink) Enabled(m *logsink.Meta) bool { - return toStderr || alsoToStderr || m.Severity >= stderrThreshold.get() -} - -// Emit implements logsink.Text.Emit. -func (s *stderrSink) Emit(m *logsink.Meta, data []byte) (n int, err error) { - s.mu.Lock() - defer s.mu.Unlock() - - dn, err := s.w.Write(data) - n += dn - return n, err -} - -// severityWriters is an array of flushSyncWriter with a value for each -// logsink.Severity. -type severityWriters [4]flushSyncWriter - -// fileSink is a logsink.Text that prints to a set of Google log files. -type fileSink struct { - mu sync.Mutex - // file holds writer for each of the log types. - file severityWriters - flushChan chan logsink.Severity -} - -// Enabled implements logsink.Text.Enabled. It returns true if google.Init -// has run and both --disable_log_to_disk and --logtostderr are false. -func (s *fileSink) Enabled(m *logsink.Meta) bool { - return !toStderr -} - -// Emit implements logsink.Text.Emit -func (s *fileSink) Emit(m *logsink.Meta, data []byte) (n int, err error) { - s.mu.Lock() - defer s.mu.Unlock() - - if err = s.createMissingFiles(m.Severity); err != nil { - return 0, err - } - for sev := m.Severity; sev >= logsink.Info; sev-- { - if _, fErr := s.file[sev].Write(data); fErr != nil && err == nil { - err = fErr // Take the first error. - } - } - n = len(data) - if int(m.Severity) > *logBufLevel { - select { - case s.flushChan <- m.Severity: - default: - } - } - - return n, err -} - -// syncBuffer joins a bufio.Writer to its underlying file, providing access to the -// file's Sync method and providing a wrapper for the Write method that provides log -// file rotation. There are conflicting methods, so the file cannot be embedded. -// s.mu is held for all its methods. -type syncBuffer struct { - sink *fileSink - *bufio.Writer - file *os.File - names []string - sev logsink.Severity - nbytes uint64 // The number of bytes written to this file -} - -func (sb *syncBuffer) Sync() error { - return sb.file.Sync() -} - -func (sb *syncBuffer) Write(p []byte) (n int, err error) { - if sb.nbytes+uint64(len(p)) >= MaxSize { - if err := sb.rotateFile(time.Now()); err != nil { - return 0, err - } - } - n, err = sb.Writer.Write(p) - sb.nbytes += uint64(n) - return n, err -} - -func (sb *syncBuffer) filenames() []string { - return sb.names -} - -const footer = "\nCONTINUED IN NEXT FILE\n" - -// rotateFile closes the syncBuffer's file and starts a new one. -func (sb *syncBuffer) rotateFile(now time.Time) error { - var err error - pn := "" - file, name, err := create(sb.sev.String(), now) - - if sb.file != nil { - // The current log file becomes the previous log at the end of - // this block, so save its name for use in the header of the next - // file. - pn = sb.file.Name() - sb.Flush() - // If there's an existing file, write a footer with the name of - // the next file in the chain, followed by the constant string - // \nCONTINUED IN NEXT FILE\n to make continuation detection simple. - sb.file.Write([]byte("Next log: ")) - sb.file.Write([]byte(name)) - sb.file.Write([]byte(footer)) - sb.file.Close() - } - - sb.file = file - sb.names = append(sb.names, name) - sb.nbytes = 0 - if err != nil { - return err - } - - sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) - - // Write header. - var buf bytes.Buffer - fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) - fmt.Fprintf(&buf, "Running on machine: %s\n", host) - fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) - fmt.Fprintf(&buf, "Previous log: %s\n", pn) - fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") - n, err := sb.file.Write(buf.Bytes()) - sb.nbytes += uint64(n) - return err -} - -// bufferSize sizes the buffer associated with each log file. It's large -// so that log records can accumulate without the logging thread blocking -// on disk I/O. The flushDaemon will block instead. -const bufferSize = 256 * 1024 - -// createMissingFiles creates all the log files for severity from infoLog up to -// upTo that have not already been created. -// s.mu is held. -func (s *fileSink) createMissingFiles(upTo logsink.Severity) error { - if s.file[upTo] != nil { - return nil - } - now := time.Now() - // Files are created in increasing severity order, so we can be assured that - // if a high severity logfile exists, then so do all of lower severity. - for sev := logsink.Info; sev <= upTo; sev++ { - if s.file[sev] != nil { - continue - } - sb := &syncBuffer{ - sink: s, - sev: sev, - } - if err := sb.rotateFile(now); err != nil { - return err - } - s.file[sev] = sb - } - return nil -} - -// flushDaemon periodically flushes the log file buffers. -func (s *fileSink) flushDaemon() { - tick := time.NewTicker(30 * time.Second) - defer tick.Stop() - for { - select { - case <-tick.C: - s.Flush() - case sev := <-s.flushChan: - s.flush(sev) - } - } -} - -// Flush flushes all pending log I/O. -func Flush() { - sinks.file.Flush() -} - -// Flush flushes all the logs and attempts to "sync" their data to disk. -func (s *fileSink) Flush() error { - return s.flush(logsink.Info) -} - -// flush flushes all logs of severity threshold or greater. -func (s *fileSink) flush(threshold logsink.Severity) error { - s.mu.Lock() - defer s.mu.Unlock() - - var firstErr error - updateErr := func(err error) { - if err != nil && firstErr == nil { - firstErr = err - } - } - - // Flush from fatal down, in case there's trouble flushing. - for sev := logsink.Fatal; sev >= threshold; sev-- { - file := s.file[sev] - if file != nil { - updateErr(file.Flush()) - updateErr(file.Sync()) - } - } - - return firstErr -} - -// Names returns the names of the log files holding the FATAL, ERROR, -// WARNING, or INFO logs. Returns ErrNoLog if the log for the given -// level doesn't exist (e.g. because no messages of that level have been -// written). This may return multiple names if the log type requested -// has rolled over. -func Names(s string) ([]string, error) { - severity, err := logsink.ParseSeverity(s) - if err != nil { - return nil, err - } - - sinks.file.mu.Lock() - defer sinks.file.mu.Unlock() - f := sinks.file.file[severity] - if f == nil { - return nil, ErrNoLog - } - - return f.filenames(), nil -} diff --git a/vendor/github.com/golang/glog/glog_flags.go b/vendor/github.com/golang/glog/glog_flags.go deleted file mode 100644 index 3060e54d9..000000000 --- a/vendor/github.com/golang/glog/glog_flags.go +++ /dev/null @@ -1,395 +0,0 @@ -// Go support for leveled logs, analogous to https://github.com/google/glog. -// -// Copyright 2023 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package glog - -import ( - "bytes" - "errors" - "flag" - "fmt" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - - "github.com/golang/glog/internal/logsink" -) - -// modulePat contains a filter for the -vmodule flag. -// It holds a verbosity level and a file pattern to match. -type modulePat struct { - pattern string - literal bool // The pattern is a literal string - full bool // The pattern wants to match the full path - level Level -} - -// match reports whether the file matches the pattern. It uses a string -// comparison if the pattern contains no metacharacters. -func (m *modulePat) match(full, file string) bool { - if m.literal { - if m.full { - return full == m.pattern - } - return file == m.pattern - } - if m.full { - match, _ := filepath.Match(m.pattern, full) - return match - } - match, _ := filepath.Match(m.pattern, file) - return match -} - -// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters -// that require filepath.Match to be called to match the pattern. -func isLiteral(pattern string) bool { - return !strings.ContainsAny(pattern, `\*?[]`) -} - -// isFull reports whether the pattern matches the full file path, that is, -// whether it contains /. -func isFull(pattern string) bool { - return strings.ContainsRune(pattern, '/') -} - -// verboseFlags represents the setting of the -v and -vmodule flags. -type verboseFlags struct { - // moduleLevelCache is a sync.Map storing the -vmodule Level for each V() - // call site, identified by PC. If there is no matching -vmodule filter, - // the cached value is exactly v. moduleLevelCache is replaced with a new - // Map whenever the -vmodule or -v flag changes state. - moduleLevelCache atomic.Value - - // mu guards all fields below. - mu sync.Mutex - - // v stores the value of the -v flag. It may be read safely using - // sync.LoadInt32, but is only modified under mu. - v Level - - // module stores the parsed -vmodule flag. - module []modulePat - - // moduleLength caches len(module). If greater than zero, it - // means vmodule is enabled. It may be read safely using sync.LoadInt32, but - // is only modified under mu. - moduleLength int32 -} - -// NOTE: For compatibility with the open-sourced v1 version of this -// package (github.com/golang/glog) we need to retain that flag.Level -// implements the flag.Value interface. See also go/log-vs-glog. - -// String is part of the flag.Value interface. -func (l *Level) String() string { - return strconv.FormatInt(int64(l.Get().(Level)), 10) -} - -// Get is part of the flag.Value interface. -func (l *Level) Get() any { - if l == &vflags.v { - // l is the value registered for the -v flag. - return Level(atomic.LoadInt32((*int32)(l))) - } - return *l -} - -// Set is part of the flag.Value interface. -func (l *Level) Set(value string) error { - v, err := strconv.Atoi(value) - if err != nil { - return err - } - if l == &vflags.v { - // l is the value registered for the -v flag. - vflags.mu.Lock() - defer vflags.mu.Unlock() - vflags.moduleLevelCache.Store(&sync.Map{}) - atomic.StoreInt32((*int32)(l), int32(v)) - return nil - } - *l = Level(v) - return nil -} - -// vModuleFlag is the flag.Value for the --vmodule flag. -type vModuleFlag struct{ *verboseFlags } - -func (f vModuleFlag) String() string { - f.mu.Lock() - defer f.mu.Unlock() - - var b bytes.Buffer - for i, f := range f.module { - if i > 0 { - b.WriteRune(',') - } - fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) - } - return b.String() -} - -// Get returns nil for this flag type since the struct is not exported. -func (f vModuleFlag) Get() any { return nil } - -var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") - -// Syntax: -vmodule=recordio=2,foo/bar/baz=1,gfs*=3 -func (f vModuleFlag) Set(value string) error { - var filter []modulePat - for _, pat := range strings.Split(value, ",") { - if len(pat) == 0 { - // Empty strings such as from a trailing comma can be ignored. - continue - } - patLev := strings.Split(pat, "=") - if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { - return errVmoduleSyntax - } - pattern := patLev[0] - v, err := strconv.Atoi(patLev[1]) - if err != nil { - return errors.New("syntax error: expect comma-separated list of filename=N") - } - // TODO: check syntax of filter? - filter = append(filter, modulePat{pattern, isLiteral(pattern), isFull(pattern), Level(v)}) - } - - f.mu.Lock() - defer f.mu.Unlock() - f.module = filter - atomic.StoreInt32((*int32)(&f.moduleLength), int32(len(f.module))) - f.moduleLevelCache.Store(&sync.Map{}) - return nil -} - -func (f *verboseFlags) levelForPC(pc uintptr) Level { - if level, ok := f.moduleLevelCache.Load().(*sync.Map).Load(pc); ok { - return level.(Level) - } - - f.mu.Lock() - defer f.mu.Unlock() - level := Level(f.v) - fn := runtime.FuncForPC(pc) - file, _ := fn.FileLine(pc) - // The file is something like /a/b/c/d.go. We want just the d for - // regular matches, /a/b/c/d for full matches. - if strings.HasSuffix(file, ".go") { - file = file[:len(file)-3] - } - full := file - if slash := strings.LastIndex(file, "/"); slash >= 0 { - file = file[slash+1:] - } - for _, filter := range f.module { - if filter.match(full, file) { - level = filter.level - break // Use the first matching level. - } - } - f.moduleLevelCache.Load().(*sync.Map).Store(pc, level) - return level -} - -func (f *verboseFlags) enabled(callerDepth int, level Level) bool { - if atomic.LoadInt32(&f.moduleLength) == 0 { - // No vmodule values specified, so compare against v level. - return Level(atomic.LoadInt32((*int32)(&f.v))) >= level - } - - pcs := [1]uintptr{} - if runtime.Callers(callerDepth+2, pcs[:]) < 1 { - return false - } - frame, _ := runtime.CallersFrames(pcs[:]).Next() - return f.levelForPC(frame.Entry) >= level -} - -// traceLocation represents an entry in the -log_backtrace_at flag. -type traceLocation struct { - file string - line int -} - -var errTraceSyntax = errors.New("syntax error: expect file.go:234") - -func parseTraceLocation(value string) (traceLocation, error) { - fields := strings.Split(value, ":") - if len(fields) != 2 { - return traceLocation{}, errTraceSyntax - } - file, lineStr := fields[0], fields[1] - if !strings.Contains(file, ".") { - return traceLocation{}, errTraceSyntax - } - line, err := strconv.Atoi(lineStr) - if err != nil { - return traceLocation{}, errTraceSyntax - } - if line < 0 { - return traceLocation{}, errors.New("negative value for line") - } - return traceLocation{file, line}, nil -} - -// match reports whether the specified file and line matches the trace location. -// The argument file name is the full path, not the basename specified in the flag. -func (t traceLocation) match(file string, line int) bool { - if t.line != line { - return false - } - if i := strings.LastIndex(file, "/"); i >= 0 { - file = file[i+1:] - } - return t.file == file -} - -func (t traceLocation) String() string { - return fmt.Sprintf("%s:%d", t.file, t.line) -} - -// traceLocations represents the -log_backtrace_at flag. -// Syntax: -log_backtrace_at=recordio.go:234,sstable.go:456 -// Note that unlike vmodule the file extension is included here. -type traceLocations struct { - mu sync.Mutex - locsLen int32 // Safe for atomic read without mu. - locs []traceLocation -} - -func (t *traceLocations) String() string { - t.mu.Lock() - defer t.mu.Unlock() - - var buf bytes.Buffer - for i, tl := range t.locs { - if i > 0 { - buf.WriteString(",") - } - buf.WriteString(tl.String()) - } - return buf.String() -} - -// Get always returns nil for this flag type since the struct is not exported -func (t *traceLocations) Get() any { return nil } - -func (t *traceLocations) Set(value string) error { - var locs []traceLocation - for _, s := range strings.Split(value, ",") { - if s == "" { - continue - } - loc, err := parseTraceLocation(s) - if err != nil { - return err - } - locs = append(locs, loc) - } - - t.mu.Lock() - defer t.mu.Unlock() - atomic.StoreInt32(&t.locsLen, int32(len(locs))) - t.locs = locs - return nil -} - -func (t *traceLocations) match(file string, line int) bool { - if atomic.LoadInt32(&t.locsLen) == 0 { - return false - } - - t.mu.Lock() - defer t.mu.Unlock() - for _, tl := range t.locs { - if tl.match(file, line) { - return true - } - } - return false -} - -// severityFlag is an atomic flag.Value implementation for logsink.Severity. -type severityFlag int32 - -func (s *severityFlag) get() logsink.Severity { - return logsink.Severity(atomic.LoadInt32((*int32)(s))) -} -func (s *severityFlag) String() string { return strconv.FormatInt(int64(*s), 10) } -func (s *severityFlag) Get() any { return s.get() } -func (s *severityFlag) Set(value string) error { - threshold, err := logsink.ParseSeverity(value) - if err != nil { - // Not a severity name. Try a raw number. - v, err := strconv.Atoi(value) - if err != nil { - return err - } - threshold = logsink.Severity(v) - if threshold < logsink.Info || threshold > logsink.Fatal { - return fmt.Errorf("Severity %d out of range (min %d, max %d).", v, logsink.Info, logsink.Fatal) - } - } - atomic.StoreInt32((*int32)(s), int32(threshold)) - return nil -} - -var ( - vflags verboseFlags // The -v and -vmodule flags. - - logBacktraceAt traceLocations // The -log_backtrace_at flag. - - // Boolean flags. Not handled atomically because the flag.Value interface - // does not let us avoid the =true, and that shorthand is necessary for - // compatibility. TODO: does this matter enough to fix? Seems unlikely. - toStderr bool // The -logtostderr flag. - alsoToStderr bool // The -alsologtostderr flag. - - stderrThreshold severityFlag // The -stderrthreshold flag. -) - -// verboseEnabled returns whether the caller at the given depth should emit -// verbose logs at the given level, with depth 0 identifying the caller of -// verboseEnabled. -func verboseEnabled(callerDepth int, level Level) bool { - return vflags.enabled(callerDepth+1, level) -} - -// backtraceAt returns whether the logging call at the given function and line -// should also emit a backtrace of the current call stack. -func backtraceAt(file string, line int) bool { - return logBacktraceAt.match(file, line) -} - -func init() { - vflags.moduleLevelCache.Store(&sync.Map{}) - - flag.Var(&vflags.v, "v", "log level for V logs") - flag.Var(vModuleFlag{&vflags}, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") - - flag.Var(&logBacktraceAt, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") - - stderrThreshold = severityFlag(logsink.Error) - - flag.BoolVar(&toStderr, "logtostderr", false, "log to standard error instead of files") - flag.BoolVar(&alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") - flag.Var(&stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") -} diff --git a/vendor/github.com/golang/glog/internal/logsink/logsink.go b/vendor/github.com/golang/glog/internal/logsink/logsink.go deleted file mode 100644 index 53758e1c9..000000000 --- a/vendor/github.com/golang/glog/internal/logsink/logsink.go +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2023 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logsink - -import ( - "bytes" - "fmt" - "strconv" - "strings" - "sync" - "time" - - "github.com/golang/glog/internal/stackdump" -) - -// MaxLogMessageLen is the limit on length of a formatted log message, including -// the standard line prefix and trailing newline. -// -// Chosen to match C++ glog. -const MaxLogMessageLen = 15000 - -// A Severity is a severity at which a message can be logged. -type Severity int8 - -// These constants identify the log levels in order of increasing severity. -// A message written to a high-severity log file is also written to each -// lower-severity log file. -const ( - Info Severity = iota - Warning - Error - - // Fatal contains logs written immediately before the process terminates. - // - // Sink implementations should not terminate the process themselves: the log - // package will perform any necessary cleanup and terminate the process as - // appropriate. - Fatal -) - -func (s Severity) String() string { - switch s { - case Info: - return "INFO" - case Warning: - return "WARNING" - case Error: - return "ERROR" - case Fatal: - return "FATAL" - } - return fmt.Sprintf("%T(%d)", s, s) -} - -// ParseSeverity returns the case-insensitive Severity value for the given string. -func ParseSeverity(name string) (Severity, error) { - name = strings.ToUpper(name) - for s := Info; s <= Fatal; s++ { - if s.String() == name { - return s, nil - } - } - return -1, fmt.Errorf("logsink: invalid severity %q", name) -} - -// Meta is metadata about a logging call. -type Meta struct { - // Time is the time at which the log call was made. - Time time.Time - - // File is the source file from which the log entry originates. - File string - // Line is the line offset within the source file. - Line int - // Depth is the number of stack frames between the logsink and the log call. - Depth int - - Severity Severity - - // Verbose indicates whether the call was made via "log.V". Log entries below - // the current verbosity threshold are not sent to the sink. - Verbose bool - - // Thread ID. This can be populated with a thread ID from another source, - // such as a system we are importing logs from. In the normal case, this - // will be set to the process ID (PID), since Go doesn't have threads. - Thread int64 - - // Stack trace starting in the logging function. May be nil. - // A logsink should implement the StackWanter interface to request this. - // - // Even if WantStack returns false, this field may be set (e.g. if another - // sink wants a stack trace). - Stack *stackdump.Stack -} - -// Structured is a logging destination that accepts structured data as input. -type Structured interface { - // Printf formats according to a fmt.Printf format specifier and writes a log - // entry. The precise result of formatting depends on the sink, but should - // aim for consistency with fmt.Printf. - // - // Printf returns the number of bytes occupied by the log entry, which - // may not be equal to the total number of bytes written. - // - // Printf returns any error encountered *if* it is severe enough that the log - // package should terminate the process. - // - // The sink must not modify the *Meta parameter, nor reference it after - // Printf has returned: it may be reused in subsequent calls. - Printf(meta *Meta, format string, a ...any) (n int, err error) -} - -// StackWanter can be implemented by a logsink.Structured to indicate that it -// wants a stack trace to accompany at least some of the log messages it receives. -type StackWanter interface { - // WantStack returns true if the sink requires a stack trace for a log message - // with this metadata. - // - // NOTE: Returning true implies that meta.Stack will be non-nil. Returning - // false does NOT imply that meta.Stack will be nil. - WantStack(meta *Meta) bool -} - -// Text is a logging destination that accepts pre-formatted log lines (instead of -// structured data). -type Text interface { - // Enabled returns whether this sink should output messages for the given - // Meta. If the sink returns false for a given Meta, the Printf function will - // not call Emit on it for the corresponding log message. - Enabled(*Meta) bool - - // Emit writes a pre-formatted text log entry (including any applicable - // header) to the log. It returns the number of bytes occupied by the entry - // (which may differ from the length of the passed-in slice). - // - // Emit returns any error encountered *if* it is severe enough that the log - // package should terminate the process. - // - // The sink must not modify the *Meta parameter, nor reference it after - // Printf has returned: it may be reused in subsequent calls. - // - // NOTE: When developing a text sink, keep in mind the surface in which the - // logs will be displayed, and whether it's important that the sink be - // resistent to tampering in the style of b/211428300. Standard text sinks - // (like `stderrSink`) do not protect against this (e.g. by escaping - // characters) because the cases where they would show user-influenced bytes - // are vanishingly small. - Emit(*Meta, []byte) (n int, err error) -} - -// bufs is a pool of *bytes.Buffer used in formatting log entries. -var bufs sync.Pool // Pool of *bytes.Buffer. - -// textPrintf formats a text log entry and emits it to all specified Text sinks. -// -// The returned n is the maximum across all Emit calls. -// The returned err is the first non-nil error encountered. -// Sinks that are disabled by configuration should return (0, nil). -func textPrintf(m *Meta, textSinks []Text, format string, args ...any) (n int, err error) { - // We expect at most file, stderr, and perhaps syslog. If there are more, - // we'll end up allocating - no big deal. - const maxExpectedTextSinks = 3 - var noAllocSinks [maxExpectedTextSinks]Text - - sinks := noAllocSinks[:0] - for _, s := range textSinks { - if s.Enabled(m) { - sinks = append(sinks, s) - } - } - if len(sinks) == 0 && m.Severity != Fatal { - return 0, nil // No TextSinks specified; don't bother formatting. - } - - bufi := bufs.Get() - var buf *bytes.Buffer - if bufi == nil { - buf = bytes.NewBuffer(nil) - bufi = buf - } else { - buf = bufi.(*bytes.Buffer) - buf.Reset() - } - - // Lmmdd hh:mm:ss.uuuuuu PID/GID file:line] - // - // The "PID" entry arguably ought to be TID for consistency with other - // environments, but TID is not meaningful in a Go program due to the - // multiplexing of goroutines across threads. - // - // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. - // It's worth about 3X. Fprintf is hard. - const severityChar = "IWEF" - buf.WriteByte(severityChar[m.Severity]) - - _, month, day := m.Time.Date() - hour, minute, second := m.Time.Clock() - twoDigits(buf, int(month)) - twoDigits(buf, day) - buf.WriteByte(' ') - twoDigits(buf, hour) - buf.WriteByte(':') - twoDigits(buf, minute) - buf.WriteByte(':') - twoDigits(buf, second) - buf.WriteByte('.') - nDigits(buf, 6, uint64(m.Time.Nanosecond()/1000), '0') - buf.WriteByte(' ') - - nDigits(buf, 7, uint64(m.Thread), ' ') - buf.WriteByte(' ') - - { - file := m.File - if i := strings.LastIndex(file, "/"); i >= 0 { - file = file[i+1:] - } - buf.WriteString(file) - } - - buf.WriteByte(':') - { - var tmp [19]byte - buf.Write(strconv.AppendInt(tmp[:0], int64(m.Line), 10)) - } - buf.WriteString("] ") - - msgStart := buf.Len() - fmt.Fprintf(buf, format, args...) - if buf.Len() > MaxLogMessageLen-1 { - buf.Truncate(MaxLogMessageLen - 1) - } - msgEnd := buf.Len() - if b := buf.Bytes(); b[len(b)-1] != '\n' { - buf.WriteByte('\n') - } - - for _, s := range sinks { - sn, sErr := s.Emit(m, buf.Bytes()) - if sn > n { - n = sn - } - if sErr != nil && err == nil { - err = sErr - } - } - - if m.Severity == Fatal { - savedM := *m - fatalMessageStore(savedEntry{ - meta: &savedM, - msg: buf.Bytes()[msgStart:msgEnd], - }) - } else { - bufs.Put(bufi) - } - return n, err -} - -const digits = "0123456789" - -// twoDigits formats a zero-prefixed two-digit integer to buf. -func twoDigits(buf *bytes.Buffer, d int) { - buf.WriteByte(digits[(d/10)%10]) - buf.WriteByte(digits[d%10]) -} - -// nDigits formats an n-digit integer to buf, padding with pad on the left. It -// assumes d != 0. -func nDigits(buf *bytes.Buffer, n int, d uint64, pad byte) { - var tmp [20]byte - - cutoff := len(tmp) - n - j := len(tmp) - 1 - for ; d > 0; j-- { - tmp[j] = digits[d%10] - d /= 10 - } - for ; j >= cutoff; j-- { - tmp[j] = pad - } - j++ - buf.Write(tmp[j:]) -} - -// Printf writes a log entry to all registered TextSinks in this package, then -// to all registered StructuredSinks. -// -// The returned n is the maximum across all Emit and Printf calls. -// The returned err is the first non-nil error encountered. -// Sinks that are disabled by configuration should return (0, nil). -func Printf(m *Meta, format string, args ...any) (n int, err error) { - m.Depth++ - n, err = textPrintf(m, TextSinks, format, args...) - - for _, sink := range StructuredSinks { - // TODO: Support TextSinks that implement StackWanter? - if sw, ok := sink.(StackWanter); ok && sw.WantStack(m) { - if m.Stack == nil { - // First, try to find a stacktrace in args, otherwise generate one. - for _, arg := range args { - if stack, ok := arg.(stackdump.Stack); ok { - m.Stack = &stack - break - } - } - if m.Stack == nil { - stack := stackdump.Caller( /* skipDepth = */ m.Depth) - m.Stack = &stack - } - } - } - sn, sErr := sink.Printf(m, format, args...) - if sn > n { - n = sn - } - if sErr != nil && err == nil { - err = sErr - } - } - return n, err -} - -// The sets of sinks to which logs should be written. -// -// These must only be modified during package init, and are read-only thereafter. -var ( - // StructuredSinks is the set of Structured sink instances to which logs - // should be written. - StructuredSinks []Structured - - // TextSinks is the set of Text sink instances to which logs should be - // written. - // - // These are registered separately from Structured sink implementations to - // avoid the need to repeat the work of formatting a message for each Text - // sink that writes it. The package-level Printf function writes to both sets - // independenty, so a given log destination should only register a Structured - // *or* a Text sink (not both). - TextSinks []Text -) - -type savedEntry struct { - meta *Meta - msg []byte -} - -// StructuredTextWrapper is a Structured sink which forwards logs to a set of Text sinks. -// -// The purpose of this sink is to allow applications to intercept logging calls before they are -// serialized and sent to Text sinks. For example, if one needs to redact PII from logging -// arguments before they reach STDERR, one solution would be to do the redacting in a Structured -// sink that forwards logs to a StructuredTextWrapper instance, and make STDERR a child of that -// StructuredTextWrapper instance. This is how one could set this up in their application: -// -// func init() { -// -// wrapper := logsink.StructuredTextWrapper{TextSinks: logsink.TextSinks} -// // sanitizersink will intercept logs and remove PII -// sanitizer := sanitizersink{Sink: &wrapper} -// logsink.StructuredSinks = append(logsink.StructuredSinks, &sanitizer) -// logsink.TextSinks = nil -// -// } -type StructuredTextWrapper struct { - // TextSinks is the set of Text sinks that should receive logs from this - // StructuredTextWrapper instance. - TextSinks []Text -} - -// Printf forwards logs to all Text sinks registered in the StructuredTextWrapper. -func (w *StructuredTextWrapper) Printf(meta *Meta, format string, args ...any) (n int, err error) { - return textPrintf(meta, w.TextSinks, format, args...) -} diff --git a/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go b/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go deleted file mode 100644 index 3dc269abc..000000000 --- a/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go +++ /dev/null @@ -1,35 +0,0 @@ -package logsink - -import ( - "sync/atomic" - "unsafe" -) - -func fatalMessageStore(e savedEntry) { - // Only put a new one in if we haven't assigned before. - atomic.CompareAndSwapPointer(&fatalMessage, nil, unsafe.Pointer(&e)) -} - -var fatalMessage unsafe.Pointer // savedEntry stored with CompareAndSwapPointer - -// FatalMessage returns the Meta and message contents of the first message -// logged with Fatal severity, or false if none has occurred. -func FatalMessage() (*Meta, []byte, bool) { - e := (*savedEntry)(atomic.LoadPointer(&fatalMessage)) - if e == nil { - return nil, nil, false - } - return e.meta, e.msg, true -} - -// DoNotUseRacyFatalMessage is FatalMessage, but worse. -// -//go:norace -//go:nosplit -func DoNotUseRacyFatalMessage() (*Meta, []byte, bool) { - e := (*savedEntry)(fatalMessage) - if e == nil { - return nil, nil, false - } - return e.meta, e.msg, true -} diff --git a/vendor/github.com/golang/glog/internal/stackdump/stackdump.go b/vendor/github.com/golang/glog/internal/stackdump/stackdump.go deleted file mode 100644 index 3427c9d6b..000000000 --- a/vendor/github.com/golang/glog/internal/stackdump/stackdump.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2023 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package stackdump provides wrappers for runtime.Stack and runtime.Callers -// with uniform support for skipping caller frames. -// -// âš  Unlike the functions in the runtime package, these may allocate a -// non-trivial quantity of memory: use them with care. âš  -package stackdump - -import ( - "bytes" - "runtime" -) - -// runtimeStackSelfFrames is 1 if runtime.Stack includes the call to -// runtime.Stack itself or 0 if it does not. -// -// As of 2016-04-27, the gccgo compiler includes runtime.Stack but the gc -// compiler does not. -var runtimeStackSelfFrames = func() int { - for n := 1 << 10; n < 1<<20; n *= 2 { - buf := make([]byte, n) - n := runtime.Stack(buf, false) - if bytes.Contains(buf[:n], []byte("runtime.Stack")) { - return 1 - } else if n < len(buf) || bytes.Count(buf, []byte("\n")) >= 3 { - return 0 - } - } - return 0 -}() - -// Stack is a stack dump for a single goroutine. -type Stack struct { - // Text is a representation of the stack dump in a human-readable format. - Text []byte - - // PC is a representation of the stack dump using raw program counter values. - PC []uintptr -} - -func (s Stack) String() string { return string(s.Text) } - -// Caller returns the Stack dump for the calling goroutine, starting skipDepth -// frames before the caller of Caller. (Caller(0) provides a dump starting at -// the caller of this function.) -func Caller(skipDepth int) Stack { - return Stack{ - Text: CallerText(skipDepth + 1), - PC: CallerPC(skipDepth + 1), - } -} - -// CallerText returns a textual dump of the stack starting skipDepth frames before -// the caller. (CallerText(0) provides a dump starting at the caller of this -// function.) -func CallerText(skipDepth int) []byte { - for n := 1 << 10; ; n *= 2 { - buf := make([]byte, n) - n := runtime.Stack(buf, false) - if n < len(buf) { - return pruneFrames(skipDepth+1+runtimeStackSelfFrames, buf[:n]) - } - } -} - -// CallerPC returns a dump of the program counters of the stack starting -// skipDepth frames before the caller. (CallerPC(0) provides a dump starting at -// the caller of this function.) -func CallerPC(skipDepth int) []uintptr { - for n := 1 << 8; ; n *= 2 { - buf := make([]uintptr, n) - n := runtime.Callers(skipDepth+2, buf) - if n < len(buf) { - return buf[:n] - } - } -} - -// pruneFrames removes the topmost skipDepth frames of the first goroutine in a -// textual stack dump. It overwrites the passed-in slice. -// -// If there are fewer than skipDepth frames in the first goroutine's stack, -// pruneFrames prunes it to an empty stack and leaves the remaining contents -// intact. -func pruneFrames(skipDepth int, stack []byte) []byte { - headerLen := 0 - for i, c := range stack { - if c == '\n' { - headerLen = i + 1 - break - } - } - if headerLen == 0 { - return stack // No header line - not a well-formed stack trace. - } - - skipLen := headerLen - skipNewlines := skipDepth * 2 - for ; skipLen < len(stack) && skipNewlines > 0; skipLen++ { - c := stack[skipLen] - if c != '\n' { - continue - } - skipNewlines-- - skipLen++ - if skipNewlines == 0 || skipLen == len(stack) || stack[skipLen] == '\n' { - break - } - } - - pruned := stack[skipLen-headerLen:] - copy(pruned, stack[:headerLen]) - return pruned -} diff --git a/vendor/github.com/golang/protobuf/jsonpb/decode.go b/vendor/github.com/golang/protobuf/jsonpb/decode.go deleted file mode 100644 index 6c16c255f..000000000 --- a/vendor/github.com/golang/protobuf/jsonpb/decode.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package jsonpb - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "math" - "reflect" - "strconv" - "strings" - "time" - - "github.com/golang/protobuf/proto" - "google.golang.org/protobuf/encoding/protojson" - protoV2 "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" -) - -const wrapJSONUnmarshalV2 = false - -// UnmarshalNext unmarshals the next JSON object from d into m. -func UnmarshalNext(d *json.Decoder, m proto.Message) error { - return new(Unmarshaler).UnmarshalNext(d, m) -} - -// Unmarshal unmarshals a JSON object from r into m. -func Unmarshal(r io.Reader, m proto.Message) error { - return new(Unmarshaler).Unmarshal(r, m) -} - -// UnmarshalString unmarshals a JSON object from s into m. -func UnmarshalString(s string, m proto.Message) error { - return new(Unmarshaler).Unmarshal(strings.NewReader(s), m) -} - -// Unmarshaler is a configurable object for converting from a JSON -// representation to a protocol buffer object. -type Unmarshaler struct { - // AllowUnknownFields specifies whether to allow messages to contain - // unknown JSON fields, as opposed to failing to unmarshal. - AllowUnknownFields bool - - // AnyResolver is used to resolve the google.protobuf.Any well-known type. - // If unset, the global registry is used by default. - AnyResolver AnyResolver -} - -// JSONPBUnmarshaler is implemented by protobuf messages that customize the way -// they are unmarshaled from JSON. Messages that implement this should also -// implement JSONPBMarshaler so that the custom format can be produced. -// -// The JSON unmarshaling must follow the JSON to proto specification: -// https://developers.google.com/protocol-buffers/docs/proto3#json -// -// Deprecated: Custom types should implement protobuf reflection instead. -type JSONPBUnmarshaler interface { - UnmarshalJSONPB(*Unmarshaler, []byte) error -} - -// Unmarshal unmarshals a JSON object from r into m. -func (u *Unmarshaler) Unmarshal(r io.Reader, m proto.Message) error { - return u.UnmarshalNext(json.NewDecoder(r), m) -} - -// UnmarshalNext unmarshals the next JSON object from d into m. -func (u *Unmarshaler) UnmarshalNext(d *json.Decoder, m proto.Message) error { - if m == nil { - return errors.New("invalid nil message") - } - - // Parse the next JSON object from the stream. - raw := json.RawMessage{} - if err := d.Decode(&raw); err != nil { - return err - } - - // Check for custom unmarshalers first since they may not properly - // implement protobuf reflection that the logic below relies on. - if jsu, ok := m.(JSONPBUnmarshaler); ok { - return jsu.UnmarshalJSONPB(u, raw) - } - - mr := proto.MessageReflect(m) - - // NOTE: For historical reasons, a top-level null is treated as a noop. - // This is incorrect, but kept for compatibility. - if string(raw) == "null" && mr.Descriptor().FullName() != "google.protobuf.Value" { - return nil - } - - if wrapJSONUnmarshalV2 { - // NOTE: If input message is non-empty, we need to preserve merge semantics - // of the old jsonpb implementation. These semantics are not supported by - // the protobuf JSON specification. - isEmpty := true - mr.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool { - isEmpty = false // at least one iteration implies non-empty - return false - }) - if !isEmpty { - // Perform unmarshaling into a newly allocated, empty message. - mr = mr.New() - - // Use a defer to copy all unmarshaled fields into the original message. - dst := proto.MessageReflect(m) - defer mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - dst.Set(fd, v) - return true - }) - } - - // Unmarshal using the v2 JSON unmarshaler. - opts := protojson.UnmarshalOptions{ - DiscardUnknown: u.AllowUnknownFields, - } - if u.AnyResolver != nil { - opts.Resolver = anyResolver{u.AnyResolver} - } - return opts.Unmarshal(raw, mr.Interface()) - } else { - if err := u.unmarshalMessage(mr, raw); err != nil { - return err - } - return protoV2.CheckInitialized(mr.Interface()) - } -} - -func (u *Unmarshaler) unmarshalMessage(m protoreflect.Message, in []byte) error { - md := m.Descriptor() - fds := md.Fields() - - if jsu, ok := proto.MessageV1(m.Interface()).(JSONPBUnmarshaler); ok { - return jsu.UnmarshalJSONPB(u, in) - } - - if string(in) == "null" && md.FullName() != "google.protobuf.Value" { - return nil - } - - switch wellKnownType(md.FullName()) { - case "Any": - var jsonObject map[string]json.RawMessage - if err := json.Unmarshal(in, &jsonObject); err != nil { - return err - } - - rawTypeURL, ok := jsonObject["@type"] - if !ok { - return errors.New("Any JSON doesn't have '@type'") - } - typeURL, err := unquoteString(string(rawTypeURL)) - if err != nil { - return fmt.Errorf("can't unmarshal Any's '@type': %q", rawTypeURL) - } - m.Set(fds.ByNumber(1), protoreflect.ValueOfString(typeURL)) - - var m2 protoreflect.Message - if u.AnyResolver != nil { - mi, err := u.AnyResolver.Resolve(typeURL) - if err != nil { - return err - } - m2 = proto.MessageReflect(mi) - } else { - mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL) - if err != nil { - if err == protoregistry.NotFound { - return fmt.Errorf("could not resolve Any message type: %v", typeURL) - } - return err - } - m2 = mt.New() - } - - if wellKnownType(m2.Descriptor().FullName()) != "" { - rawValue, ok := jsonObject["value"] - if !ok { - return errors.New("Any JSON doesn't have 'value'") - } - if err := u.unmarshalMessage(m2, rawValue); err != nil { - return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) - } - } else { - delete(jsonObject, "@type") - rawJSON, err := json.Marshal(jsonObject) - if err != nil { - return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) - } - if err = u.unmarshalMessage(m2, rawJSON); err != nil { - return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) - } - } - - rawWire, err := protoV2.Marshal(m2.Interface()) - if err != nil { - return fmt.Errorf("can't marshal proto %v into Any.Value: %v", typeURL, err) - } - m.Set(fds.ByNumber(2), protoreflect.ValueOfBytes(rawWire)) - return nil - case "BoolValue", "BytesValue", "StringValue", - "Int32Value", "UInt32Value", "FloatValue", - "Int64Value", "UInt64Value", "DoubleValue": - fd := fds.ByNumber(1) - v, err := u.unmarshalValue(m.NewField(fd), in, fd) - if err != nil { - return err - } - m.Set(fd, v) - return nil - case "Duration": - v, err := unquoteString(string(in)) - if err != nil { - return err - } - d, err := time.ParseDuration(v) - if err != nil { - return fmt.Errorf("bad Duration: %v", err) - } - - sec := d.Nanoseconds() / 1e9 - nsec := d.Nanoseconds() % 1e9 - m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) - m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) - return nil - case "Timestamp": - v, err := unquoteString(string(in)) - if err != nil { - return err - } - t, err := time.Parse(time.RFC3339Nano, v) - if err != nil { - return fmt.Errorf("bad Timestamp: %v", err) - } - - sec := t.Unix() - nsec := t.Nanosecond() - m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) - m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) - return nil - case "Value": - switch { - case string(in) == "null": - m.Set(fds.ByNumber(1), protoreflect.ValueOfEnum(0)) - case string(in) == "true": - m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(true)) - case string(in) == "false": - m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(false)) - case hasPrefixAndSuffix('"', in, '"'): - s, err := unquoteString(string(in)) - if err != nil { - return fmt.Errorf("unrecognized type for Value %q", in) - } - m.Set(fds.ByNumber(3), protoreflect.ValueOfString(s)) - case hasPrefixAndSuffix('[', in, ']'): - v := m.Mutable(fds.ByNumber(6)) - return u.unmarshalMessage(v.Message(), in) - case hasPrefixAndSuffix('{', in, '}'): - v := m.Mutable(fds.ByNumber(5)) - return u.unmarshalMessage(v.Message(), in) - default: - f, err := strconv.ParseFloat(string(in), 0) - if err != nil { - return fmt.Errorf("unrecognized type for Value %q", in) - } - m.Set(fds.ByNumber(2), protoreflect.ValueOfFloat64(f)) - } - return nil - case "ListValue": - var jsonArray []json.RawMessage - if err := json.Unmarshal(in, &jsonArray); err != nil { - return fmt.Errorf("bad ListValue: %v", err) - } - - lv := m.Mutable(fds.ByNumber(1)).List() - for _, raw := range jsonArray { - ve := lv.NewElement() - if err := u.unmarshalMessage(ve.Message(), raw); err != nil { - return err - } - lv.Append(ve) - } - return nil - case "Struct": - var jsonObject map[string]json.RawMessage - if err := json.Unmarshal(in, &jsonObject); err != nil { - return fmt.Errorf("bad StructValue: %v", err) - } - - mv := m.Mutable(fds.ByNumber(1)).Map() - for key, raw := range jsonObject { - kv := protoreflect.ValueOf(key).MapKey() - vv := mv.NewValue() - if err := u.unmarshalMessage(vv.Message(), raw); err != nil { - return fmt.Errorf("bad value in StructValue for key %q: %v", key, err) - } - mv.Set(kv, vv) - } - return nil - } - - var jsonObject map[string]json.RawMessage - if err := json.Unmarshal(in, &jsonObject); err != nil { - return err - } - - // Handle known fields. - for i := 0; i < fds.Len(); i++ { - fd := fds.Get(i) - if fd.IsWeak() && fd.Message().IsPlaceholder() { - continue // weak reference is not linked in - } - - // Search for any raw JSON value associated with this field. - var raw json.RawMessage - name := string(fd.Name()) - if fd.Kind() == protoreflect.GroupKind { - name = string(fd.Message().Name()) - } - if v, ok := jsonObject[name]; ok { - delete(jsonObject, name) - raw = v - } - name = string(fd.JSONName()) - if v, ok := jsonObject[name]; ok { - delete(jsonObject, name) - raw = v - } - - field := m.NewField(fd) - // Unmarshal the field value. - if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { - continue - } - v, err := u.unmarshalValue(field, raw, fd) - if err != nil { - return err - } - m.Set(fd, v) - } - - // Handle extension fields. - for name, raw := range jsonObject { - if !strings.HasPrefix(name, "[") || !strings.HasSuffix(name, "]") { - continue - } - - // Resolve the extension field by name. - xname := protoreflect.FullName(name[len("[") : len(name)-len("]")]) - xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) - if xt == nil && isMessageSet(md) { - xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) - } - if xt == nil { - continue - } - delete(jsonObject, name) - fd := xt.TypeDescriptor() - if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { - return fmt.Errorf("extension field %q does not extend message %q", xname, m.Descriptor().FullName()) - } - - field := m.NewField(fd) - // Unmarshal the field value. - if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { - continue - } - v, err := u.unmarshalValue(field, raw, fd) - if err != nil { - return err - } - m.Set(fd, v) - } - - if !u.AllowUnknownFields && len(jsonObject) > 0 { - for name := range jsonObject { - return fmt.Errorf("unknown field %q in %v", name, md.FullName()) - } - } - return nil -} - -func isSingularWellKnownValue(fd protoreflect.FieldDescriptor) bool { - if fd.Cardinality() == protoreflect.Repeated { - return false - } - if md := fd.Message(); md != nil { - return md.FullName() == "google.protobuf.Value" - } - if ed := fd.Enum(); ed != nil { - return ed.FullName() == "google.protobuf.NullValue" - } - return false -} - -func isSingularJSONPBUnmarshaler(v protoreflect.Value, fd protoreflect.FieldDescriptor) bool { - if fd.Message() != nil && fd.Cardinality() != protoreflect.Repeated { - _, ok := proto.MessageV1(v.Interface()).(JSONPBUnmarshaler) - return ok - } - return false -} - -func (u *Unmarshaler) unmarshalValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { - switch { - case fd.IsList(): - var jsonArray []json.RawMessage - if err := json.Unmarshal(in, &jsonArray); err != nil { - return v, err - } - lv := v.List() - for _, raw := range jsonArray { - ve, err := u.unmarshalSingularValue(lv.NewElement(), raw, fd) - if err != nil { - return v, err - } - lv.Append(ve) - } - return v, nil - case fd.IsMap(): - var jsonObject map[string]json.RawMessage - if err := json.Unmarshal(in, &jsonObject); err != nil { - return v, err - } - kfd := fd.MapKey() - vfd := fd.MapValue() - mv := v.Map() - for key, raw := range jsonObject { - var kv protoreflect.MapKey - if kfd.Kind() == protoreflect.StringKind { - kv = protoreflect.ValueOf(key).MapKey() - } else { - v, err := u.unmarshalSingularValue(kfd.Default(), []byte(key), kfd) - if err != nil { - return v, err - } - kv = v.MapKey() - } - - vv, err := u.unmarshalSingularValue(mv.NewValue(), raw, vfd) - if err != nil { - return v, err - } - mv.Set(kv, vv) - } - return v, nil - default: - return u.unmarshalSingularValue(v, in, fd) - } -} - -var nonFinite = map[string]float64{ - `"NaN"`: math.NaN(), - `"Infinity"`: math.Inf(+1), - `"-Infinity"`: math.Inf(-1), -} - -func (u *Unmarshaler) unmarshalSingularValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { - switch fd.Kind() { - case protoreflect.BoolKind: - return unmarshalValue(in, new(bool)) - case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: - return unmarshalValue(trimQuote(in), new(int32)) - case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: - return unmarshalValue(trimQuote(in), new(int64)) - case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: - return unmarshalValue(trimQuote(in), new(uint32)) - case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: - return unmarshalValue(trimQuote(in), new(uint64)) - case protoreflect.FloatKind: - if f, ok := nonFinite[string(in)]; ok { - return protoreflect.ValueOfFloat32(float32(f)), nil - } - return unmarshalValue(trimQuote(in), new(float32)) - case protoreflect.DoubleKind: - if f, ok := nonFinite[string(in)]; ok { - return protoreflect.ValueOfFloat64(float64(f)), nil - } - return unmarshalValue(trimQuote(in), new(float64)) - case protoreflect.StringKind: - return unmarshalValue(in, new(string)) - case protoreflect.BytesKind: - return unmarshalValue(in, new([]byte)) - case protoreflect.EnumKind: - if hasPrefixAndSuffix('"', in, '"') { - vd := fd.Enum().Values().ByName(protoreflect.Name(trimQuote(in))) - if vd == nil { - return v, fmt.Errorf("unknown value %q for enum %s", in, fd.Enum().FullName()) - } - return protoreflect.ValueOfEnum(vd.Number()), nil - } - return unmarshalValue(in, new(protoreflect.EnumNumber)) - case protoreflect.MessageKind, protoreflect.GroupKind: - err := u.unmarshalMessage(v.Message(), in) - return v, err - default: - panic(fmt.Sprintf("invalid kind %v", fd.Kind())) - } -} - -func unmarshalValue(in []byte, v interface{}) (protoreflect.Value, error) { - err := json.Unmarshal(in, v) - return protoreflect.ValueOf(reflect.ValueOf(v).Elem().Interface()), err -} - -func unquoteString(in string) (out string, err error) { - err = json.Unmarshal([]byte(in), &out) - return out, err -} - -func hasPrefixAndSuffix(prefix byte, in []byte, suffix byte) bool { - if len(in) >= 2 && in[0] == prefix && in[len(in)-1] == suffix { - return true - } - return false -} - -// trimQuote is like unquoteString but simply strips surrounding quotes. -// This is incorrect, but is behavior done by the legacy implementation. -func trimQuote(in []byte) []byte { - if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' { - in = in[1 : len(in)-1] - } - return in -} diff --git a/vendor/github.com/golang/protobuf/jsonpb/encode.go b/vendor/github.com/golang/protobuf/jsonpb/encode.go deleted file mode 100644 index 685c80a62..000000000 --- a/vendor/github.com/golang/protobuf/jsonpb/encode.go +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package jsonpb - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "math" - "reflect" - "sort" - "strconv" - "strings" - "time" - - "github.com/golang/protobuf/proto" - "google.golang.org/protobuf/encoding/protojson" - protoV2 "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" -) - -const wrapJSONMarshalV2 = false - -// Marshaler is a configurable object for marshaling protocol buffer messages -// to the specified JSON representation. -type Marshaler struct { - // OrigName specifies whether to use the original protobuf name for fields. - OrigName bool - - // EnumsAsInts specifies whether to render enum values as integers, - // as opposed to string values. - EnumsAsInts bool - - // EmitDefaults specifies whether to render fields with zero values. - EmitDefaults bool - - // Indent controls whether the output is compact or not. - // If empty, the output is compact JSON. Otherwise, every JSON object - // entry and JSON array value will be on its own line. - // Each line will be preceded by repeated copies of Indent, where the - // number of copies is the current indentation depth. - Indent string - - // AnyResolver is used to resolve the google.protobuf.Any well-known type. - // If unset, the global registry is used by default. - AnyResolver AnyResolver -} - -// JSONPBMarshaler is implemented by protobuf messages that customize the -// way they are marshaled to JSON. Messages that implement this should also -// implement JSONPBUnmarshaler so that the custom format can be parsed. -// -// The JSON marshaling must follow the proto to JSON specification: -// https://developers.google.com/protocol-buffers/docs/proto3#json -// -// Deprecated: Custom types should implement protobuf reflection instead. -type JSONPBMarshaler interface { - MarshalJSONPB(*Marshaler) ([]byte, error) -} - -// Marshal serializes a protobuf message as JSON into w. -func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error { - b, err := jm.marshal(m) - if len(b) > 0 { - if _, err := w.Write(b); err != nil { - return err - } - } - return err -} - -// MarshalToString serializes a protobuf message as JSON in string form. -func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) { - b, err := jm.marshal(m) - if err != nil { - return "", err - } - return string(b), nil -} - -func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) { - v := reflect.ValueOf(m) - if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { - return nil, errors.New("Marshal called with nil") - } - - // Check for custom marshalers first since they may not properly - // implement protobuf reflection that the logic below relies on. - if jsm, ok := m.(JSONPBMarshaler); ok { - return jsm.MarshalJSONPB(jm) - } - - if wrapJSONMarshalV2 { - opts := protojson.MarshalOptions{ - UseProtoNames: jm.OrigName, - UseEnumNumbers: jm.EnumsAsInts, - EmitUnpopulated: jm.EmitDefaults, - Indent: jm.Indent, - } - if jm.AnyResolver != nil { - opts.Resolver = anyResolver{jm.AnyResolver} - } - return opts.Marshal(proto.MessageReflect(m).Interface()) - } else { - // Check for unpopulated required fields first. - m2 := proto.MessageReflect(m) - if err := protoV2.CheckInitialized(m2.Interface()); err != nil { - return nil, err - } - - w := jsonWriter{Marshaler: jm} - err := w.marshalMessage(m2, "", "") - return w.buf, err - } -} - -type jsonWriter struct { - *Marshaler - buf []byte -} - -func (w *jsonWriter) write(s string) { - w.buf = append(w.buf, s...) -} - -func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error { - if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok { - b, err := jsm.MarshalJSONPB(w.Marshaler) - if err != nil { - return err - } - if typeURL != "" { - // we are marshaling this object to an Any type - var js map[string]*json.RawMessage - if err = json.Unmarshal(b, &js); err != nil { - return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err) - } - turl, err := json.Marshal(typeURL) - if err != nil { - return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err) - } - js["@type"] = (*json.RawMessage)(&turl) - if b, err = json.Marshal(js); err != nil { - return err - } - } - w.write(string(b)) - return nil - } - - md := m.Descriptor() - fds := md.Fields() - - // Handle well-known types. - const secondInNanos = int64(time.Second / time.Nanosecond) - switch wellKnownType(md.FullName()) { - case "Any": - return w.marshalAny(m, indent) - case "BoolValue", "BytesValue", "StringValue", - "Int32Value", "UInt32Value", "FloatValue", - "Int64Value", "UInt64Value", "DoubleValue": - fd := fds.ByNumber(1) - return w.marshalValue(fd, m.Get(fd), indent) - case "Duration": - const maxSecondsInDuration = 315576000000 - // "Generated output always contains 0, 3, 6, or 9 fractional digits, - // depending on required precision." - s := m.Get(fds.ByNumber(1)).Int() - ns := m.Get(fds.ByNumber(2)).Int() - if s < -maxSecondsInDuration || s > maxSecondsInDuration { - return fmt.Errorf("seconds out of range %v", s) - } - if ns <= -secondInNanos || ns >= secondInNanos { - return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos) - } - if (s > 0 && ns < 0) || (s < 0 && ns > 0) { - return errors.New("signs of seconds and nanos do not match") - } - var sign string - if s < 0 || ns < 0 { - sign, s, ns = "-", -1*s, -1*ns - } - x := fmt.Sprintf("%s%d.%09d", sign, s, ns) - x = strings.TrimSuffix(x, "000") - x = strings.TrimSuffix(x, "000") - x = strings.TrimSuffix(x, ".000") - w.write(fmt.Sprintf(`"%vs"`, x)) - return nil - case "Timestamp": - // "RFC 3339, where generated output will always be Z-normalized - // and uses 0, 3, 6 or 9 fractional digits." - s := m.Get(fds.ByNumber(1)).Int() - ns := m.Get(fds.ByNumber(2)).Int() - if ns < 0 || ns >= secondInNanos { - return fmt.Errorf("ns out of range [0, %v)", secondInNanos) - } - t := time.Unix(s, ns).UTC() - // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits). - x := t.Format("2006-01-02T15:04:05.000000000") - x = strings.TrimSuffix(x, "000") - x = strings.TrimSuffix(x, "000") - x = strings.TrimSuffix(x, ".000") - w.write(fmt.Sprintf(`"%vZ"`, x)) - return nil - case "Value": - // JSON value; which is a null, number, string, bool, object, or array. - od := md.Oneofs().Get(0) - fd := m.WhichOneof(od) - if fd == nil { - return errors.New("nil Value") - } - return w.marshalValue(fd, m.Get(fd), indent) - case "Struct", "ListValue": - // JSON object or array. - fd := fds.ByNumber(1) - return w.marshalValue(fd, m.Get(fd), indent) - } - - w.write("{") - if w.Indent != "" { - w.write("\n") - } - - firstField := true - if typeURL != "" { - if err := w.marshalTypeURL(indent, typeURL); err != nil { - return err - } - firstField = false - } - - for i := 0; i < fds.Len(); { - fd := fds.Get(i) - if od := fd.ContainingOneof(); od != nil { - fd = m.WhichOneof(od) - i += od.Fields().Len() - if fd == nil { - continue - } - } else { - i++ - } - - v := m.Get(fd) - - if !m.Has(fd) { - if !w.EmitDefaults || fd.ContainingOneof() != nil { - continue - } - if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) { - v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars - } - } - - if !firstField { - w.writeComma() - } - if err := w.marshalField(fd, v, indent); err != nil { - return err - } - firstField = false - } - - // Handle proto2 extensions. - if md.ExtensionRanges().Len() > 0 { - // Collect a sorted list of all extension descriptor and values. - type ext struct { - desc protoreflect.FieldDescriptor - val protoreflect.Value - } - var exts []ext - m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - if fd.IsExtension() { - exts = append(exts, ext{fd, v}) - } - return true - }) - sort.Slice(exts, func(i, j int) bool { - return exts[i].desc.Number() < exts[j].desc.Number() - }) - - for _, ext := range exts { - if !firstField { - w.writeComma() - } - if err := w.marshalField(ext.desc, ext.val, indent); err != nil { - return err - } - firstField = false - } - } - - if w.Indent != "" { - w.write("\n") - w.write(indent) - } - w.write("}") - return nil -} - -func (w *jsonWriter) writeComma() { - if w.Indent != "" { - w.write(",\n") - } else { - w.write(",") - } -} - -func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error { - // "If the Any contains a value that has a special JSON mapping, - // it will be converted as follows: {"@type": xxx, "value": yyy}. - // Otherwise, the value will be converted into a JSON object, - // and the "@type" field will be inserted to indicate the actual data type." - md := m.Descriptor() - typeURL := m.Get(md.Fields().ByNumber(1)).String() - rawVal := m.Get(md.Fields().ByNumber(2)).Bytes() - - var m2 protoreflect.Message - if w.AnyResolver != nil { - mi, err := w.AnyResolver.Resolve(typeURL) - if err != nil { - return err - } - m2 = proto.MessageReflect(mi) - } else { - mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL) - if err != nil { - return err - } - m2 = mt.New() - } - - if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil { - return err - } - - if wellKnownType(m2.Descriptor().FullName()) == "" { - return w.marshalMessage(m2, indent, typeURL) - } - - w.write("{") - if w.Indent != "" { - w.write("\n") - } - if err := w.marshalTypeURL(indent, typeURL); err != nil { - return err - } - w.writeComma() - if w.Indent != "" { - w.write(indent) - w.write(w.Indent) - w.write(`"value": `) - } else { - w.write(`"value":`) - } - if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil { - return err - } - if w.Indent != "" { - w.write("\n") - w.write(indent) - } - w.write("}") - return nil -} - -func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error { - if w.Indent != "" { - w.write(indent) - w.write(w.Indent) - } - w.write(`"@type":`) - if w.Indent != "" { - w.write(" ") - } - b, err := json.Marshal(typeURL) - if err != nil { - return err - } - w.write(string(b)) - return nil -} - -// marshalField writes field description and value to the Writer. -func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { - if w.Indent != "" { - w.write(indent) - w.write(w.Indent) - } - w.write(`"`) - switch { - case fd.IsExtension(): - // For message set, use the fname of the message as the extension name. - name := string(fd.FullName()) - if isMessageSet(fd.ContainingMessage()) { - name = strings.TrimSuffix(name, ".message_set_extension") - } - - w.write("[" + name + "]") - case w.OrigName: - name := string(fd.Name()) - if fd.Kind() == protoreflect.GroupKind { - name = string(fd.Message().Name()) - } - w.write(name) - default: - w.write(string(fd.JSONName())) - } - w.write(`":`) - if w.Indent != "" { - w.write(" ") - } - return w.marshalValue(fd, v, indent) -} - -func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { - switch { - case fd.IsList(): - w.write("[") - comma := "" - lv := v.List() - for i := 0; i < lv.Len(); i++ { - w.write(comma) - if w.Indent != "" { - w.write("\n") - w.write(indent) - w.write(w.Indent) - w.write(w.Indent) - } - if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil { - return err - } - comma = "," - } - if w.Indent != "" { - w.write("\n") - w.write(indent) - w.write(w.Indent) - } - w.write("]") - return nil - case fd.IsMap(): - kfd := fd.MapKey() - vfd := fd.MapValue() - mv := v.Map() - - // Collect a sorted list of all map keys and values. - type entry struct{ key, val protoreflect.Value } - var entries []entry - mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { - entries = append(entries, entry{k.Value(), v}) - return true - }) - sort.Slice(entries, func(i, j int) bool { - switch kfd.Kind() { - case protoreflect.BoolKind: - return !entries[i].key.Bool() && entries[j].key.Bool() - case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: - return entries[i].key.Int() < entries[j].key.Int() - case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: - return entries[i].key.Uint() < entries[j].key.Uint() - case protoreflect.StringKind: - return entries[i].key.String() < entries[j].key.String() - default: - panic("invalid kind") - } - }) - - w.write(`{`) - comma := "" - for _, entry := range entries { - w.write(comma) - if w.Indent != "" { - w.write("\n") - w.write(indent) - w.write(w.Indent) - w.write(w.Indent) - } - - s := fmt.Sprint(entry.key.Interface()) - b, err := json.Marshal(s) - if err != nil { - return err - } - w.write(string(b)) - - w.write(`:`) - if w.Indent != "" { - w.write(` `) - } - - if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil { - return err - } - comma = "," - } - if w.Indent != "" { - w.write("\n") - w.write(indent) - w.write(w.Indent) - } - w.write(`}`) - return nil - default: - return w.marshalSingularValue(fd, v, indent) - } -} - -func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { - switch { - case !v.IsValid(): - w.write("null") - return nil - case fd.Message() != nil: - return w.marshalMessage(v.Message(), indent+w.Indent, "") - case fd.Enum() != nil: - if fd.Enum().FullName() == "google.protobuf.NullValue" { - w.write("null") - return nil - } - - vd := fd.Enum().Values().ByNumber(v.Enum()) - if vd == nil || w.EnumsAsInts { - w.write(strconv.Itoa(int(v.Enum()))) - } else { - w.write(`"` + string(vd.Name()) + `"`) - } - return nil - default: - switch v.Interface().(type) { - case float32, float64: - switch { - case math.IsInf(v.Float(), +1): - w.write(`"Infinity"`) - return nil - case math.IsInf(v.Float(), -1): - w.write(`"-Infinity"`) - return nil - case math.IsNaN(v.Float()): - w.write(`"NaN"`) - return nil - } - case int64, uint64: - w.write(fmt.Sprintf(`"%d"`, v.Interface())) - return nil - } - - b, err := json.Marshal(v.Interface()) - if err != nil { - return err - } - w.write(string(b)) - return nil - } -} diff --git a/vendor/github.com/golang/protobuf/jsonpb/json.go b/vendor/github.com/golang/protobuf/jsonpb/json.go deleted file mode 100644 index 480e2448d..000000000 --- a/vendor/github.com/golang/protobuf/jsonpb/json.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package jsonpb provides functionality to marshal and unmarshal between a -// protocol buffer message and JSON. It follows the specification at -// https://developers.google.com/protocol-buffers/docs/proto3#json. -// -// Do not rely on the default behavior of the standard encoding/json package -// when called on generated message types as it does not operate correctly. -// -// Deprecated: Use the "google.golang.org/protobuf/encoding/protojson" -// package instead. -package jsonpb - -import ( - "github.com/golang/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" - "google.golang.org/protobuf/runtime/protoimpl" -) - -// AnyResolver takes a type URL, present in an Any message, -// and resolves it into an instance of the associated message. -type AnyResolver interface { - Resolve(typeURL string) (proto.Message, error) -} - -type anyResolver struct{ AnyResolver } - -func (r anyResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { - return r.FindMessageByURL(string(message)) -} - -func (r anyResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) { - m, err := r.Resolve(url) - if err != nil { - return nil, err - } - return protoimpl.X.MessageTypeOf(m), nil -} - -func (r anyResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { - return protoregistry.GlobalTypes.FindExtensionByName(field) -} - -func (r anyResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { - return protoregistry.GlobalTypes.FindExtensionByNumber(message, field) -} - -func wellKnownType(s protoreflect.FullName) string { - if s.Parent() == "google.protobuf" { - switch s.Name() { - case "Empty", "Any", - "BoolValue", "BytesValue", "StringValue", - "Int32Value", "UInt32Value", "FloatValue", - "Int64Value", "UInt64Value", "DoubleValue", - "Duration", "Timestamp", - "NullValue", "Struct", "Value", "ListValue": - return string(s.Name()) - } - } - return "" -} - -func isMessageSet(md protoreflect.MessageDescriptor) bool { - ms, ok := md.(interface{ IsMessageSet() bool }) - return ok && ms.IsMessageSet() -} diff --git a/vendor/github.com/golang/protobuf/ptypes/any.go b/vendor/github.com/golang/protobuf/ptypes/any.go index 85f9f5736..fdff3fdb4 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any.go +++ b/vendor/github.com/golang/protobuf/ptypes/any.go @@ -127,9 +127,10 @@ func Is(any *anypb.Any, m proto.Message) bool { // The allocated message is stored in the embedded proto.Message. // // Example: -// var x ptypes.DynamicAny -// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... } -// fmt.Printf("unmarshaled message: %v", x.Message) +// +// var x ptypes.DynamicAny +// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... } +// fmt.Printf("unmarshaled message: %v", x.Message) // // Deprecated: Use the any.UnmarshalNew method instead to unmarshal // the any message contents into a new instance of the underlying message. diff --git a/vendor/github.com/google/gnostic/LICENSE b/vendor/github.com/google/gnostic-models/LICENSE similarity index 100% rename from vendor/github.com/google/gnostic/LICENSE rename to vendor/github.com/google/gnostic-models/LICENSE diff --git a/vendor/github.com/google/gnostic/compiler/README.md b/vendor/github.com/google/gnostic-models/compiler/README.md similarity index 100% rename from vendor/github.com/google/gnostic/compiler/README.md rename to vendor/github.com/google/gnostic-models/compiler/README.md diff --git a/vendor/github.com/google/gnostic/compiler/context.go b/vendor/github.com/google/gnostic-models/compiler/context.go similarity index 100% rename from vendor/github.com/google/gnostic/compiler/context.go rename to vendor/github.com/google/gnostic-models/compiler/context.go diff --git a/vendor/github.com/google/gnostic/compiler/error.go b/vendor/github.com/google/gnostic-models/compiler/error.go similarity index 100% rename from vendor/github.com/google/gnostic/compiler/error.go rename to vendor/github.com/google/gnostic-models/compiler/error.go diff --git a/vendor/github.com/google/gnostic/compiler/extensions.go b/vendor/github.com/google/gnostic-models/compiler/extensions.go similarity index 97% rename from vendor/github.com/google/gnostic/compiler/extensions.go rename to vendor/github.com/google/gnostic-models/compiler/extensions.go index 5b5a916d2..250c81e8c 100644 --- a/vendor/github.com/google/gnostic/compiler/extensions.go +++ b/vendor/github.com/google/gnostic-models/compiler/extensions.go @@ -24,7 +24,7 @@ import ( "github.com/golang/protobuf/ptypes/any" yaml "gopkg.in/yaml.v3" - extensions "github.com/google/gnostic/extensions" + extensions "github.com/google/gnostic-models/extensions" ) // ExtensionHandler describes a binary that is called by the compiler to handle specification extensions. diff --git a/vendor/github.com/google/gnostic/compiler/helpers.go b/vendor/github.com/google/gnostic-models/compiler/helpers.go similarity index 99% rename from vendor/github.com/google/gnostic/compiler/helpers.go rename to vendor/github.com/google/gnostic-models/compiler/helpers.go index 97ffaa513..975d65e8f 100644 --- a/vendor/github.com/google/gnostic/compiler/helpers.go +++ b/vendor/github.com/google/gnostic-models/compiler/helpers.go @@ -22,7 +22,7 @@ import ( "gopkg.in/yaml.v3" - "github.com/google/gnostic/jsonschema" + "github.com/google/gnostic-models/jsonschema" ) // compiler helper functions, usually called from generated code diff --git a/vendor/github.com/google/gnostic/compiler/main.go b/vendor/github.com/google/gnostic-models/compiler/main.go similarity index 100% rename from vendor/github.com/google/gnostic/compiler/main.go rename to vendor/github.com/google/gnostic-models/compiler/main.go diff --git a/vendor/github.com/google/gnostic/compiler/reader.go b/vendor/github.com/google/gnostic-models/compiler/reader.go similarity index 100% rename from vendor/github.com/google/gnostic/compiler/reader.go rename to vendor/github.com/google/gnostic-models/compiler/reader.go diff --git a/vendor/github.com/google/gnostic/extensions/README.md b/vendor/github.com/google/gnostic-models/extensions/README.md similarity index 100% rename from vendor/github.com/google/gnostic/extensions/README.md rename to vendor/github.com/google/gnostic-models/extensions/README.md diff --git a/vendor/github.com/google/gnostic/extensions/extension.pb.go b/vendor/github.com/google/gnostic-models/extensions/extension.pb.go similarity index 99% rename from vendor/github.com/google/gnostic/extensions/extension.pb.go rename to vendor/github.com/google/gnostic-models/extensions/extension.pb.go index a6a4ccca6..a71df8abe 100644 --- a/vendor/github.com/google/gnostic/extensions/extension.pb.go +++ b/vendor/github.com/google/gnostic-models/extensions/extension.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.18.1 +// protoc-gen-go v1.27.1 +// protoc v3.19.3 // source: extensions/extension.proto package gnostic_extension_v1 diff --git a/vendor/github.com/google/gnostic/extensions/extension.proto b/vendor/github.com/google/gnostic-models/extensions/extension.proto similarity index 100% rename from vendor/github.com/google/gnostic/extensions/extension.proto rename to vendor/github.com/google/gnostic-models/extensions/extension.proto diff --git a/vendor/github.com/google/gnostic/extensions/extensions.go b/vendor/github.com/google/gnostic-models/extensions/extensions.go similarity index 100% rename from vendor/github.com/google/gnostic/extensions/extensions.go rename to vendor/github.com/google/gnostic-models/extensions/extensions.go diff --git a/vendor/github.com/google/gnostic/jsonschema/README.md b/vendor/github.com/google/gnostic-models/jsonschema/README.md similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/README.md rename to vendor/github.com/google/gnostic-models/jsonschema/README.md diff --git a/vendor/github.com/google/gnostic/jsonschema/base.go b/vendor/github.com/google/gnostic-models/jsonschema/base.go similarity index 90% rename from vendor/github.com/google/gnostic/jsonschema/base.go rename to vendor/github.com/google/gnostic-models/jsonschema/base.go index 0af8b148b..5fcc4885a 100644 --- a/vendor/github.com/google/gnostic/jsonschema/base.go +++ b/vendor/github.com/google/gnostic-models/jsonschema/base.go @@ -1,3 +1,16 @@ +// Copyright 2017 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. @@ -81,4 +94,4 @@ YXkiIH0sCiAgICAgICAgImFueU9mIjogeyAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NjaGVtYUFycmF5 IiB9LAogICAgICAgICJvbmVPZiI6IHsgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWFBcnJheSIg fSwKICAgICAgICAibm90IjogeyAiJHJlZiI6ICIjIiB9CiAgICB9LAogICAgImRlcGVuZGVuY2llcyI6 IHsKICAgICAgICAiZXhjbHVzaXZlTWF4aW11bSI6IFsgIm1heGltdW0iIF0sCiAgICAgICAgImV4Y2x1 -c2l2ZU1pbmltdW0iOiBbICJtaW5pbXVtIiBdCiAgICB9LAogICAgImRlZmF1bHQiOiB7fQp9Cg==`)} \ No newline at end of file +c2l2ZU1pbmltdW0iOiBbICJtaW5pbXVtIiBdCiAgICB9LAogICAgImRlZmF1bHQiOiB7fQp9Cg==`)} diff --git a/vendor/github.com/google/gnostic/jsonschema/display.go b/vendor/github.com/google/gnostic-models/jsonschema/display.go similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/display.go rename to vendor/github.com/google/gnostic-models/jsonschema/display.go diff --git a/vendor/github.com/google/gnostic/jsonschema/models.go b/vendor/github.com/google/gnostic-models/jsonschema/models.go similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/models.go rename to vendor/github.com/google/gnostic-models/jsonschema/models.go diff --git a/vendor/github.com/google/gnostic/jsonschema/operations.go b/vendor/github.com/google/gnostic-models/jsonschema/operations.go similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/operations.go rename to vendor/github.com/google/gnostic-models/jsonschema/operations.go diff --git a/vendor/github.com/google/gnostic/jsonschema/reader.go b/vendor/github.com/google/gnostic-models/jsonschema/reader.go similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/reader.go rename to vendor/github.com/google/gnostic-models/jsonschema/reader.go diff --git a/vendor/github.com/google/gnostic/jsonschema/schema.json b/vendor/github.com/google/gnostic-models/jsonschema/schema.json similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/schema.json rename to vendor/github.com/google/gnostic-models/jsonschema/schema.json diff --git a/vendor/github.com/google/gnostic/jsonschema/writer.go b/vendor/github.com/google/gnostic-models/jsonschema/writer.go similarity index 100% rename from vendor/github.com/google/gnostic/jsonschema/writer.go rename to vendor/github.com/google/gnostic-models/jsonschema/writer.go diff --git a/vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.go b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.go similarity index 99% rename from vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.go rename to vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.go index 0f1790766..d71fe6d54 100644 --- a/vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.go +++ b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.go @@ -23,7 +23,7 @@ import ( "gopkg.in/yaml.v3" - "github.com/google/gnostic/compiler" + "github.com/google/gnostic-models/compiler" ) // Version returns the package name (and OpenAPI version). diff --git a/vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.pb.go b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.pb.go similarity index 99% rename from vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.pb.go rename to vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.pb.go index 06b60157c..65c4c913c 100644 --- a/vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.pb.go +++ b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.pb.go @@ -16,8 +16,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.18.1 +// protoc-gen-go v1.27.1 +// protoc v3.19.3 // source: openapiv2/OpenAPIv2.proto package openapi_v2 diff --git a/vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.proto b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto similarity index 100% rename from vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.proto rename to vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto diff --git a/vendor/github.com/google/gnostic/openapiv2/README.md b/vendor/github.com/google/gnostic-models/openapiv2/README.md similarity index 100% rename from vendor/github.com/google/gnostic/openapiv2/README.md rename to vendor/github.com/google/gnostic-models/openapiv2/README.md diff --git a/vendor/github.com/google/gnostic/openapiv2/document.go b/vendor/github.com/google/gnostic-models/openapiv2/document.go similarity index 96% rename from vendor/github.com/google/gnostic/openapiv2/document.go rename to vendor/github.com/google/gnostic-models/openapiv2/document.go index 0021ae871..e96ac0d6d 100644 --- a/vendor/github.com/google/gnostic/openapiv2/document.go +++ b/vendor/github.com/google/gnostic-models/openapiv2/document.go @@ -17,7 +17,7 @@ package openapi_v2 import ( "gopkg.in/yaml.v3" - "github.com/google/gnostic/compiler" + "github.com/google/gnostic-models/compiler" ) // ParseDocument reads an OpenAPI v2 description from a YAML/JSON representation. diff --git a/vendor/github.com/google/gnostic/openapiv2/openapi-2.0.json b/vendor/github.com/google/gnostic-models/openapiv2/openapi-2.0.json similarity index 100% rename from vendor/github.com/google/gnostic/openapiv2/openapi-2.0.json rename to vendor/github.com/google/gnostic-models/openapiv2/openapi-2.0.json diff --git a/vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.go b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.go similarity index 99% rename from vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.go rename to vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.go index 5f4a7025e..4b1131ce1 100644 --- a/vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.go +++ b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.go @@ -23,7 +23,7 @@ import ( "gopkg.in/yaml.v3" - "github.com/google/gnostic/compiler" + "github.com/google/gnostic-models/compiler" ) // Version returns the package name (and OpenAPI version). diff --git a/vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.pb.go b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.pb.go similarity index 99% rename from vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.pb.go rename to vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.pb.go index 499e7f932..945b8d11f 100644 --- a/vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.pb.go +++ b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.pb.go @@ -16,8 +16,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.18.1 +// protoc-gen-go v1.27.1 +// protoc v3.19.3 // source: openapiv3/OpenAPIv3.proto package openapi_v3 diff --git a/vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.proto b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto similarity index 100% rename from vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.proto rename to vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto diff --git a/vendor/github.com/google/gnostic/openapiv3/README.md b/vendor/github.com/google/gnostic-models/openapiv3/README.md similarity index 100% rename from vendor/github.com/google/gnostic/openapiv3/README.md rename to vendor/github.com/google/gnostic-models/openapiv3/README.md diff --git a/vendor/github.com/google/gnostic/openapiv3/document.go b/vendor/github.com/google/gnostic-models/openapiv3/document.go similarity index 96% rename from vendor/github.com/google/gnostic/openapiv3/document.go rename to vendor/github.com/google/gnostic-models/openapiv3/document.go index ef10d1d90..1cee46773 100644 --- a/vendor/github.com/google/gnostic/openapiv3/document.go +++ b/vendor/github.com/google/gnostic-models/openapiv3/document.go @@ -17,7 +17,7 @@ package openapi_v3 import ( "gopkg.in/yaml.v3" - "github.com/google/gnostic/compiler" + "github.com/google/gnostic-models/compiler" ) // ParseDocument reads an OpenAPI v3 description from a YAML/JSON representation. diff --git a/vendor/github.com/google/gnostic/openapiv3/openapi-3.0.json b/vendor/github.com/google/gnostic/openapiv3/openapi-3.0.json deleted file mode 100644 index d5caed162..000000000 --- a/vendor/github.com/google/gnostic/openapiv3/openapi-3.0.json +++ /dev/null @@ -1,1251 +0,0 @@ -{ - "title": "A JSON Schema for OpenAPI 3.0.", - "id": "http://openapis.org/v3/schema.json#", - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "description": "This is the root document object of the OpenAPI document.", - "required": [ - "openapi", - "info", - "paths" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "openapi": { - "type": "string" - }, - "info": { - "$ref": "#/definitions/info" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - }, - "paths": { - "$ref": "#/definitions/paths" - }, - "components": { - "$ref": "#/definitions/components" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/definitions/securityRequirement" - }, - "uniqueItems": true - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/definitions/tag" - }, - "uniqueItems": true - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - } - }, - "definitions": { - "info": { - "type": "object", - "description": "The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.", - "required": [ - "title", - "version" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "termsOfService": { - "type": "string" - }, - "contact": { - "$ref": "#/definitions/contact" - }, - "license": { - "$ref": "#/definitions/license" - }, - "version": { - "type": "string" - } - } - }, - "contact": { - "type": "object", - "description": "Contact information for the exposed API.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "email": { - "type": "string", - "format": "email" - } - } - }, - "license": { - "type": "object", - "description": "License information for the exposed API.", - "required": [ - "name" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "server": { - "type": "object", - "description": "An object representing a Server.", - "required": [ - "url" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "url": { - "type": "string" - }, - "description": { - "type": "string" - }, - "variables": { - "$ref": "#/definitions/serverVariables" - } - } - }, - "serverVariable": { - "type": "object", - "description": "An object representing a Server Variable for server URL template substitution.", - "required": [ - "default" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "default": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "components": { - "type": "object", - "description": "Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "schemas": { - "$ref": "#/definitions/schemasOrReferences" - }, - "responses": { - "$ref": "#/definitions/responsesOrReferences" - }, - "parameters": { - "$ref": "#/definitions/parametersOrReferences" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "requestBodies": { - "$ref": "#/definitions/requestBodiesOrReferences" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "securitySchemes": { - "$ref": "#/definitions/securitySchemesOrReferences" - }, - "links": { - "$ref": "#/definitions/linksOrReferences" - }, - "callbacks": { - "$ref": "#/definitions/callbacksOrReferences" - } - } - }, - "paths": { - "type": "object", - "description": "Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints.", - "additionalProperties": false, - "patternProperties": { - "^/": { - "$ref": "#/definitions/pathItem" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - } - }, - "pathItem": { - "type": "object", - "description": "Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "$ref": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "get": { - "$ref": "#/definitions/operation" - }, - "put": { - "$ref": "#/definitions/operation" - }, - "post": { - "$ref": "#/definitions/operation" - }, - "delete": { - "$ref": "#/definitions/operation" - }, - "options": { - "$ref": "#/definitions/operation" - }, - "head": { - "$ref": "#/definitions/operation" - }, - "patch": { - "$ref": "#/definitions/operation" - }, - "trace": { - "$ref": "#/definitions/operation" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/parameterOrReference" - }, - "uniqueItems": true - } - } - }, - "operation": { - "type": "object", - "description": "Describes a single API operation on a path.", - "required": [ - "responses" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/parameterOrReference" - }, - "uniqueItems": true - }, - "requestBody": { - "$ref": "#/definitions/requestBodyOrReference" - }, - "responses": { - "$ref": "#/definitions/responses" - }, - "callbacks": { - "$ref": "#/definitions/callbacksOrReferences" - }, - "deprecated": { - "type": "boolean" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/definitions/securityRequirement" - }, - "uniqueItems": true - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - } - } - }, - "externalDocs": { - "type": "object", - "description": "Allows referencing an external resource for extended documentation.", - "required": [ - "url" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "parameter": { - "type": "object", - "description": "Describes a single operation parameter. A unique parameter is defined by a combination of a name and location.", - "required": [ - "name", - "in" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "in": { - "type": "string" - }, - "description": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "deprecated": { - "type": "boolean" - }, - "allowEmptyValue": { - "type": "boolean" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - }, - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - } - } - }, - "requestBody": { - "type": "object", - "description": "Describes a single request body.", - "required": [ - "content" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - }, - "required": { - "type": "boolean" - } - } - }, - "mediaType": { - "type": "object", - "description": "Each Media Type Object provides schema and examples for the media type identified by its key.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "encoding": { - "$ref": "#/definitions/encodings" - } - } - }, - "encoding": { - "type": "object", - "description": "A single encoding definition applied to a single schema property.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "contentType": { - "type": "string" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - } - } - }, - "responses": { - "type": "object", - "description": "A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call.", - "additionalProperties": false, - "patternProperties": { - "^([0-9X]{3})$": { - "$ref": "#/definitions/responseOrReference" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "default": { - "$ref": "#/definitions/responseOrReference" - } - } - }, - "response": { - "type": "object", - "description": "Describes a single response from an API Operation, including design-time, static `links` to operations based on the response.", - "required": [ - "description" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - }, - "links": { - "$ref": "#/definitions/linksOrReferences" - } - } - }, - "callback": { - "type": "object", - "description": "A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.", - "additionalProperties": false, - "patternProperties": { - "^": { - "$ref": "#/definitions/pathItem" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - } - }, - "example": { - "type": "object", - "description": "", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "value": { - "$ref": "#/definitions/any" - }, - "externalValue": { - "type": "string" - } - } - }, - "link": { - "type": "object", - "description": "The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "operationRef": { - "type": "string" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "$ref": "#/definitions/anysOrExpressions" - }, - "requestBody": { - "$ref": "#/definitions/anyOrExpression" - }, - "description": { - "type": "string" - }, - "server": { - "$ref": "#/definitions/server" - } - } - }, - "header": { - "type": "object", - "description": "The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`).", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "deprecated": { - "type": "boolean" - }, - "allowEmptyValue": { - "type": "boolean" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - }, - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - } - } - }, - "tag": { - "type": "object", - "description": "Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.", - "required": [ - "name" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - } - } - }, - "reference": { - "type": "object", - "description": "A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification.", - "required": [ - "$ref" - ], - "additionalProperties": false, - "properties": { - "$ref": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "schema": { - "type": "object", - "description": "The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "nullable": { - "type": "boolean" - }, - "discriminator": { - "$ref": "#/definitions/discriminator" - }, - "readOnly": { - "type": "boolean" - }, - "writeOnly": { - "type": "boolean" - }, - "xml": { - "$ref": "#/definitions/xml" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - }, - "example": { - "$ref": "#/definitions/any" - }, - "deprecated": { - "type": "boolean" - }, - "title": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/title" - }, - "multipleOf": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" - }, - "maximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" - }, - "exclusiveMaximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" - }, - "minimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" - }, - "exclusiveMinimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" - }, - "maxLength": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxLength" - }, - "minLength": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minLength" - }, - "pattern": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" - }, - "maxItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxItems" - }, - "minItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minItems" - }, - "uniqueItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" - }, - "maxProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxProperties" - }, - "minProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minProperties" - }, - "required": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/required" - }, - "enum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" - }, - "type": { - "type": "string" - }, - "allOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "anyOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "not": { - "$ref": "#/definitions/schema" - }, - "items": { - "anyOf": [ - { - "$ref": "#/definitions/schemaOrReference" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - } - ] - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/schemaOrReference" - } - }, - "additionalProperties": { - "oneOf": [ - { - "$ref": "#/definitions/schemaOrReference" - }, - { - "type": "boolean" - } - ] - }, - "default": { - "$ref": "#/definitions/defaultType" - }, - "description": { - "type": "string" - }, - "format": { - "type": "string" - } - } - }, - "discriminator": { - "type": "object", - "description": "When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered.", - "required": [ - "propertyName" - ], - "additionalProperties": false, - "properties": { - "propertyName": { - "type": "string" - }, - "mapping": { - "$ref": "#/definitions/strings" - } - } - }, - "xml": { - "type": "object", - "description": "A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "prefix": { - "type": "string" - }, - "attribute": { - "type": "boolean" - }, - "wrapped": { - "type": "boolean" - } - } - }, - "securityScheme": { - "type": "object", - "description": "Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header or as a query parameter), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect Discovery.", - "required": [ - "type" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "type": { - "type": "string" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "in": { - "type": "string" - }, - "scheme": { - "type": "string" - }, - "bearerFormat": { - "type": "string" - }, - "flows": { - "$ref": "#/definitions/oauthFlows" - }, - "openIdConnectUrl": { - "type": "string" - } - } - }, - "oauthFlows": { - "type": "object", - "description": "Allows configuration of the supported OAuth Flows.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "implicit": { - "$ref": "#/definitions/oauthFlow" - }, - "password": { - "$ref": "#/definitions/oauthFlow" - }, - "clientCredentials": { - "$ref": "#/definitions/oauthFlow" - }, - "authorizationCode": { - "$ref": "#/definitions/oauthFlow" - } - } - }, - "oauthFlow": { - "type": "object", - "description": "Configuration details for a supported OAuth Flow", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "authorizationUrl": { - "type": "string" - }, - "tokenUrl": { - "type": "string" - }, - "refreshUrl": { - "type": "string" - }, - "scopes": { - "$ref": "#/definitions/strings" - } - } - }, - "securityRequirement": { - "type": "object", - "description": "Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the Open API object or Operation Object, only one of Security Requirement Objects in the list needs to be satisfied to authorize the request.", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9\\.\\-_]+$": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - } - } - }, - "anyOrExpression": { - "oneOf": [ - { - "$ref": "#/definitions/any" - }, - { - "$ref": "#/definitions/expression" - } - ] - }, - "callbackOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/callback" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "exampleOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/example" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "headerOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/header" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "linkOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/link" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "parameterOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/parameter" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "requestBodyOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/requestBody" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "responseOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/response" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "schemaOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/schema" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "securitySchemeOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/securityScheme" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "anysOrExpressions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/anyOrExpression" - } - }, - "callbacksOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/callbackOrReference" - } - }, - "encodings": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/encoding" - } - }, - "examplesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/exampleOrReference" - } - }, - "headersOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/headerOrReference" - } - }, - "linksOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/linkOrReference" - } - }, - "mediaTypes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mediaType" - } - }, - "parametersOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/parameterOrReference" - } - }, - "requestBodiesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/requestBodyOrReference" - } - }, - "responsesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/responseOrReference" - } - }, - "schemasOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/schemaOrReference" - } - }, - "securitySchemesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/securitySchemeOrReference" - } - }, - "serverVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/serverVariable" - } - }, - "strings": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "object": { - "type": "object", - "additionalProperties": true - }, - "any": { - "additionalProperties": true - }, - "expression": { - "type": "object", - "additionalProperties": true - }, - "specificationExtension": { - "description": "Any property starting with x- is valid.", - "oneOf": [ - { - "type": "null" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "object" - }, - { - "type": "array" - } - ] - }, - "defaultType": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "array" - }, - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - } - ] - } - } -} diff --git a/vendor/github.com/google/gnostic/openapiv3/openapi-3.1.json b/vendor/github.com/google/gnostic/openapiv3/openapi-3.1.json deleted file mode 100644 index ed0b83adf..000000000 --- a/vendor/github.com/google/gnostic/openapiv3/openapi-3.1.json +++ /dev/null @@ -1,1250 +0,0 @@ -{ - "title": "A JSON Schema for OpenAPI 3.0.", - "id": "http://openapis.org/v3/schema.json#", - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "description": "This is the root document object of the OpenAPI document.", - "required": [ - "openapi", - "info", - "paths" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "openapi": { - "type": "string" - }, - "info": { - "$ref": "#/definitions/info" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - }, - "paths": { - "$ref": "#/definitions/paths" - }, - "components": { - "$ref": "#/definitions/components" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/definitions/securityRequirement" - }, - "uniqueItems": true - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/definitions/tag" - }, - "uniqueItems": true - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - } - }, - "definitions": { - "info": { - "type": "object", - "description": "The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.", - "required": [ - "title", - "version" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "termsOfService": { - "type": "string" - }, - "contact": { - "$ref": "#/definitions/contact" - }, - "license": { - "$ref": "#/definitions/license" - }, - "version": { - "type": "string" - }, - "summary": { - "type": "string" - } - } - }, - "contact": { - "type": "object", - "description": "Contact information for the exposed API.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "email": { - "type": "string", - "format": "email" - } - } - }, - "license": { - "type": "object", - "description": "License information for the exposed API.", - "required": [ - "name" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "server": { - "type": "object", - "description": "An object representing a Server.", - "required": [ - "url" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "url": { - "type": "string" - }, - "description": { - "type": "string" - }, - "variables": { - "$ref": "#/definitions/serverVariables" - } - } - }, - "serverVariable": { - "type": "object", - "description": "An object representing a Server Variable for server URL template substitution.", - "required": [ - "default" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "default": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "components": { - "type": "object", - "description": "Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "schemas": { - "$ref": "#/definitions/schemasOrReferences" - }, - "responses": { - "$ref": "#/definitions/responsesOrReferences" - }, - "parameters": { - "$ref": "#/definitions/parametersOrReferences" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "requestBodies": { - "$ref": "#/definitions/requestBodiesOrReferences" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "securitySchemes": { - "$ref": "#/definitions/securitySchemesOrReferences" - }, - "links": { - "$ref": "#/definitions/linksOrReferences" - }, - "callbacks": { - "$ref": "#/definitions/callbacksOrReferences" - } - } - }, - "paths": { - "type": "object", - "description": "Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints.", - "additionalProperties": false, - "patternProperties": { - "^/": { - "$ref": "#/definitions/pathItem" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - } - }, - "pathItem": { - "type": "object", - "description": "Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "$ref": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "get": { - "$ref": "#/definitions/operation" - }, - "put": { - "$ref": "#/definitions/operation" - }, - "post": { - "$ref": "#/definitions/operation" - }, - "delete": { - "$ref": "#/definitions/operation" - }, - "options": { - "$ref": "#/definitions/operation" - }, - "head": { - "$ref": "#/definitions/operation" - }, - "patch": { - "$ref": "#/definitions/operation" - }, - "trace": { - "$ref": "#/definitions/operation" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/parameterOrReference" - }, - "uniqueItems": true - } - } - }, - "operation": { - "type": "object", - "description": "Describes a single API operation on a path.", - "required": [ - "responses" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/parameterOrReference" - }, - "uniqueItems": true - }, - "requestBody": { - "$ref": "#/definitions/requestBodyOrReference" - }, - "responses": { - "$ref": "#/definitions/responses" - }, - "callbacks": { - "$ref": "#/definitions/callbacksOrReferences" - }, - "deprecated": { - "type": "boolean" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/definitions/securityRequirement" - }, - "uniqueItems": true - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/definitions/server" - }, - "uniqueItems": true - } - } - }, - "externalDocs": { - "type": "object", - "description": "Allows referencing an external resource for extended documentation.", - "required": [ - "url" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "parameter": { - "type": "object", - "description": "Describes a single operation parameter. A unique parameter is defined by a combination of a name and location.", - "required": [ - "name", - "in" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "in": { - "type": "string" - }, - "description": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "deprecated": { - "type": "boolean" - }, - "allowEmptyValue": { - "type": "boolean" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - }, - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - } - } - }, - "requestBody": { - "type": "object", - "description": "Describes a single request body.", - "required": [ - "content" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - }, - "required": { - "type": "boolean" - } - } - }, - "mediaType": { - "type": "object", - "description": "Each Media Type Object provides schema and examples for the media type identified by its key.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "encoding": { - "$ref": "#/definitions/encodings" - } - } - }, - "encoding": { - "type": "object", - "description": "A single encoding definition applied to a single schema property.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "contentType": { - "type": "string" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - } - } - }, - "responses": { - "type": "object", - "description": "A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call.", - "additionalProperties": false, - "patternProperties": { - "^([0-9X]{3})$": { - "$ref": "#/definitions/responseOrReference" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "default": { - "$ref": "#/definitions/responseOrReference" - } - } - }, - "response": { - "type": "object", - "description": "Describes a single response from an API Operation, including design-time, static `links` to operations based on the response.", - "required": [ - "description" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "headers": { - "$ref": "#/definitions/headersOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - }, - "links": { - "$ref": "#/definitions/linksOrReferences" - } - } - }, - "callback": { - "type": "object", - "description": "A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.", - "additionalProperties": false, - "patternProperties": { - "^": { - "$ref": "#/definitions/pathItem" - }, - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - } - }, - "example": { - "type": "object", - "description": "", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "value": { - "$ref": "#/definitions/any" - }, - "externalValue": { - "type": "string" - } - } - }, - "link": { - "type": "object", - "description": "The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "operationRef": { - "type": "string" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "$ref": "#/definitions/anyOrExpression" - }, - "requestBody": { - "$ref": "#/definitions/anyOrExpression" - }, - "description": { - "type": "string" - }, - "server": { - "$ref": "#/definitions/server" - } - } - }, - "header": { - "type": "object", - "description": "The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`).", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "description": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "deprecated": { - "type": "boolean" - }, - "allowEmptyValue": { - "type": "boolean" - }, - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "type": "boolean" - }, - "schema": { - "$ref": "#/definitions/schemaOrReference" - }, - "example": { - "$ref": "#/definitions/any" - }, - "examples": { - "$ref": "#/definitions/examplesOrReferences" - }, - "content": { - "$ref": "#/definitions/mediaTypes" - } - } - }, - "tag": { - "type": "object", - "description": "Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.", - "required": [ - "name" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - } - } - }, - "reference": { - "type": "object", - "description": "A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification.", - "required": [ - "$ref" - ], - "additionalProperties": false, - "properties": { - "$ref": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "schema": { - "type": "object", - "description": "The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "nullable": { - "type": "boolean" - }, - "discriminator": { - "$ref": "#/definitions/discriminator" - }, - "readOnly": { - "type": "boolean" - }, - "writeOnly": { - "type": "boolean" - }, - "xml": { - "$ref": "#/definitions/xml" - }, - "externalDocs": { - "$ref": "#/definitions/externalDocs" - }, - "example": { - "$ref": "#/definitions/any" - }, - "deprecated": { - "type": "boolean" - }, - "title": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/title" - }, - "multipleOf": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" - }, - "maximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" - }, - "exclusiveMaximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" - }, - "minimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" - }, - "exclusiveMinimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" - }, - "maxLength": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxLength" - }, - "minLength": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minLength" - }, - "pattern": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" - }, - "maxItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxItems" - }, - "minItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minItems" - }, - "uniqueItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" - }, - "maxProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maxProperties" - }, - "minProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minProperties" - }, - "required": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/required" - }, - "enum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" - }, - "type": { - "type": "string" - }, - "allOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "anyOf": { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - }, - "not": { - "$ref": "#/definitions/schema" - }, - "items": { - "anyOf": [ - { - "$ref": "#/definitions/schemaOrReference" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/schemaOrReference" - }, - "minItems": 1 - } - ] - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/schemaOrReference" - } - }, - "additionalProperties": { - "oneOf": [ - { - "$ref": "#/definitions/schemaOrReference" - }, - { - "type": "boolean" - } - ] - }, - "default": { - "$ref": "#/definitions/defaultType" - }, - "description": { - "type": "string" - }, - "format": { - "type": "string" - } - } - }, - "discriminator": { - "type": "object", - "description": "When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered.", - "required": [ - "propertyName" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "propertyName": { - "type": "string" - }, - "mapping": { - "$ref": "#/definitions/strings" - } - } - }, - "xml": { - "type": "object", - "description": "A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "prefix": { - "type": "string" - }, - "attribute": { - "type": "boolean" - }, - "wrapped": { - "type": "boolean" - } - } - }, - "securityScheme": { - "type": "object", - "description": "Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect. Please note that currently (2019) the implicit flow is about to be deprecated OAuth 2.0 Security Best Current Practice. Recommended for most use case is Authorization Code Grant flow with PKCE.", - "required": [ - "type" - ], - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "type": { - "type": "string" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "in": { - "type": "string" - }, - "scheme": { - "type": "string" - }, - "bearerFormat": { - "type": "string" - }, - "flows": { - "$ref": "#/definitions/oauthFlows" - }, - "openIdConnectUrl": { - "type": "string" - } - } - }, - "oauthFlows": { - "type": "object", - "description": "Allows configuration of the supported OAuth Flows.", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "implicit": { - "$ref": "#/definitions/oauthFlow" - }, - "password": { - "$ref": "#/definitions/oauthFlow" - }, - "clientCredentials": { - "$ref": "#/definitions/oauthFlow" - }, - "authorizationCode": { - "$ref": "#/definitions/oauthFlow" - } - } - }, - "oauthFlow": { - "type": "object", - "description": "Configuration details for a supported OAuth Flow", - "additionalProperties": false, - "patternProperties": { - "^x-": { - "$ref": "#/definitions/specificationExtension" - } - }, - "properties": { - "authorizationUrl": { - "type": "string" - }, - "tokenUrl": { - "type": "string" - }, - "refreshUrl": { - "type": "string" - }, - "scopes": { - "$ref": "#/definitions/strings" - } - } - }, - "securityRequirement": { - "type": "object", - "description": "Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the Security Requirement Objects in the list needs to be satisfied to authorize the request.", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - } - }, - "anyOrExpression": { - "oneOf": [ - { - "$ref": "#/definitions/any" - }, - { - "$ref": "#/definitions/expression" - } - ] - }, - "callbackOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/callback" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "exampleOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/example" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "headerOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/header" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "linkOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/link" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "parameterOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/parameter" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "requestBodyOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/requestBody" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "responseOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/response" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "schemaOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/schema" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "securitySchemeOrReference": { - "oneOf": [ - { - "$ref": "#/definitions/securityScheme" - }, - { - "$ref": "#/definitions/reference" - } - ] - }, - "callbacksOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/callbackOrReference" - } - }, - "encodings": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/encoding" - } - }, - "examplesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/exampleOrReference" - } - }, - "headersOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/headerOrReference" - } - }, - "linksOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/linkOrReference" - } - }, - "mediaTypes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/mediaType" - } - }, - "parametersOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/parameterOrReference" - } - }, - "requestBodiesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/requestBodyOrReference" - } - }, - "responsesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/responseOrReference" - } - }, - "schemasOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/schemaOrReference" - } - }, - "securitySchemesOrReferences": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/securitySchemeOrReference" - } - }, - "serverVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/serverVariable" - } - }, - "strings": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "object": { - "type": "object", - "additionalProperties": true - }, - "any": { - "additionalProperties": true - }, - "expression": { - "type": "object", - "additionalProperties": true - }, - "specificationExtension": { - "description": "Any property starting with x- is valid.", - "oneOf": [ - { - "type": "null" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "object" - }, - { - "type": "array" - } - ] - }, - "defaultType": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "array" - }, - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - } - ] - } - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go index 087320da7..0f5b8a48c 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -5,7 +5,7 @@ // Package cmp determines equality of values. // // This package is intended to be a more powerful and safer alternative to -// reflect.DeepEqual for comparing whether two values are semantically equal. +// [reflect.DeepEqual] for comparing whether two values are semantically equal. // It is intended to only be used in tests, as performance is not a goal and // it may panic if it cannot compare the values. Its propensity towards // panicking means that its unsuitable for production environments where a @@ -18,16 +18,17 @@ // For example, an equality function may report floats as equal so long as // they are within some tolerance of each other. // -// - Types with an Equal method may use that method to determine equality. -// This allows package authors to determine the equality operation -// for the types that they define. +// - Types with an Equal method (e.g., [time.Time.Equal]) may use that method +// to determine equality. This allows package authors to determine +// the equality operation for the types that they define. // // - If no custom equality functions are used and no Equal method is defined, // equality is determined by recursively comparing the primitive kinds on -// both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, +// both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual], // unexported fields are not compared by default; they result in panics -// unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported) -// or explicitly compared using the Exporter option. +// unless suppressed by using an [Ignore] option +// (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) +// or explicitly compared using the [Exporter] option. package cmp import ( @@ -45,14 +46,14 @@ import ( // Equal reports whether x and y are equal by recursively applying the // following rules in the given order to x and y and all of their sub-values: // -// - Let S be the set of all Ignore, Transformer, and Comparer options that +// - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that // remain after applying all path filters, value filters, and type filters. -// If at least one Ignore exists in S, then the comparison is ignored. -// If the number of Transformer and Comparer options in S is non-zero, +// If at least one [Ignore] exists in S, then the comparison is ignored. +// If the number of [Transformer] and [Comparer] options in S is non-zero, // then Equal panics because it is ambiguous which option to use. -// If S contains a single Transformer, then use that to transform +// If S contains a single [Transformer], then use that to transform // the current values and recursively call Equal on the output values. -// If S contains a single Comparer, then use that to compare the current values. +// If S contains a single [Comparer], then use that to compare the current values. // Otherwise, evaluation proceeds to the next rule. // // - If the values have an Equal method of the form "(T) Equal(T) bool" or @@ -66,21 +67,22 @@ import ( // Functions are only equal if they are both nil, otherwise they are unequal. // // Structs are equal if recursively calling Equal on all fields report equal. -// If a struct contains unexported fields, Equal panics unless an Ignore option -// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option -// explicitly permits comparing the unexported field. +// If a struct contains unexported fields, Equal panics unless an [Ignore] option +// (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field +// or the [Exporter] option explicitly permits comparing the unexported field. // // Slices are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored slice or array elements report equal. // Empty non-nil slices and nil slices are not equal; to equate empty slices, -// consider using cmpopts.EquateEmpty. +// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Maps are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored map entries report equal. // Map keys are equal according to the == operator. -// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// To use custom comparisons for map keys, consider using +// [github.com/google/go-cmp/cmp/cmpopts.SortMaps]. // Empty non-nil maps and nil maps are not equal; to equate empty maps, -// consider using cmpopts.EquateEmpty. +// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Pointers and interfaces are equal if they are both nil or both non-nil, // where they have the same underlying concrete type and recursively diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export.go similarity index 94% rename from vendor/github.com/google/go-cmp/cmp/export_unsafe.go rename to vendor/github.com/google/go-cmp/cmp/export.go index e2c0f74e8..29f82fe6b 100644 --- a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go +++ b/vendor/github.com/google/go-cmp/cmp/export.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !purego -// +build !purego - package cmp import ( @@ -12,8 +9,6 @@ import ( "unsafe" ) -const supportExporters = true - // retrieveUnexportedField uses unsafe to forcibly retrieve any field from // a struct such that the value has read-write permissions. // diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go deleted file mode 100644 index ae851fe53..000000000 --- a/vendor/github.com/google/go-cmp/cmp/export_panic.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build purego -// +build purego - -package cmp - -import "reflect" - -const supportExporters = false - -func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { - panic("no support for forcibly accessing unexported fields") -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go similarity index 95% rename from vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go rename to vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go index 16e6860af..e5dfff69a 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !purego -// +build !purego - package value import ( diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go deleted file mode 100644 index 1a71bfcbd..000000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build purego -// +build purego - -package value - -import "reflect" - -// Pointer is an opaque typed pointer and is guaranteed to be comparable. -type Pointer struct { - p uintptr - t reflect.Type -} - -// PointerOf returns a Pointer from v, which must be a -// reflect.Ptr, reflect.Slice, or reflect.Map. -func PointerOf(v reflect.Value) Pointer { - // NOTE: Storing a pointer as an uintptr is technically incorrect as it - // assumes that the GC implementation does not use a moving collector. - return Pointer{v.Pointer(), v.Type()} -} - -// IsNil reports whether the pointer is nil. -func (p Pointer) IsNil() bool { - return p.p == 0 -} - -// Uintptr returns the pointer as a uintptr. -func (p Pointer) Uintptr() uintptr { - return p.p -} diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index 1f9ca9c48..754496f3b 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -13,15 +13,15 @@ import ( "github.com/google/go-cmp/cmp/internal/function" ) -// Option configures for specific behavior of Equal and Diff. In particular, -// the fundamental Option functions (Ignore, Transformer, and Comparer), +// Option configures for specific behavior of [Equal] and [Diff]. In particular, +// the fundamental Option functions ([Ignore], [Transformer], and [Comparer]), // configure how equality is determined. // -// The fundamental options may be composed with filters (FilterPath and -// FilterValues) to control the scope over which they are applied. +// The fundamental options may be composed with filters ([FilterPath] and +// [FilterValues]) to control the scope over which they are applied. // -// The cmp/cmpopts package provides helper functions for creating options that -// may be used with Equal and Diff. +// The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions +// for creating options that may be used with [Equal] and [Diff]. type Option interface { // filter applies all filters and returns the option that remains. // Each option may only read s.curPath and call s.callTTBFunc. @@ -56,9 +56,9 @@ type core struct{} func (core) isCore() {} -// Options is a list of Option values that also satisfies the Option interface. +// Options is a list of [Option] values that also satisfies the [Option] interface. // Helper comparison packages may return an Options value when packing multiple -// Option values into a single Option. When this package processes an Options, +// [Option] values into a single [Option]. When this package processes an Options, // it will be implicitly expanded into a flat list. // // Applying a filter on an Options is equivalent to applying that same filter @@ -105,16 +105,16 @@ func (opts Options) String() string { return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) } -// FilterPath returns a new Option where opt is only evaluated if filter f -// returns true for the current Path in the value tree. +// FilterPath returns a new [Option] where opt is only evaluated if filter f +// returns true for the current [Path] in the value tree. // // This filter is called even if a slice element or map entry is missing and // provides an opportunity to ignore such cases. The filter function must be // symmetric such that the filter result is identical regardless of whether the // missing value is from x or y. // -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. +// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or +// a previously filtered [Option]. func FilterPath(f func(Path) bool, opt Option) Option { if f == nil { panic("invalid path filter function") @@ -142,7 +142,7 @@ func (f pathFilter) String() string { return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) } -// FilterValues returns a new Option where opt is only evaluated if filter f, +// FilterValues returns a new [Option] where opt is only evaluated if filter f, // which is a function of the form "func(T, T) bool", returns true for the // current pair of values being compared. If either value is invalid or // the type of the values is not assignable to T, then this filter implicitly @@ -154,8 +154,8 @@ func (f pathFilter) String() string { // If T is an interface, it is possible that f is called with two values with // different concrete types that both implement T. // -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. +// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or +// a previously filtered [Option]. func FilterValues(f interface{}, opt Option) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { @@ -192,9 +192,9 @@ func (f valuesFilter) String() string { return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) } -// Ignore is an Option that causes all comparisons to be ignored. -// This value is intended to be combined with FilterPath or FilterValues. -// It is an error to pass an unfiltered Ignore option to Equal. +// Ignore is an [Option] that causes all comparisons to be ignored. +// This value is intended to be combined with [FilterPath] or [FilterValues]. +// It is an error to pass an unfiltered Ignore option to [Equal]. func Ignore() Option { return ignore{} } type ignore struct{ core } @@ -234,6 +234,8 @@ func (validator) apply(s *state, vx, vy reflect.Value) { name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType if _, ok := reflect.New(t).Interface().(error); ok { help = "consider using cmpopts.EquateErrors to compare error values" + } else if t.Comparable() { + help = "consider using cmpopts.EquateComparable to compare comparable Go types" } } else { // Unnamed type with unexported fields. Derive PkgPath from field. @@ -254,7 +256,7 @@ const identRx = `[_\p{L}][_\p{L}\p{N}]*` var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) -// Transformer returns an Option that applies a transformation function that +// Transformer returns an [Option] that applies a transformation function that // converts values of a certain type into that of another. // // The transformer f must be a function "func(T) R" that converts values of @@ -265,13 +267,14 @@ var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) // same transform to the output of itself (e.g., in the case where the // input and output types are the same), an implicit filter is added such that // a transformer is applicable only if that exact transformer is not already -// in the tail of the Path since the last non-Transform step. +// in the tail of the [Path] since the last non-[Transform] step. // For situations where the implicit filter is still insufficient, -// consider using cmpopts.AcyclicTransformer, which adds a filter -// to prevent the transformer from being recursively applied upon itself. +// consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer], +// which adds a filter to prevent the transformer from +// being recursively applied upon itself. // -// The name is a user provided label that is used as the Transform.Name in the -// transformation PathStep (and eventually shown in the Diff output). +// The name is a user provided label that is used as the [Transform.Name] in the +// transformation [PathStep] (and eventually shown in the [Diff] output). // The name must be a valid identifier or qualified identifier in Go syntax. // If empty, an arbitrary name is used. func Transformer(name string, f interface{}) Option { @@ -329,7 +332,7 @@ func (tr transformer) String() string { return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) } -// Comparer returns an Option that determines whether two values are equal +// Comparer returns an [Option] that determines whether two values are equal // to each other. // // The comparer f must be a function "func(T, T) bool" and is implicitly @@ -377,35 +380,32 @@ func (cm comparer) String() string { return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) } -// Exporter returns an Option that specifies whether Equal is allowed to +// Exporter returns an [Option] that specifies whether [Equal] is allowed to // introspect into the unexported fields of certain struct types. // // Users of this option must understand that comparing on unexported fields // from external packages is not safe since changes in the internal -// implementation of some external package may cause the result of Equal +// implementation of some external package may cause the result of [Equal] // to unexpectedly change. However, it may be valid to use this option on types // defined in an internal package where the semantic meaning of an unexported // field is in the control of the user. // -// In many cases, a custom Comparer should be used instead that defines +// In many cases, a custom [Comparer] should be used instead that defines // equality as a function of the public API of a type rather than the underlying // unexported implementation. // -// For example, the reflect.Type documentation defines equality to be determined +// For example, the [reflect.Type] documentation defines equality to be determined // by the == operator on the interface (essentially performing a shallow pointer -// comparison) and most attempts to compare *regexp.Regexp types are interested +// comparison) and most attempts to compare *[regexp.Regexp] types are interested // in only checking that the regular expression strings are equal. -// Both of these are accomplished using Comparers: +// Both of these are accomplished using [Comparer] options: // // Comparer(func(x, y reflect.Type) bool { return x == y }) // Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) // -// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore -// all unexported fields on specified struct types. +// In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported] +// option can be used to ignore all unexported fields on specified struct types. func Exporter(f func(reflect.Type) bool) Option { - if !supportExporters { - panic("Exporter is not supported on purego builds") - } return exporter(f) } @@ -415,10 +415,10 @@ func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableO panic("not implemented") } -// AllowUnexported returns an Options that allows Equal to forcibly introspect +// AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect // unexported fields of the specified struct types. // -// See Exporter for the proper use of this option. +// See [Exporter] for the proper use of this option. func AllowUnexported(types ...interface{}) Option { m := make(map[reflect.Type]bool) for _, typ := range types { @@ -432,7 +432,7 @@ func AllowUnexported(types ...interface{}) Option { } // Result represents the comparison result for a single node and -// is provided by cmp when calling Report (see Reporter). +// is provided by cmp when calling Report (see [Reporter]). type Result struct { _ [0]func() // Make Result incomparable flags resultFlags @@ -445,7 +445,7 @@ func (r Result) Equal() bool { } // ByIgnore reports whether the node is equal because it was ignored. -// This never reports true if Equal reports false. +// This never reports true if [Result.Equal] reports false. func (r Result) ByIgnore() bool { return r.flags&reportByIgnore != 0 } @@ -455,7 +455,7 @@ func (r Result) ByMethod() bool { return r.flags&reportByMethod != 0 } -// ByFunc reports whether a Comparer function determined equality. +// ByFunc reports whether a [Comparer] function determined equality. func (r Result) ByFunc() bool { return r.flags&reportByFunc != 0 } @@ -478,7 +478,7 @@ const ( reportByCycle ) -// Reporter is an Option that can be passed to Equal. When Equal traverses +// Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses // the value trees, it calls PushStep as it descends into each node in the // tree and PopStep as it ascend out of the node. The leaves of the tree are // either compared (determined to be equal or not equal) or ignored and reported diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go index a0a588502..c3c145642 100644 --- a/vendor/github.com/google/go-cmp/cmp/path.go +++ b/vendor/github.com/google/go-cmp/cmp/path.go @@ -14,9 +14,9 @@ import ( "github.com/google/go-cmp/cmp/internal/value" ) -// Path is a list of PathSteps describing the sequence of operations to get +// Path is a list of [PathStep] describing the sequence of operations to get // from some root type to the current position in the value tree. -// The first Path element is always an operation-less PathStep that exists +// The first Path element is always an operation-less [PathStep] that exists // simply to identify the initial type. // // When traversing structs with embedded structs, the embedded struct will @@ -29,8 +29,13 @@ type Path []PathStep // a value's tree structure. Users of this package never need to implement // these types as values of this type will be returned by this package. // -// Implementations of this interface are -// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. +// Implementations of this interface: +// - [StructField] +// - [SliceIndex] +// - [MapIndex] +// - [Indirect] +// - [TypeAssertion] +// - [Transform] type PathStep interface { String() string @@ -70,8 +75,9 @@ func (pa *Path) pop() { *pa = (*pa)[:len(*pa)-1] } -// Last returns the last PathStep in the Path. -// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +// Last returns the last [PathStep] in the Path. +// If the path is empty, this returns a non-nil [PathStep] +// that reports a nil [PathStep.Type]. func (pa Path) Last() PathStep { return pa.Index(-1) } @@ -79,7 +85,8 @@ func (pa Path) Last() PathStep { // Index returns the ith step in the Path and supports negative indexing. // A negative index starts counting from the tail of the Path such that -1 // refers to the last step, -2 refers to the second-to-last step, and so on. -// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +// If index is invalid, this returns a non-nil [PathStep] +// that reports a nil [PathStep.Type]. func (pa Path) Index(i int) PathStep { if i < 0 { i = len(pa) + i @@ -168,7 +175,8 @@ func (ps pathStep) String() string { return fmt.Sprintf("{%s}", s) } -// StructField represents a struct field access on a field called Name. +// StructField is a [PathStep] that represents a struct field access +// on a field called [StructField.Name]. type StructField struct{ *structField } type structField struct { pathStep @@ -204,10 +212,11 @@ func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } func (sf StructField) Name() string { return sf.name } // Index is the index of the field in the parent struct type. -// See reflect.Type.Field. +// See [reflect.Type.Field]. func (sf StructField) Index() int { return sf.idx } -// SliceIndex is an index operation on a slice or array at some index Key. +// SliceIndex is a [PathStep] that represents an index operation on +// a slice or array at some index [SliceIndex.Key]. type SliceIndex struct{ *sliceIndex } type sliceIndex struct { pathStep @@ -247,12 +256,12 @@ func (si SliceIndex) Key() int { // all of the indexes to be shifted. If an index is -1, then that // indicates that the element does not exist in the associated slice. // -// Key is guaranteed to return -1 if and only if the indexes returned -// by SplitKeys are not the same. SplitKeys will never return -1 for +// [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes +// returned by SplitKeys are not the same. SplitKeys will never return -1 for // both indexes. func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } -// MapIndex is an index operation on a map at some index Key. +// MapIndex is a [PathStep] that represents an index operation on a map at some index Key. type MapIndex struct{ *mapIndex } type mapIndex struct { pathStep @@ -266,7 +275,7 @@ func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", // Key is the value of the map key. func (mi MapIndex) Key() reflect.Value { return mi.key } -// Indirect represents pointer indirection on the parent type. +// Indirect is a [PathStep] that represents pointer indirection on the parent type. type Indirect struct{ *indirect } type indirect struct { pathStep @@ -276,7 +285,7 @@ func (in Indirect) Type() reflect.Type { return in.typ } func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } func (in Indirect) String() string { return "*" } -// TypeAssertion represents a type assertion on an interface. +// TypeAssertion is a [PathStep] that represents a type assertion on an interface. type TypeAssertion struct{ *typeAssertion } type typeAssertion struct { pathStep @@ -286,7 +295,8 @@ func (ta TypeAssertion) Type() reflect.Type { return ta.typ } func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } -// Transform is a transformation from the parent type to the current type. +// Transform is a [PathStep] that represents a transformation +// from the parent type to the current type. type Transform struct{ *transform } type transform struct { pathStep @@ -297,13 +307,13 @@ func (tf Transform) Type() reflect.Type { return tf.typ } func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } -// Name is the name of the Transformer. +// Name is the name of the [Transformer]. func (tf Transform) Name() string { return tf.trans.name } // Func is the function pointer to the transformer function. func (tf Transform) Func() reflect.Value { return tf.trans.fnc } -// Option returns the originally constructed Transformer option. +// Option returns the originally constructed [Transformer] option. // The == operator can be used to detect the exact option used. func (tf Transform) Option() Option { return tf.trans } diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go index 2ab41fad3..e39f42284 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -199,7 +199,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, break } sf := t.Field(i) - if supportExporters && !isExported(sf.Name) { + if !isExported(sf.Name) { vv = retrieveUnexportedField(v, sf, true) } s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) diff --git a/vendor/github.com/google/pprof/AUTHORS b/vendor/github.com/google/pprof/AUTHORS new file mode 100644 index 000000000..fd736cb1c --- /dev/null +++ b/vendor/github.com/google/pprof/AUTHORS @@ -0,0 +1,7 @@ +# This is the official list of pprof authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. +Google Inc. \ No newline at end of file diff --git a/vendor/github.com/google/pprof/CONTRIBUTORS b/vendor/github.com/google/pprof/CONTRIBUTORS new file mode 100644 index 000000000..8c8c37d2c --- /dev/null +++ b/vendor/github.com/google/pprof/CONTRIBUTORS @@ -0,0 +1,16 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name +Raul Silvera +Tipp Moseley +Hyoun Kyu Cho +Martin Spier +Taco de Wolff +Andrew Hunter diff --git a/vendor/google.golang.org/genproto/LICENSE b/vendor/github.com/google/pprof/LICENSE similarity index 100% rename from vendor/google.golang.org/genproto/LICENSE rename to vendor/github.com/google/pprof/LICENSE diff --git a/vendor/github.com/google/pprof/profile/encode.go b/vendor/github.com/google/pprof/profile/encode.go new file mode 100644 index 000000000..182c926b9 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/encode.go @@ -0,0 +1,588 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package profile + +import ( + "errors" + "sort" + "strings" +) + +func (p *Profile) decoder() []decoder { + return profileDecoder +} + +// preEncode populates the unexported fields to be used by encode +// (with suffix X) from the corresponding exported fields. The +// exported fields are cleared up to facilitate testing. +func (p *Profile) preEncode() { + strings := make(map[string]int) + addString(strings, "") + + for _, st := range p.SampleType { + st.typeX = addString(strings, st.Type) + st.unitX = addString(strings, st.Unit) + } + + for _, s := range p.Sample { + s.labelX = nil + var keys []string + for k := range s.Label { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := s.Label[k] + for _, v := range vs { + s.labelX = append(s.labelX, + label{ + keyX: addString(strings, k), + strX: addString(strings, v), + }, + ) + } + } + var numKeys []string + for k := range s.NumLabel { + numKeys = append(numKeys, k) + } + sort.Strings(numKeys) + for _, k := range numKeys { + keyX := addString(strings, k) + vs := s.NumLabel[k] + units := s.NumUnit[k] + for i, v := range vs { + var unitX int64 + if len(units) != 0 { + unitX = addString(strings, units[i]) + } + s.labelX = append(s.labelX, + label{ + keyX: keyX, + numX: v, + unitX: unitX, + }, + ) + } + } + s.locationIDX = make([]uint64, len(s.Location)) + for i, loc := range s.Location { + s.locationIDX[i] = loc.ID + } + } + + for _, m := range p.Mapping { + m.fileX = addString(strings, m.File) + m.buildIDX = addString(strings, m.BuildID) + } + + for _, l := range p.Location { + for i, ln := range l.Line { + if ln.Function != nil { + l.Line[i].functionIDX = ln.Function.ID + } else { + l.Line[i].functionIDX = 0 + } + } + if l.Mapping != nil { + l.mappingIDX = l.Mapping.ID + } else { + l.mappingIDX = 0 + } + } + for _, f := range p.Function { + f.nameX = addString(strings, f.Name) + f.systemNameX = addString(strings, f.SystemName) + f.filenameX = addString(strings, f.Filename) + } + + p.dropFramesX = addString(strings, p.DropFrames) + p.keepFramesX = addString(strings, p.KeepFrames) + + if pt := p.PeriodType; pt != nil { + pt.typeX = addString(strings, pt.Type) + pt.unitX = addString(strings, pt.Unit) + } + + p.commentX = nil + for _, c := range p.Comments { + p.commentX = append(p.commentX, addString(strings, c)) + } + + p.defaultSampleTypeX = addString(strings, p.DefaultSampleType) + + p.stringTable = make([]string, len(strings)) + for s, i := range strings { + p.stringTable[i] = s + } +} + +func (p *Profile) encode(b *buffer) { + for _, x := range p.SampleType { + encodeMessage(b, 1, x) + } + for _, x := range p.Sample { + encodeMessage(b, 2, x) + } + for _, x := range p.Mapping { + encodeMessage(b, 3, x) + } + for _, x := range p.Location { + encodeMessage(b, 4, x) + } + for _, x := range p.Function { + encodeMessage(b, 5, x) + } + encodeStrings(b, 6, p.stringTable) + encodeInt64Opt(b, 7, p.dropFramesX) + encodeInt64Opt(b, 8, p.keepFramesX) + encodeInt64Opt(b, 9, p.TimeNanos) + encodeInt64Opt(b, 10, p.DurationNanos) + if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { + encodeMessage(b, 11, p.PeriodType) + } + encodeInt64Opt(b, 12, p.Period) + encodeInt64s(b, 13, p.commentX) + encodeInt64(b, 14, p.defaultSampleTypeX) +} + +var profileDecoder = []decoder{ + nil, // 0 + // repeated ValueType sample_type = 1 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.SampleType = append(pp.SampleType, x) + return decodeMessage(b, x) + }, + // repeated Sample sample = 2 + func(b *buffer, m message) error { + x := new(Sample) + pp := m.(*Profile) + pp.Sample = append(pp.Sample, x) + return decodeMessage(b, x) + }, + // repeated Mapping mapping = 3 + func(b *buffer, m message) error { + x := new(Mapping) + pp := m.(*Profile) + pp.Mapping = append(pp.Mapping, x) + return decodeMessage(b, x) + }, + // repeated Location location = 4 + func(b *buffer, m message) error { + x := new(Location) + x.Line = b.tmpLines[:0] // Use shared space temporarily + pp := m.(*Profile) + pp.Location = append(pp.Location, x) + err := decodeMessage(b, x) + b.tmpLines = x.Line[:0] + // Copy to shrink size and detach from shared space. + x.Line = append([]Line(nil), x.Line...) + return err + }, + // repeated Function function = 5 + func(b *buffer, m message) error { + x := new(Function) + pp := m.(*Profile) + pp.Function = append(pp.Function, x) + return decodeMessage(b, x) + }, + // repeated string string_table = 6 + func(b *buffer, m message) error { + err := decodeStrings(b, &m.(*Profile).stringTable) + if err != nil { + return err + } + if m.(*Profile).stringTable[0] != "" { + return errors.New("string_table[0] must be ''") + } + return nil + }, + // int64 drop_frames = 7 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, + // int64 keep_frames = 8 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, + // int64 time_nanos = 9 + func(b *buffer, m message) error { + if m.(*Profile).TimeNanos != 0 { + return errConcatProfile + } + return decodeInt64(b, &m.(*Profile).TimeNanos) + }, + // int64 duration_nanos = 10 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, + // ValueType period_type = 11 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.PeriodType = x + return decodeMessage(b, x) + }, + // int64 period = 12 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, + // repeated int64 comment = 13 + 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) }, +} + +// postDecode takes the unexported fields populated by decode (with +// suffix X) and populates the corresponding exported fields. +// The unexported fields are cleared up to facilitate testing. +func (p *Profile) postDecode() error { + var err error + mappings := make(map[uint64]*Mapping, len(p.Mapping)) + mappingIds := make([]*Mapping, len(p.Mapping)+1) + for _, m := range p.Mapping { + m.File, err = getString(p.stringTable, &m.fileX, err) + m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) + if m.ID < uint64(len(mappingIds)) { + mappingIds[m.ID] = m + } else { + mappings[m.ID] = m + } + + // If this a main linux kernel mapping with a relocation symbol suffix + // ("[kernel.kallsyms]_text"), extract said suffix. + // It is fairly hacky to handle at this level, but the alternatives appear even worse. + const prefix = "[kernel.kallsyms]" + if strings.HasPrefix(m.File, prefix) { + m.KernelRelocationSymbol = m.File[len(prefix):] + } + } + + functions := make(map[uint64]*Function, len(p.Function)) + functionIds := make([]*Function, len(p.Function)+1) + for _, f := range p.Function { + f.Name, err = getString(p.stringTable, &f.nameX, err) + f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) + f.Filename, err = getString(p.stringTable, &f.filenameX, err) + if f.ID < uint64(len(functionIds)) { + functionIds[f.ID] = f + } else { + functions[f.ID] = f + } + } + + locations := make(map[uint64]*Location, len(p.Location)) + locationIds := make([]*Location, len(p.Location)+1) + for _, l := range p.Location { + if id := l.mappingIDX; id < uint64(len(mappingIds)) { + l.Mapping = mappingIds[id] + } else { + l.Mapping = mappings[id] + } + l.mappingIDX = 0 + for i, ln := range l.Line { + if id := ln.functionIDX; id != 0 { + l.Line[i].functionIDX = 0 + if id < uint64(len(functionIds)) { + l.Line[i].Function = functionIds[id] + } else { + l.Line[i].Function = functions[id] + } + } + } + if l.ID < uint64(len(locationIds)) { + locationIds[l.ID] = l + } else { + locations[l.ID] = l + } + } + + for _, st := range p.SampleType { + st.Type, err = getString(p.stringTable, &st.typeX, err) + st.Unit, err = getString(p.stringTable, &st.unitX, err) + } + + // Pre-allocate space for all locations. + numLocations := 0 + for _, s := range p.Sample { + numLocations += len(s.locationIDX) + } + locBuffer := make([]*Location, numLocations) + + for _, s := range p.Sample { + if len(s.labelX) > 0 { + labels := make(map[string][]string, len(s.labelX)) + numLabels := make(map[string][]int64, len(s.labelX)) + numUnits := make(map[string][]string, len(s.labelX)) + for _, l := range s.labelX { + var key, value string + key, err = getString(p.stringTable, &l.keyX, err) + if l.strX != 0 { + value, err = getString(p.stringTable, &l.strX, err) + labels[key] = append(labels[key], value) + } else if l.numX != 0 || l.unitX != 0 { + numValues := numLabels[key] + units := numUnits[key] + if l.unitX != 0 { + var unit string + unit, err = getString(p.stringTable, &l.unitX, err) + units = padStringArray(units, len(numValues)) + numUnits[key] = append(units, unit) + } + numLabels[key] = append(numLabels[key], l.numX) + } + } + if len(labels) > 0 { + s.Label = labels + } + if len(numLabels) > 0 { + s.NumLabel = numLabels + for key, units := range numUnits { + if len(units) > 0 { + numUnits[key] = padStringArray(units, len(numLabels[key])) + } + } + s.NumUnit = numUnits + } + } + + s.Location = locBuffer[:len(s.locationIDX)] + locBuffer = locBuffer[len(s.locationIDX):] + for i, lid := range s.locationIDX { + if lid < uint64(len(locationIds)) { + s.Location[i] = locationIds[lid] + } else { + s.Location[i] = locations[lid] + } + } + s.locationIDX = nil + } + + p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) + p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) + + if pt := p.PeriodType; pt == nil { + p.PeriodType = &ValueType{} + } + + if pt := p.PeriodType; pt != nil { + pt.Type, err = getString(p.stringTable, &pt.typeX, err) + pt.Unit, err = getString(p.stringTable, &pt.unitX, err) + } + + for _, i := range p.commentX { + var c string + c, err = getString(p.stringTable, &i, err) + p.Comments = append(p.Comments, c) + } + + p.commentX = nil + p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) + p.stringTable = nil + return err +} + +// padStringArray pads arr with enough empty strings to make arr +// length l when arr's length is less than l. +func padStringArray(arr []string, l int) []string { + if l <= len(arr) { + return arr + } + return append(arr, make([]string, l-len(arr))...) +} + +func (p *ValueType) decoder() []decoder { + return valueTypeDecoder +} + +func (p *ValueType) encode(b *buffer) { + encodeInt64Opt(b, 1, p.typeX) + encodeInt64Opt(b, 2, p.unitX) +} + +var valueTypeDecoder = []decoder{ + nil, // 0 + // optional int64 type = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, + // optional int64 unit = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, +} + +func (p *Sample) decoder() []decoder { + return sampleDecoder +} + +func (p *Sample) encode(b *buffer) { + encodeUint64s(b, 1, p.locationIDX) + encodeInt64s(b, 2, p.Value) + for _, x := range p.labelX { + encodeMessage(b, 3, x) + } +} + +var sampleDecoder = []decoder{ + nil, // 0 + // repeated uint64 location = 1 + func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, + // repeated int64 value = 2 + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, + // repeated Label label = 3 + func(b *buffer, m message) error { + s := m.(*Sample) + n := len(s.labelX) + s.labelX = append(s.labelX, label{}) + return decodeMessage(b, &s.labelX[n]) + }, +} + +func (p label) decoder() []decoder { + return labelDecoder +} + +func (p label) encode(b *buffer) { + encodeInt64Opt(b, 1, p.keyX) + encodeInt64Opt(b, 2, p.strX) + encodeInt64Opt(b, 3, p.numX) + encodeInt64Opt(b, 4, p.unitX) +} + +var labelDecoder = []decoder{ + nil, // 0 + // optional int64 key = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).keyX) }, + // optional int64 str = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).strX) }, + // optional int64 num = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).numX) }, + // optional int64 num = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).unitX) }, +} + +func (p *Mapping) decoder() []decoder { + return mappingDecoder +} + +func (p *Mapping) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.Start) + encodeUint64Opt(b, 3, p.Limit) + encodeUint64Opt(b, 4, p.Offset) + encodeInt64Opt(b, 5, p.fileX) + encodeInt64Opt(b, 6, p.buildIDX) + encodeBoolOpt(b, 7, p.HasFunctions) + encodeBoolOpt(b, 8, p.HasFilenames) + encodeBoolOpt(b, 9, p.HasLineNumbers) + encodeBoolOpt(b, 10, p.HasInlineFrames) +} + +var mappingDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 +} + +func (p *Location) decoder() []decoder { + return locationDecoder +} + +func (p *Location) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.mappingIDX) + encodeUint64Opt(b, 3, p.Address) + for i := range p.Line { + encodeMessage(b, 4, &p.Line[i]) + } + encodeBoolOpt(b, 5, p.IsFolded) +} + +var locationDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; + func(b *buffer, m message) error { // repeated Line line = 4 + pp := m.(*Location) + n := len(pp.Line) + pp.Line = append(pp.Line, Line{}) + return decodeMessage(b, &pp.Line[n]) + }, + func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5; +} + +func (p *Line) decoder() []decoder { + return lineDecoder +} + +func (p *Line) encode(b *buffer) { + encodeUint64Opt(b, 1, p.functionIDX) + encodeInt64Opt(b, 2, p.Line) +} + +var lineDecoder = []decoder{ + nil, // 0 + // optional uint64 function_id = 1 + 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) }, +} + +func (p *Function) decoder() []decoder { + return functionDecoder +} + +func (p *Function) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeInt64Opt(b, 2, p.nameX) + encodeInt64Opt(b, 3, p.systemNameX) + encodeInt64Opt(b, 4, p.filenameX) + encodeInt64Opt(b, 5, p.StartLine) +} + +var functionDecoder = []decoder{ + nil, // 0 + // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, + // optional int64 function_name = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, + // optional int64 function_system_name = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, + // repeated int64 filename = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, + // optional int64 start_line = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, +} + +func addString(strings map[string]int, s string) int64 { + i, ok := strings[s] + if !ok { + i = len(strings) + strings[s] = i + } + return int64(i) +} + +func getString(strings []string, strng *int64, err error) (string, error) { + if err != nil { + return "", err + } + s := int(*strng) + if s < 0 || s >= len(strings) { + return "", errMalformed + } + *strng = 0 + return strings[s], nil +} diff --git a/vendor/github.com/google/pprof/profile/filter.go b/vendor/github.com/google/pprof/profile/filter.go new file mode 100644 index 000000000..c794b9390 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/filter.go @@ -0,0 +1,274 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package profile + +// Implements methods to filter samples from profiles. + +import "regexp" + +// FilterSamplesByName filters the samples in a profile and only keeps +// samples where at least one frame matches focus but none match ignore. +// Returns true is the corresponding regexp matched at least one sample. +func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp.Regexp) (fm, im, hm, hnm bool) { + if focus == nil && ignore == nil && hide == nil && show == nil { + fm = true // Missing focus implies a match + return + } + focusOrIgnore := make(map[uint64]bool) + hidden := make(map[uint64]bool) + for _, l := range p.Location { + if ignore != nil && l.matchesName(ignore) { + im = true + focusOrIgnore[l.ID] = false + } else if focus == nil || l.matchesName(focus) { + fm = true + focusOrIgnore[l.ID] = true + } + + if hide != nil && l.matchesName(hide) { + hm = true + l.Line = l.unmatchedLines(hide) + if len(l.Line) == 0 { + hidden[l.ID] = true + } + } + if show != nil { + l.Line = l.matchedLines(show) + if len(l.Line) == 0 { + hidden[l.ID] = true + } else { + hnm = true + } + } + } + + s := make([]*Sample, 0, len(p.Sample)) + for _, sample := range p.Sample { + if focusedAndNotIgnored(sample.Location, focusOrIgnore) { + if len(hidden) > 0 { + var locs []*Location + for _, loc := range sample.Location { + if !hidden[loc.ID] { + locs = append(locs, loc) + } + } + if len(locs) == 0 { + // Remove sample with no locations (by not adding it to s). + continue + } + sample.Location = locs + } + s = append(s, sample) + } + } + p.Sample = s + + return +} + +// ShowFrom drops all stack frames above the highest matching frame and returns +// whether a match was found. If showFrom is nil it returns false and does not +// modify the profile. +// +// Example: consider a sample with frames [A, B, C, B], where A is the root. +// ShowFrom(nil) returns false and has frames [A, B, C, B]. +// ShowFrom(A) returns true and has frames [A, B, C, B]. +// ShowFrom(B) returns true and has frames [B, C, B]. +// ShowFrom(C) returns true and has frames [C, B]. +// ShowFrom(D) returns false and drops the sample because no frames remain. +func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) { + if showFrom == nil { + return false + } + // showFromLocs stores location IDs that matched ShowFrom. + showFromLocs := make(map[uint64]bool) + // Apply to locations. + for _, loc := range p.Location { + if filterShowFromLocation(loc, showFrom) { + showFromLocs[loc.ID] = true + matched = true + } + } + // For all samples, strip locations after the highest matching one. + s := make([]*Sample, 0, len(p.Sample)) + for _, sample := range p.Sample { + for i := len(sample.Location) - 1; i >= 0; i-- { + if showFromLocs[sample.Location[i].ID] { + sample.Location = sample.Location[:i+1] + s = append(s, sample) + break + } + } + } + p.Sample = s + return matched +} + +// filterShowFromLocation tests a showFrom regex against a location, removes +// lines after the last match and returns whether a match was found. If the +// mapping is matched, then all lines are kept. +func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool { + if m := loc.Mapping; m != nil && showFrom.MatchString(m.File) { + return true + } + if i := loc.lastMatchedLineIndex(showFrom); i >= 0 { + loc.Line = loc.Line[:i+1] + return true + } + return false +} + +// lastMatchedLineIndex returns the index of the last line that matches a regex, +// or -1 if no match is found. +func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int { + for i := len(loc.Line) - 1; i >= 0; i-- { + if fn := loc.Line[i].Function; fn != nil { + if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { + return i + } + } + } + return -1 +} + +// FilterTagsByName filters the tags in a profile and only keeps +// tags that match show and not hide. +func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bool) { + matchRemove := func(name string) bool { + matchShow := show == nil || show.MatchString(name) + matchHide := hide != nil && hide.MatchString(name) + + if matchShow { + sm = true + } + if matchHide { + hm = true + } + return !matchShow || matchHide + } + for _, s := range p.Sample { + for lab := range s.Label { + if matchRemove(lab) { + delete(s.Label, lab) + } + } + for lab := range s.NumLabel { + if matchRemove(lab) { + delete(s.NumLabel, lab) + } + } + } + return +} + +// matchesName returns whether the location matches the regular +// expression. It checks any available function names, file names, and +// mapping object filename. +func (loc *Location) matchesName(re *regexp.Regexp) bool { + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { + return true + } + } + } + if m := loc.Mapping; m != nil && re.MatchString(m.File) { + return true + } + return false +} + +// unmatchedLines returns the lines in the location that do not match +// the regular expression. +func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { + if m := loc.Mapping; m != nil && re.MatchString(m.File) { + return nil + } + var lines []Line + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { + continue + } + } + lines = append(lines, ln) + } + return lines +} + +// matchedLines returns the lines in the location that match +// the regular expression. +func (loc *Location) matchedLines(re *regexp.Regexp) []Line { + if m := loc.Mapping; m != nil && re.MatchString(m.File) { + return loc.Line + } + var lines []Line + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if !re.MatchString(fn.Name) && !re.MatchString(fn.Filename) { + continue + } + } + lines = append(lines, ln) + } + return lines +} + +// focusedAndNotIgnored looks up a slice of ids against a map of +// focused/ignored locations. The map only contains locations that are +// explicitly focused or ignored. Returns whether there is at least +// one focused location but no ignored locations. +func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { + var f bool + for _, loc := range locs { + if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { + if focus { + // Found focused location. Must keep searching in case there + // is an ignored one as well. + f = true + } else { + // Found ignored location. Can return false right away. + return false + } + } + } + return f +} + +// TagMatch selects tags for filtering +type TagMatch func(s *Sample) bool + +// FilterSamplesByTag removes all samples from the profile, except +// those that match focus and do not match the ignore regular +// expression. +func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { + samples := make([]*Sample, 0, len(p.Sample)) + for _, s := range p.Sample { + focused, ignored := true, false + if focus != nil { + focused = focus(s) + } + if ignore != nil { + ignored = ignore(s) + } + fm = fm || focused + im = im || ignored + if focused && !ignored { + samples = append(samples, s) + } + } + p.Sample = samples + return +} diff --git a/vendor/github.com/google/pprof/profile/index.go b/vendor/github.com/google/pprof/profile/index.go new file mode 100644 index 000000000..bef1d6046 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/index.go @@ -0,0 +1,64 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package profile + +import ( + "fmt" + "strconv" + "strings" +) + +// SampleIndexByName returns the appropriate index for a value of sample index. +// If numeric, it returns the number, otherwise it looks up the text in the +// profile sample types. +func (p *Profile) SampleIndexByName(sampleIndex string) (int, error) { + if sampleIndex == "" { + if dst := p.DefaultSampleType; dst != "" { + for i, t := range sampleTypes(p) { + if t == dst { + return i, nil + } + } + } + // By default select the last sample value + return len(p.SampleType) - 1, nil + } + if i, err := strconv.Atoi(sampleIndex); err == nil { + if i < 0 || i >= len(p.SampleType) { + return 0, fmt.Errorf("sample_index %s is outside the range [0..%d]", sampleIndex, len(p.SampleType)-1) + } + return i, nil + } + + // Remove the inuse_ prefix to support legacy pprof options + // "inuse_space" and "inuse_objects" for profiles containing types + // "space" and "objects". + noInuse := strings.TrimPrefix(sampleIndex, "inuse_") + for i, t := range p.SampleType { + if t.Type == sampleIndex || t.Type == noInuse { + return i, nil + } + } + + return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes(p)) +} + +func sampleTypes(p *Profile) []string { + types := make([]string, len(p.SampleType)) + for i, t := range p.SampleType { + types[i] = t.Type + } + return types +} diff --git a/vendor/github.com/google/pprof/profile/legacy_java_profile.go b/vendor/github.com/google/pprof/profile/legacy_java_profile.go new file mode 100644 index 000000000..91f45e53c --- /dev/null +++ b/vendor/github.com/google/pprof/profile/legacy_java_profile.go @@ -0,0 +1,315 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements parsers to convert java legacy profiles into +// the profile.proto format. + +package profile + +import ( + "bytes" + "fmt" + "io" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +var ( + attributeRx = regexp.MustCompile(`([\w ]+)=([\w ]+)`) + javaSampleRx = regexp.MustCompile(` *(\d+) +(\d+) +@ +([ x0-9a-f]*)`) + javaLocationRx = regexp.MustCompile(`^\s*0x([[:xdigit:]]+)\s+(.*)\s*$`) + javaLocationFileLineRx = regexp.MustCompile(`^(.*)\s+\((.+):(-?[[:digit:]]+)\)$`) + javaLocationPathRx = regexp.MustCompile(`^(.*)\s+\((.*)\)$`) +) + +// javaCPUProfile returns a new Profile from profilez data. +// b is the profile bytes after the header, period is the profiling +// period, and parse is a function to parse 8-byte chunks from the +// profile in its native endianness. +func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { + p := &Profile{ + Period: period * 1000, + PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, + SampleType: []*ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}}, + } + var err error + var locs map[uint64]*Location + if b, locs, err = parseCPUSamples(b, parse, false, p); err != nil { + return nil, err + } + + if err = parseJavaLocations(b, locs, p); err != nil { + return nil, err + } + + // Strip out addresses for better merge. + if err = p.Aggregate(true, true, true, true, false); err != nil { + return nil, err + } + + return p, nil +} + +// parseJavaProfile returns a new profile from heapz or contentionz +// data. b is the profile bytes after the header. +func parseJavaProfile(b []byte) (*Profile, error) { + h := bytes.SplitAfterN(b, []byte("\n"), 2) + if len(h) < 2 { + return nil, errUnrecognized + } + + p := &Profile{ + PeriodType: &ValueType{}, + } + header := string(bytes.TrimSpace(h[0])) + + var err error + var pType string + switch header { + case "--- heapz 1 ---": + pType = "heap" + case "--- contentionz 1 ---": + pType = "contention" + default: + return nil, errUnrecognized + } + + if b, err = parseJavaHeader(pType, h[1], p); err != nil { + return nil, err + } + var locs map[uint64]*Location + if b, locs, err = parseJavaSamples(pType, b, p); err != nil { + return nil, err + } + if err = parseJavaLocations(b, locs, p); err != nil { + return nil, err + } + + // Strip out addresses for better merge. + if err = p.Aggregate(true, true, true, true, false); err != nil { + return nil, err + } + + return p, nil +} + +// parseJavaHeader parses the attribute section on a java profile and +// populates a profile. Returns the remainder of the buffer after all +// attributes. +func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) { + nextNewLine := bytes.IndexByte(b, byte('\n')) + for nextNewLine != -1 { + line := string(bytes.TrimSpace(b[0:nextNewLine])) + if line != "" { + h := attributeRx.FindStringSubmatch(line) + if h == nil { + // Not a valid attribute, exit. + return b, nil + } + + attribute, value := strings.TrimSpace(h[1]), strings.TrimSpace(h[2]) + var err error + switch pType + "/" + attribute { + case "heap/format", "cpu/format", "contention/format": + if value != "java" { + return nil, errUnrecognized + } + case "heap/resolution": + p.SampleType = []*ValueType{ + {Type: "inuse_objects", Unit: "count"}, + {Type: "inuse_space", Unit: value}, + } + case "contention/resolution": + p.SampleType = []*ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: value}, + } + case "contention/sampling period": + p.PeriodType = &ValueType{ + Type: "contentions", Unit: "count", + } + if p.Period, err = strconv.ParseInt(value, 0, 64); err != nil { + return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) + } + case "contention/ms since reset": + millis, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) + } + p.DurationNanos = millis * 1000 * 1000 + default: + return nil, errUnrecognized + } + } + // Grab next line. + b = b[nextNewLine+1:] + nextNewLine = bytes.IndexByte(b, byte('\n')) + } + return b, nil +} + +// parseJavaSamples parses the samples from a java profile and +// populates the Samples in a profile. Returns the remainder of the +// buffer after the samples. +func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*Location, error) { + nextNewLine := bytes.IndexByte(b, byte('\n')) + locs := make(map[uint64]*Location) + for nextNewLine != -1 { + line := string(bytes.TrimSpace(b[0:nextNewLine])) + if line != "" { + sample := javaSampleRx.FindStringSubmatch(line) + if sample == nil { + // Not a valid sample, exit. + return b, locs, nil + } + + // Java profiles have data/fields inverted compared to other + // profile types. + var err error + value1, value2, value3 := sample[2], sample[1], sample[3] + addrs, err := parseHexAddresses(value3) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + var sloc []*Location + for _, addr := range addrs { + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + s := &Sample{ + Value: make([]int64, 2), + Location: sloc, + } + + if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil { + return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) + } + if s.Value[1], err = strconv.ParseInt(value2, 0, 64); err != nil { + return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) + } + + switch pType { + case "heap": + const javaHeapzSamplingRate = 524288 // 512K + if s.Value[0] == 0 { + return nil, nil, fmt.Errorf("parsing sample %s: second value must be non-zero", line) + } + s.NumLabel = map[string][]int64{"bytes": {s.Value[1] / s.Value[0]}} + s.Value[0], s.Value[1] = scaleHeapSample(s.Value[0], s.Value[1], javaHeapzSamplingRate) + case "contention": + if period := p.Period; period != 0 { + s.Value[0] = s.Value[0] * p.Period + s.Value[1] = s.Value[1] * p.Period + } + } + p.Sample = append(p.Sample, s) + } + // Grab next line. + b = b[nextNewLine+1:] + nextNewLine = bytes.IndexByte(b, byte('\n')) + } + return b, locs, nil +} + +// parseJavaLocations parses the location information in a java +// profile and populates the Locations in a profile. It uses the +// location addresses from the profile as both the ID of each +// location. +func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile) error { + r := bytes.NewBuffer(b) + fns := make(map[string]*Function) + for { + line, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + return err + } + if line == "" { + break + } + } + + if line = strings.TrimSpace(line); line == "" { + continue + } + + jloc := javaLocationRx.FindStringSubmatch(line) + if len(jloc) != 3 { + continue + } + addr, err := strconv.ParseUint(jloc[1], 16, 64) + if err != nil { + return fmt.Errorf("parsing sample %s: %v", line, err) + } + loc := locs[addr] + if loc == nil { + // Unused/unseen + continue + } + var lineFunc, lineFile string + var lineNo int64 + + if fileLine := javaLocationFileLineRx.FindStringSubmatch(jloc[2]); len(fileLine) == 4 { + // Found a line of the form: "function (file:line)" + lineFunc, lineFile = fileLine[1], fileLine[2] + if n, err := strconv.ParseInt(fileLine[3], 10, 64); err == nil && n > 0 { + lineNo = n + } + } else if filePath := javaLocationPathRx.FindStringSubmatch(jloc[2]); len(filePath) == 3 { + // If there's not a file:line, it's a shared library path. + // The path isn't interesting, so just give the .so. + lineFunc, lineFile = filePath[1], filepath.Base(filePath[2]) + } else if strings.Contains(jloc[2], "generated stub/JIT") { + lineFunc = "STUB" + } else { + // Treat whole line as the function name. This is used by the + // java agent for internal states such as "GC" or "VM". + lineFunc = jloc[2] + } + fn := fns[lineFunc] + + if fn == nil { + fn = &Function{ + Name: lineFunc, + SystemName: lineFunc, + Filename: lineFile, + } + fns[lineFunc] = fn + p.Function = append(p.Function, fn) + } + loc.Line = []Line{ + { + Function: fn, + Line: lineNo, + }, + } + loc.Address = 0 + } + + p.remapLocationIDs() + p.remapFunctionIDs() + p.remapMappingIDs() + + return nil +} diff --git a/vendor/github.com/google/pprof/profile/legacy_profile.go b/vendor/github.com/google/pprof/profile/legacy_profile.go new file mode 100644 index 000000000..8d07fd6c2 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/legacy_profile.go @@ -0,0 +1,1228 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements parsers to convert legacy profiles into the +// profile.proto format. + +package profile + +import ( + "bufio" + "bytes" + "fmt" + "io" + "math" + "regexp" + "strconv" + "strings" +) + +var ( + countStartRE = regexp.MustCompile(`\A(\S+) profile: total \d+\z`) + countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\z`) + + heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) + heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) + + contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`) + + hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`) + + growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz?`) + + fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz?`) + + threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`) + threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) + + // Regular expressions to parse process mappings. Support the format used by Linux /proc/.../maps and other tools. + // Recommended format: + // Start End object file name offset(optional) linker build id + // 0x40000-0x80000 /path/to/binary (@FF00) abc123456 + spaceDigits = `\s+[[:digit:]]+` + hexPair = `\s+[[:xdigit:]]+:[[:xdigit:]]+` + oSpace = `\s*` + // Capturing expressions. + cHex = `(?:0x)?([[:xdigit:]]+)` + cHexRange = `\s*` + cHex + `[\s-]?` + oSpace + cHex + `:?` + cSpaceString = `(?:\s+(\S+))?` + cSpaceHex = `(?:\s+([[:xdigit:]]+))?` + cSpaceAtOffset = `(?:\s+\(@([[:xdigit:]]+)\))?` + cPerm = `(?:\s+([-rwxp]+))?` + + procMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceHex + hexPair + spaceDigits + cSpaceString) + briefMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceString + cSpaceAtOffset + cSpaceHex) + + // Regular expression to parse log data, of the form: + // ... file:line] msg... + logInfoRE = regexp.MustCompile(`^[^\[\]]+:[0-9]+]\s`) +) + +func isSpaceOrComment(line string) bool { + trimmed := strings.TrimSpace(line) + return len(trimmed) == 0 || trimmed[0] == '#' +} + +// parseGoCount parses a Go count profile (e.g., threadcreate or +// goroutine) and returns a new Profile. +func parseGoCount(b []byte) (*Profile, error) { + s := bufio.NewScanner(bytes.NewBuffer(b)) + // Skip comments at the beginning of the file. + for s.Scan() && isSpaceOrComment(s.Text()) { + } + if err := s.Err(); err != nil { + return nil, err + } + m := countStartRE.FindStringSubmatch(s.Text()) + if m == nil { + return nil, errUnrecognized + } + profileType := m[1] + p := &Profile{ + PeriodType: &ValueType{Type: profileType, Unit: "count"}, + Period: 1, + SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, + } + locations := make(map[uint64]*Location) + for s.Scan() { + line := s.Text() + if isSpaceOrComment(line) { + continue + } + if strings.HasPrefix(line, "---") { + break + } + m := countRE.FindStringSubmatch(line) + if m == nil { + return nil, errMalformed + } + n, err := strconv.ParseInt(m[1], 0, 64) + if err != nil { + return nil, errMalformed + } + fields := strings.Fields(m[2]) + locs := make([]*Location, 0, len(fields)) + for _, stk := range fields { + addr, err := strconv.ParseUint(stk, 0, 64) + if err != nil { + return nil, errMalformed + } + // Adjust all frames by -1 to land on top of the call instruction. + addr-- + loc := locations[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locations[addr] = loc + p.Location = append(p.Location, loc) + } + locs = append(locs, loc) + } + p.Sample = append(p.Sample, &Sample{ + Location: locs, + Value: []int64{n}, + }) + } + if err := s.Err(); err != nil { + return nil, err + } + + if err := parseAdditionalSections(s, p); err != nil { + return nil, err + } + return p, nil +} + +// remapLocationIDs ensures there is a location for each address +// referenced by a sample, and remaps the samples to point to the new +// location ids. +func (p *Profile) remapLocationIDs() { + seen := make(map[*Location]bool, len(p.Location)) + var locs []*Location + + for _, s := range p.Sample { + for _, l := range s.Location { + if seen[l] { + continue + } + l.ID = uint64(len(locs) + 1) + locs = append(locs, l) + seen[l] = true + } + } + p.Location = locs +} + +func (p *Profile) remapFunctionIDs() { + seen := make(map[*Function]bool, len(p.Function)) + var fns []*Function + + for _, l := range p.Location { + for _, ln := range l.Line { + fn := ln.Function + if fn == nil || seen[fn] { + continue + } + fn.ID = uint64(len(fns) + 1) + fns = append(fns, fn) + seen[fn] = true + } + } + p.Function = fns +} + +// remapMappingIDs matches location addresses with existing mappings +// and updates them appropriately. This is O(N*M), if this ever shows +// up as a bottleneck, evaluate sorting the mappings and doing a +// binary search, which would make it O(N*log(M)). +func (p *Profile) remapMappingIDs() { + // Some profile handlers will incorrectly set regions for the main + // executable if its section is remapped. Fix them through heuristics. + + if len(p.Mapping) > 0 { + // Remove the initial mapping if named '/anon_hugepage' and has a + // consecutive adjacent mapping. + if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { + if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { + p.Mapping = p.Mapping[1:] + } + } + } + + // Subtract the offset from the start of the main mapping if it + // ends up at a recognizable start address. + if len(p.Mapping) > 0 { + const expectedStart = 0x400000 + if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { + m.Start = expectedStart + m.Offset = 0 + } + } + + // Associate each location with an address to the corresponding + // mapping. Create fake mapping if a suitable one isn't found. + var fake *Mapping +nextLocation: + for _, l := range p.Location { + a := l.Address + if l.Mapping != nil || a == 0 { + continue + } + for _, m := range p.Mapping { + if m.Start <= a && a < m.Limit { + l.Mapping = m + continue nextLocation + } + } + // Work around legacy handlers failing to encode the first + // part of mappings split into adjacent ranges. + for _, m := range p.Mapping { + if m.Offset != 0 && m.Start-m.Offset <= a && a < m.Start { + m.Start -= m.Offset + m.Offset = 0 + l.Mapping = m + continue nextLocation + } + } + // If there is still no mapping, create a fake one. + // This is important for the Go legacy handler, which produced + // no mappings. + if fake == nil { + fake = &Mapping{ + ID: 1, + Limit: ^uint64(0), + } + p.Mapping = append(p.Mapping, fake) + } + l.Mapping = fake + } + + // Reset all mapping IDs. + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } +} + +var cpuInts = []func([]byte) (uint64, []byte){ + get32l, + get32b, + get64l, + get64b, +} + +func get32l(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] +} + +func get32b(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] +} + +func get64l(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] +} + +func get64b(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] +} + +// parseCPU parses a profilez legacy profile and returns a newly +// populated Profile. +// +// The general format for profilez samples is a sequence of words in +// binary format. The first words are a header with the following data: +// +// 1st word -- 0 +// 2nd word -- 3 +// 3rd word -- 0 if a c++ application, 1 if a java application. +// 4th word -- Sampling period (in microseconds). +// 5th word -- Padding. +func parseCPU(b []byte) (*Profile, error) { + var parse func([]byte) (uint64, []byte) + var n1, n2, n3, n4, n5 uint64 + for _, parse = range cpuInts { + var tmp []byte + n1, tmp = parse(b) + n2, tmp = parse(tmp) + n3, tmp = parse(tmp) + n4, tmp = parse(tmp) + n5, tmp = parse(tmp) + + if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { + b = tmp + return cpuProfile(b, int64(n4), parse) + } + if tmp != nil && n1 == 0 && n2 == 3 && n3 == 1 && n4 > 0 && n5 == 0 { + b = tmp + return javaCPUProfile(b, int64(n4), parse) + } + } + return nil, errUnrecognized +} + +// cpuProfile returns a new Profile from C++ profilez data. +// b is the profile bytes after the header, period is the profiling +// period, and parse is a function to parse 8-byte chunks from the +// profile in its native endianness. +func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { + p := &Profile{ + Period: period * 1000, + PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, + SampleType: []*ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "nanoseconds"}, + }, + } + var err error + if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { + return nil, err + } + + // If *most* samples have the same second-to-the-bottom frame, it + // strongly suggests that it is an uninteresting artifact of + // measurement -- a stack frame pushed by the signal handler. The + // bottom frame is always correct as it is picked up from the signal + // structure, not the stack. Check if this is the case and if so, + // remove. + + // Remove up to two frames. + maxiter := 2 + // Allow one different sample for this many samples with the same + // second-to-last frame. + similarSamples := 32 + margin := len(p.Sample) / similarSamples + + for iter := 0; iter < maxiter; iter++ { + addr1 := make(map[uint64]int) + for _, s := range p.Sample { + if len(s.Location) > 1 { + a := s.Location[1].Address + addr1[a] = addr1[a] + 1 + } + } + + for id1, count := range addr1 { + if count >= len(p.Sample)-margin { + // Found uninteresting frame, strip it out from all samples + for _, s := range p.Sample { + if len(s.Location) > 1 && s.Location[1].Address == id1 { + s.Location = append(s.Location[:1], s.Location[2:]...) + } + } + break + } + } + } + + if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { + return nil, err + } + + cleanupDuplicateLocations(p) + return p, nil +} + +func cleanupDuplicateLocations(p *Profile) { + // The profile handler may duplicate the leaf frame, because it gets + // its address both from stack unwinding and from the signal + // context. Detect this and delete the duplicate, which has been + // adjusted by -1. The leaf address should not be adjusted as it is + // not a call. + for _, s := range p.Sample { + if len(s.Location) > 1 && s.Location[0].Address == s.Location[1].Address+1 { + s.Location = append(s.Location[:1], s.Location[2:]...) + } + } +} + +// parseCPUSamples parses a collection of profilez samples from a +// profile. +// +// profilez samples are a repeated sequence of stack frames of the +// form: +// +// 1st word -- The number of times this stack was encountered. +// 2nd word -- The size of the stack (StackSize). +// 3rd word -- The first address on the stack. +// ... +// StackSize + 2 -- The last address on the stack +// +// The last stack trace is of the form: +// +// 1st word -- 0 +// 2nd word -- 1 +// 3rd word -- 0 +// +// Addresses from stack traces may point to the next instruction after +// each call. Optionally adjust by -1 to land somewhere on the actual +// call (except for the leaf, which is not a call). +func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { + locs := make(map[uint64]*Location) + for len(b) > 0 { + var count, nstk uint64 + count, b = parse(b) + nstk, b = parse(b) + if b == nil || nstk > uint64(len(b)/4) { + return nil, nil, errUnrecognized + } + var sloc []*Location + addrs := make([]uint64, nstk) + for i := 0; i < int(nstk); i++ { + addrs[i], b = parse(b) + } + + if count == 0 && nstk == 1 && addrs[0] == 0 { + // End of data marker + break + } + for i, addr := range addrs { + if adjust && i > 0 { + addr-- + } + loc := locs[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locs[addr] = loc + p.Location = append(p.Location, loc) + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, + &Sample{ + Value: []int64{int64(count), int64(count) * p.Period}, + Location: sloc, + }) + } + // Reached the end without finding the EOD marker. + return b, locs, nil +} + +// parseHeap parses a heapz legacy or a growthz profile and +// returns a newly populated Profile. +func parseHeap(b []byte) (p *Profile, err error) { + s := bufio.NewScanner(bytes.NewBuffer(b)) + if !s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + return nil, errUnrecognized + } + p = &Profile{} + + sampling := "" + hasAlloc := false + + line := s.Text() + p.PeriodType = &ValueType{Type: "space", Unit: "bytes"} + if header := heapHeaderRE.FindStringSubmatch(line); header != nil { + sampling, p.Period, hasAlloc, err = parseHeapHeader(line) + if err != nil { + return nil, err + } + } else if header = growthHeaderRE.FindStringSubmatch(line); header != nil { + p.Period = 1 + } else if header = fragmentationHeaderRE.FindStringSubmatch(line); header != nil { + p.Period = 1 + } else { + return nil, errUnrecognized + } + + if hasAlloc { + // Put alloc before inuse so that default pprof selection + // will prefer inuse_space. + p.SampleType = []*ValueType{ + {Type: "alloc_objects", Unit: "count"}, + {Type: "alloc_space", Unit: "bytes"}, + {Type: "inuse_objects", Unit: "count"}, + {Type: "inuse_space", Unit: "bytes"}, + } + } else { + p.SampleType = []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + } + } + + locs := make(map[uint64]*Location) + for s.Scan() { + line := strings.TrimSpace(s.Text()) + + if isSpaceOrComment(line) { + continue + } + + if isMemoryMapSentinel(line) { + break + } + + value, blocksize, addrs, err := parseHeapSample(line, p.Period, sampling, hasAlloc) + if err != nil { + return nil, err + } + + var sloc []*Location + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + NumLabel: map[string][]int64{"bytes": {blocksize}}, + }) + } + if err := s.Err(); err != nil { + return nil, err + } + if err := parseAdditionalSections(s, p); err != nil { + return nil, err + } + return p, nil +} + +func parseHeapHeader(line string) (sampling string, period int64, hasAlloc bool, err error) { + header := heapHeaderRE.FindStringSubmatch(line) + if header == nil { + return "", 0, false, errUnrecognized + } + + if len(header[6]) > 0 { + if period, err = strconv.ParseInt(header[6], 10, 64); err != nil { + return "", 0, false, errUnrecognized + } + } + + if (header[3] != header[1] && header[3] != "0") || (header[4] != header[2] && header[4] != "0") { + hasAlloc = true + } + + switch header[5] { + case "heapz_v2", "heap_v2": + return "v2", period, hasAlloc, nil + case "heapprofile": + return "", 1, hasAlloc, nil + case "heap": + return "v2", period / 2, hasAlloc, nil + default: + return "", 0, false, errUnrecognized + } +} + +// parseHeapSample parses a single row from a heap profile into a new Sample. +func parseHeapSample(line string, rate int64, sampling string, includeAlloc bool) (value []int64, blocksize int64, addrs []uint64, err error) { + sampleData := heapSampleRE.FindStringSubmatch(line) + if len(sampleData) != 6 { + return nil, 0, nil, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) + } + + // This is a local-scoped helper function to avoid needing to pass + // around rate, sampling and many return parameters. + addValues := func(countString, sizeString string, label string) error { + count, err := strconv.ParseInt(countString, 10, 64) + if err != nil { + return fmt.Errorf("malformed sample: %s: %v", line, err) + } + size, err := strconv.ParseInt(sizeString, 10, 64) + if err != nil { + return fmt.Errorf("malformed sample: %s: %v", line, err) + } + if count == 0 && size != 0 { + return fmt.Errorf("%s count was 0 but %s bytes was %d", label, label, size) + } + if count != 0 { + blocksize = size / count + if sampling == "v2" { + count, size = scaleHeapSample(count, size, rate) + } + } + value = append(value, count, size) + return nil + } + + if includeAlloc { + if err := addValues(sampleData[3], sampleData[4], "allocation"); err != nil { + return nil, 0, nil, err + } + } + + if err := addValues(sampleData[1], sampleData[2], "inuse"); err != nil { + return nil, 0, nil, err + } + + addrs, err = parseHexAddresses(sampleData[5]) + if err != nil { + return nil, 0, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + return value, blocksize, addrs, nil +} + +// parseHexAddresses extracts hex numbers from a string, attempts to convert +// each to an unsigned 64-bit number and returns the resulting numbers as a +// slice, or an error if the string contains hex numbers which are too large to +// handle (which means a malformed profile). +func parseHexAddresses(s string) ([]uint64, error) { + hexStrings := hexNumberRE.FindAllString(s, -1) + var addrs []uint64 + for _, s := range hexStrings { + if addr, err := strconv.ParseUint(s, 0, 64); err == nil { + addrs = append(addrs, addr) + } else { + return nil, fmt.Errorf("failed to parse as hex 64-bit number: %s", s) + } + } + return addrs, nil +} + +// scaleHeapSample adjusts the data from a heapz Sample to +// account for its probability of appearing in the collected +// data. heapz profiles are a sampling of the memory allocations +// requests in a program. We estimate the unsampled value by dividing +// each collected sample by its probability of appearing in the +// profile. heapz v2 profiles rely on a poisson process to determine +// which samples to collect, based on the desired average collection +// rate R. The probability of a sample of size S to appear in that +// profile is 1-exp(-S/R). +func scaleHeapSample(count, size, rate int64) (int64, int64) { + if count == 0 || size == 0 { + return 0, 0 + } + + if rate <= 1 { + // if rate==1 all samples were collected so no adjustment is needed. + // if rate<1 treat as unknown and skip scaling. + return count, size + } + + avgSize := float64(size) / float64(count) + scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) + + return int64(float64(count) * scale), int64(float64(size) * scale) +} + +// parseContention parses a mutex or contention profile. There are 2 cases: +// "--- contentionz " for legacy C++ profiles (and backwards compatibility) +// "--- mutex:" or "--- contention:" for profiles generated by the Go runtime. +func parseContention(b []byte) (*Profile, error) { + s := bufio.NewScanner(bytes.NewBuffer(b)) + if !s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + return nil, errUnrecognized + } + + switch l := s.Text(); { + case strings.HasPrefix(l, "--- contentionz "): + case strings.HasPrefix(l, "--- mutex:"): + case strings.HasPrefix(l, "--- contention:"): + default: + return nil, errUnrecognized + } + + p := &Profile{ + PeriodType: &ValueType{Type: "contentions", Unit: "count"}, + Period: 1, + SampleType: []*ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: "nanoseconds"}, + }, + } + + var cpuHz int64 + // Parse text of the form "attribute = value" before the samples. + const delimiter = "=" + for s.Scan() { + line := s.Text() + if line = strings.TrimSpace(line); isSpaceOrComment(line) { + continue + } + if strings.HasPrefix(line, "---") { + break + } + attr := strings.SplitN(line, delimiter, 2) + if len(attr) != 2 { + break + } + key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) + var err error + switch key { + case "cycles/second": + if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "sampling period": + if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "ms since reset": + ms, err := strconv.ParseInt(val, 0, 64) + if err != nil { + return nil, errUnrecognized + } + p.DurationNanos = ms * 1000 * 1000 + case "format": + // CPP contentionz profiles don't have format. + return nil, errUnrecognized + case "resolution": + // CPP contentionz profiles don't have resolution. + return nil, errUnrecognized + case "discarded samples": + default: + return nil, errUnrecognized + } + } + if err := s.Err(); err != nil { + return nil, err + } + + locs := make(map[uint64]*Location) + for { + line := strings.TrimSpace(s.Text()) + if strings.HasPrefix(line, "---") { + break + } + if !isSpaceOrComment(line) { + value, addrs, err := parseContentionSample(line, p.Period, cpuHz) + if err != nil { + return nil, err + } + var sloc []*Location + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call. + addr-- + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + }) + } + if !s.Scan() { + break + } + } + if err := s.Err(); err != nil { + return nil, err + } + + if err := parseAdditionalSections(s, p); err != nil { + return nil, err + } + + return p, nil +} + +// parseContentionSample parses a single row from a contention profile +// into a new Sample. +func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { + sampleData := contentionSampleRE.FindStringSubmatch(line) + if sampleData == nil { + return nil, nil, errUnrecognized + } + + v1, err := strconv.ParseInt(sampleData[1], 10, 64) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + v2, err := strconv.ParseInt(sampleData[2], 10, 64) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + // Unsample values if period and cpuHz are available. + // - Delays are scaled to cycles and then to nanoseconds. + // - Contentions are scaled to cycles. + if period > 0 { + if cpuHz > 0 { + cpuGHz := float64(cpuHz) / 1e9 + v1 = int64(float64(v1) * float64(period) / cpuGHz) + } + v2 = v2 * period + } + + value = []int64{v2, v1} + addrs, err = parseHexAddresses(sampleData[3]) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + return value, addrs, nil +} + +// parseThread parses a Threadz profile and returns a new Profile. +func parseThread(b []byte) (*Profile, error) { + s := bufio.NewScanner(bytes.NewBuffer(b)) + // Skip past comments and empty lines seeking a real header. + for s.Scan() && isSpaceOrComment(s.Text()) { + } + + line := s.Text() + if m := threadzStartRE.FindStringSubmatch(line); m != nil { + // Advance over initial comments until first stack trace. + for s.Scan() { + if line = s.Text(); isMemoryMapSentinel(line) || strings.HasPrefix(line, "-") { + break + } + } + } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + p := &Profile{ + SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, + PeriodType: &ValueType{Type: "thread", Unit: "count"}, + Period: 1, + } + + locs := make(map[uint64]*Location) + // Recognize each thread and populate profile samples. + for !isMemoryMapSentinel(line) { + if strings.HasPrefix(line, "---- no stack trace for") { + break + } + if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + var addrs []uint64 + var err error + line, addrs, err = parseThreadSample(s) + if err != nil { + return nil, err + } + if len(addrs) == 0 { + // We got a --same as previous threads--. Bump counters. + if len(p.Sample) > 0 { + s := p.Sample[len(p.Sample)-1] + s.Value[0]++ + } + continue + } + + var sloc []*Location + for i, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call + // (except for the leaf, which is not a call). + if i > 0 { + addr-- + } + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: []int64{1}, + Location: sloc, + }) + } + + if err := parseAdditionalSections(s, p); err != nil { + return nil, err + } + + cleanupDuplicateLocations(p) + return p, nil +} + +// parseThreadSample parses a symbolized or unsymbolized stack trace. +// Returns the first line after the traceback, the sample (or nil if +// it hits a 'same-as-previous' marker) and an error. +func parseThreadSample(s *bufio.Scanner) (nextl string, addrs []uint64, err error) { + var line string + sameAsPrevious := false + for s.Scan() { + line = strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + if strings.HasPrefix(line, "---") { + break + } + if strings.Contains(line, "same as previous thread") { + sameAsPrevious = true + continue + } + + curAddrs, err := parseHexAddresses(line) + if err != nil { + return "", nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + addrs = append(addrs, curAddrs...) + } + if err := s.Err(); err != nil { + return "", nil, err + } + if sameAsPrevious { + return line, nil, nil + } + return line, addrs, nil +} + +// parseAdditionalSections parses any additional sections in the +// profile, ignoring any unrecognized sections. +func parseAdditionalSections(s *bufio.Scanner, p *Profile) error { + for !isMemoryMapSentinel(s.Text()) && s.Scan() { + } + if err := s.Err(); err != nil { + return err + } + return p.ParseMemoryMapFromScanner(s) +} + +// ParseProcMaps parses a memory map in the format of /proc/self/maps. +// ParseMemoryMap should be called after setting on a profile to +// associate locations to the corresponding mapping based on their +// address. +func ParseProcMaps(rd io.Reader) ([]*Mapping, error) { + s := bufio.NewScanner(rd) + return parseProcMapsFromScanner(s) +} + +func parseProcMapsFromScanner(s *bufio.Scanner) ([]*Mapping, error) { + var mapping []*Mapping + + var attrs []string + const delimiter = "=" + r := strings.NewReplacer() + for s.Scan() { + line := r.Replace(removeLoggingInfo(s.Text())) + m, err := parseMappingEntry(line) + if err != nil { + if err == errUnrecognized { + // Recognize assignments of the form: attr=value, and replace + // $attr with value on subsequent mappings. + if attr := strings.SplitN(line, delimiter, 2); len(attr) == 2 { + attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) + r = strings.NewReplacer(attrs...) + } + // Ignore any unrecognized entries + continue + } + return nil, err + } + if m == nil { + continue + } + mapping = append(mapping, m) + } + if err := s.Err(); err != nil { + return nil, err + } + return mapping, nil +} + +// removeLoggingInfo detects and removes log prefix entries generated +// by the glog package. If no logging prefix is detected, the string +// is returned unmodified. +func removeLoggingInfo(line string) string { + if match := logInfoRE.FindStringIndex(line); match != nil { + return line[match[1]:] + } + return line +} + +// ParseMemoryMap parses a memory map in the format of +// /proc/self/maps, and overrides the mappings in the current profile. +// It renumbers the samples and locations in the profile correspondingly. +func (p *Profile) ParseMemoryMap(rd io.Reader) error { + return p.ParseMemoryMapFromScanner(bufio.NewScanner(rd)) +} + +// ParseMemoryMapFromScanner parses a memory map in the format of +// /proc/self/maps or a variety of legacy format, and overrides the +// mappings in the current profile. It renumbers the samples and +// locations in the profile correspondingly. +func (p *Profile) ParseMemoryMapFromScanner(s *bufio.Scanner) error { + mapping, err := parseProcMapsFromScanner(s) + if err != nil { + return err + } + p.Mapping = append(p.Mapping, mapping...) + p.massageMappings() + p.remapLocationIDs() + p.remapFunctionIDs() + p.remapMappingIDs() + return nil +} + +func parseMappingEntry(l string) (*Mapping, error) { + var start, end, perm, file, offset, buildID string + if me := procMapsRE.FindStringSubmatch(l); len(me) == 6 { + start, end, perm, offset, file = me[1], me[2], me[3], me[4], me[5] + } else if me := briefMapsRE.FindStringSubmatch(l); len(me) == 7 { + start, end, perm, file, offset, buildID = me[1], me[2], me[3], me[4], me[5], me[6] + } else { + return nil, errUnrecognized + } + + var err error + mapping := &Mapping{ + File: file, + BuildID: buildID, + } + if perm != "" && !strings.Contains(perm, "x") { + // Skip non-executable entries. + return nil, nil + } + if mapping.Start, err = strconv.ParseUint(start, 16, 64); err != nil { + return nil, errUnrecognized + } + if mapping.Limit, err = strconv.ParseUint(end, 16, 64); err != nil { + return nil, errUnrecognized + } + if offset != "" { + if mapping.Offset, err = strconv.ParseUint(offset, 16, 64); err != nil { + return nil, errUnrecognized + } + } + return mapping, nil +} + +var memoryMapSentinels = []string{ + "--- Memory map: ---", + "MAPPED_LIBRARIES:", +} + +// isMemoryMapSentinel returns true if the string contains one of the +// known sentinels for memory map information. +func isMemoryMapSentinel(line string) bool { + for _, s := range memoryMapSentinels { + if strings.Contains(line, s) { + return true + } + } + return false +} + +func (p *Profile) addLegacyFrameInfo() { + switch { + case isProfileType(p, heapzSampleTypes): + p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr + case isProfileType(p, contentionzSampleTypes): + p.DropFrames, p.KeepFrames = lockRxStr, "" + default: + p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" + } +} + +var heapzSampleTypes = [][]string{ + {"allocations", "size"}, // early Go pprof profiles + {"objects", "space"}, + {"inuse_objects", "inuse_space"}, + {"alloc_objects", "alloc_space"}, + {"alloc_objects", "alloc_space", "inuse_objects", "inuse_space"}, // Go pprof legacy profiles +} +var contentionzSampleTypes = [][]string{ + {"contentions", "delay"}, +} + +func isProfileType(p *Profile, types [][]string) bool { + st := p.SampleType +nextType: + for _, t := range types { + if len(st) != len(t) { + continue + } + + for i := range st { + if st[i].Type != t[i] { + continue nextType + } + } + return true + } + return false +} + +var allocRxStr = strings.Join([]string{ + // POSIX entry points. + `calloc`, + `cfree`, + `malloc`, + `free`, + `memalign`, + `do_memalign`, + `(__)?posix_memalign`, + `pvalloc`, + `valloc`, + `realloc`, + + // TC malloc. + `tcmalloc::.*`, + `tc_calloc`, + `tc_cfree`, + `tc_malloc`, + `tc_free`, + `tc_memalign`, + `tc_posix_memalign`, + `tc_pvalloc`, + `tc_valloc`, + `tc_realloc`, + `tc_new`, + `tc_delete`, + `tc_newarray`, + `tc_deletearray`, + `tc_new_nothrow`, + `tc_newarray_nothrow`, + + // Memory-allocation routines on OS X. + `malloc_zone_malloc`, + `malloc_zone_calloc`, + `malloc_zone_valloc`, + `malloc_zone_realloc`, + `malloc_zone_memalign`, + `malloc_zone_free`, + + // Go runtime + `runtime\..*`, + + // Other misc. memory allocation routines + `BaseArena::.*`, + `(::)?do_malloc_no_errno`, + `(::)?do_malloc_pages`, + `(::)?do_malloc`, + `DoSampledAllocation`, + `MallocedMemBlock::MallocedMemBlock`, + `_M_allocate`, + `__builtin_(vec_)?delete`, + `__builtin_(vec_)?new`, + `__gnu_cxx::new_allocator::allocate`, + `__libc_malloc`, + `__malloc_alloc_template::allocate`, + `allocate`, + `cpp_alloc`, + `operator new(\[\])?`, + `simple_alloc::allocate`, +}, `|`) + +var allocSkipRxStr = strings.Join([]string{ + // Preserve Go runtime frames that appear in the middle/bottom of + // the stack. + `runtime\.panic`, + `runtime\.reflectcall`, + `runtime\.call[0-9]*`, +}, `|`) + +var cpuProfilerRxStr = strings.Join([]string{ + `ProfileData::Add`, + `ProfileData::prof_handler`, + `CpuProfiler::prof_handler`, + `__pthread_sighandler`, + `__restore`, +}, `|`) + +var lockRxStr = strings.Join([]string{ + `RecordLockProfileData`, + `(base::)?RecordLockProfileData.*`, + `(base::)?SubmitMutexProfileData.*`, + `(base::)?SubmitSpinLockProfileData.*`, + `(base::Mutex::)?AwaitCommon.*`, + `(base::Mutex::)?Unlock.*`, + `(base::Mutex::)?UnlockSlow.*`, + `(base::Mutex::)?ReaderUnlock.*`, + `(base::MutexLock::)?~MutexLock.*`, + `(Mutex::)?AwaitCommon.*`, + `(Mutex::)?Unlock.*`, + `(Mutex::)?UnlockSlow.*`, + `(Mutex::)?ReaderUnlock.*`, + `(MutexLock::)?~MutexLock.*`, + `(SpinLock::)?Unlock.*`, + `(SpinLock::)?SlowUnlock.*`, + `(SpinLockHolder::)?~SpinLockHolder.*`, +}, `|`) diff --git a/vendor/github.com/google/pprof/profile/merge.go b/vendor/github.com/google/pprof/profile/merge.go new file mode 100644 index 000000000..4b66282cb --- /dev/null +++ b/vendor/github.com/google/pprof/profile/merge.go @@ -0,0 +1,667 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package profile + +import ( + "encoding/binary" + "fmt" + "sort" + "strconv" + "strings" +) + +// Compact performs garbage collection on a profile to remove any +// unreferenced fields. This is useful to reduce the size of a profile +// after samples or locations have been removed. +func (p *Profile) Compact() *Profile { + p, _ = Merge([]*Profile{p}) + return p +} + +// Merge merges all the profiles in profs into a single Profile. +// Returns a new profile independent of the input profiles. The merged +// profile is compacted to eliminate unused samples, locations, +// functions and mappings. Profiles must have identical profile sample +// and period types or the merge will fail. profile.Period of the +// resulting profile will be the maximum of all profiles, and +// profile.TimeNanos will be the earliest nonzero one. Merges are +// associative with the caveat of the first profile having some +// specialization in how headers are combined. There may be other +// subtleties now or in the future regarding associativity. +func Merge(srcs []*Profile) (*Profile, error) { + if len(srcs) == 0 { + return nil, fmt.Errorf("no profiles to merge") + } + p, err := combineHeaders(srcs) + if err != nil { + return nil, err + } + + pm := &profileMerger{ + p: p, + samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)), + locations: make(map[locationKey]*Location, len(srcs[0].Location)), + functions: make(map[functionKey]*Function, len(srcs[0].Function)), + mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)), + } + + for _, src := range srcs { + // Clear the profile-specific hash tables + pm.locationsByID = makeLocationIDMap(len(src.Location)) + pm.functionsByID = make(map[uint64]*Function, len(src.Function)) + pm.mappingsByID = make(map[uint64]mapInfo, len(src.Mapping)) + + if len(pm.mappings) == 0 && len(src.Mapping) > 0 { + // The Mapping list has the property that the first mapping + // represents the main binary. Take the first Mapping we see, + // otherwise the operations below will add mappings in an + // arbitrary order. + pm.mapMapping(src.Mapping[0]) + } + + for _, s := range src.Sample { + if !isZeroSample(s) { + pm.mapSample(s) + } + } + } + + for _, s := range p.Sample { + if isZeroSample(s) { + // If there are any zero samples, re-merge the profile to GC + // them. + return Merge([]*Profile{p}) + } + } + + return p, nil +} + +// Normalize normalizes the source profile by multiplying each value in profile by the +// ratio of the sum of the base profile's values of that sample type to the sum of the +// source profile's value of that sample type. +func (p *Profile) Normalize(pb *Profile) error { + + if err := p.compatible(pb); err != nil { + return err + } + + baseVals := make([]int64, len(p.SampleType)) + for _, s := range pb.Sample { + for i, v := range s.Value { + baseVals[i] += v + } + } + + srcVals := make([]int64, len(p.SampleType)) + for _, s := range p.Sample { + for i, v := range s.Value { + srcVals[i] += v + } + } + + normScale := make([]float64, len(baseVals)) + for i := range baseVals { + if srcVals[i] == 0 { + normScale[i] = 0.0 + } else { + normScale[i] = float64(baseVals[i]) / float64(srcVals[i]) + } + } + p.ScaleN(normScale) + return nil +} + +func isZeroSample(s *Sample) bool { + for _, v := range s.Value { + if v != 0 { + return false + } + } + return true +} + +type profileMerger struct { + p *Profile + + // Memoization tables within a profile. + locationsByID locationIDMap + functionsByID map[uint64]*Function + mappingsByID map[uint64]mapInfo + + // Memoization tables for profile entities. + samples map[sampleKey]*Sample + locations map[locationKey]*Location + functions map[functionKey]*Function + mappings map[mappingKey]*Mapping +} + +type mapInfo struct { + m *Mapping + offset int64 +} + +func (pm *profileMerger) mapSample(src *Sample) *Sample { + // Check memoization table + k := pm.sampleKey(src) + if ss, ok := pm.samples[k]; ok { + for i, v := range src.Value { + ss.Value[i] += v + } + return ss + } + + // Make new sample. + s := &Sample{ + Location: make([]*Location, len(src.Location)), + Value: make([]int64, len(src.Value)), + Label: make(map[string][]string, len(src.Label)), + NumLabel: make(map[string][]int64, len(src.NumLabel)), + NumUnit: make(map[string][]string, len(src.NumLabel)), + } + for i, l := range src.Location { + s.Location[i] = pm.mapLocation(l) + } + for k, v := range src.Label { + vv := make([]string, len(v)) + copy(vv, v) + s.Label[k] = vv + } + for k, v := range src.NumLabel { + u := src.NumUnit[k] + vv := make([]int64, len(v)) + uu := make([]string, len(u)) + copy(vv, v) + copy(uu, u) + s.NumLabel[k] = vv + s.NumUnit[k] = uu + } + copy(s.Value, src.Value) + pm.samples[k] = s + pm.p.Sample = append(pm.p.Sample, s) + return s +} + +func (pm *profileMerger) sampleKey(sample *Sample) sampleKey { + // Accumulate contents into a string. + var buf strings.Builder + buf.Grow(64) // Heuristic to avoid extra allocs + + // encode a number + putNumber := func(v uint64) { + var num [binary.MaxVarintLen64]byte + n := binary.PutUvarint(num[:], v) + buf.Write(num[:n]) + } + + // encode a string prefixed with its length. + putDelimitedString := func(s string) { + putNumber(uint64(len(s))) + buf.WriteString(s) + } + + for _, l := range sample.Location { + // Get the location in the merged profile, which may have a different ID. + if loc := pm.mapLocation(l); loc != nil { + putNumber(loc.ID) + } + } + putNumber(0) // Delimiter + + for _, l := range sortedKeys1(sample.Label) { + putDelimitedString(l) + values := sample.Label[l] + putNumber(uint64(len(values))) + for _, v := range values { + putDelimitedString(v) + } + } + + for _, l := range sortedKeys2(sample.NumLabel) { + putDelimitedString(l) + values := sample.NumLabel[l] + putNumber(uint64(len(values))) + for _, v := range values { + putNumber(uint64(v)) + } + units := sample.NumUnit[l] + putNumber(uint64(len(units))) + for _, v := range units { + putDelimitedString(v) + } + } + + return sampleKey(buf.String()) +} + +type sampleKey string + +// sortedKeys1 returns the sorted keys found in a string->[]string map. +// +// Note: this is currently non-generic since github pprof runs golint, +// which does not support generics. When that issue is fixed, it can +// be merged with sortedKeys2 and made into a generic function. +func sortedKeys1(m map[string][]string) []string { + if len(m) == 0 { + return nil + } + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +// sortedKeys2 returns the sorted keys found in a string->[]int64 map. +// +// Note: this is currently non-generic since github pprof runs golint, +// which does not support generics. When that issue is fixed, it can +// be merged with sortedKeys1 and made into a generic function. +func sortedKeys2(m map[string][]int64) []string { + if len(m) == 0 { + return nil + } + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +func (pm *profileMerger) mapLocation(src *Location) *Location { + if src == nil { + return nil + } + + if l := pm.locationsByID.get(src.ID); l != nil { + return l + } + + mi := pm.mapMapping(src.Mapping) + l := &Location{ + ID: uint64(len(pm.p.Location) + 1), + Mapping: mi.m, + Address: uint64(int64(src.Address) + mi.offset), + Line: make([]Line, len(src.Line)), + IsFolded: src.IsFolded, + } + for i, ln := range src.Line { + l.Line[i] = pm.mapLine(ln) + } + // Check memoization table. Must be done on the remapped location to + // account for the remapped mapping ID. + k := l.key() + if ll, ok := pm.locations[k]; ok { + pm.locationsByID.set(src.ID, ll) + return ll + } + pm.locationsByID.set(src.ID, l) + pm.locations[k] = l + pm.p.Location = append(pm.p.Location, l) + return l +} + +// key generates locationKey to be used as a key for maps. +func (l *Location) key() locationKey { + key := locationKey{ + addr: l.Address, + isFolded: l.IsFolded, + } + if l.Mapping != nil { + // Normalizes address to handle address space randomization. + key.addr -= l.Mapping.Start + key.mappingID = l.Mapping.ID + } + lines := make([]string, len(l.Line)*2) + 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) + } + key.lines = strings.Join(lines, "|") + return key +} + +type locationKey struct { + addr, mappingID uint64 + lines string + isFolded bool +} + +func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { + if src == nil { + return mapInfo{} + } + + if mi, ok := pm.mappingsByID[src.ID]; ok { + return mi + } + + // Check memoization tables. + mk := src.key() + if m, ok := pm.mappings[mk]; ok { + mi := mapInfo{m, int64(m.Start) - int64(src.Start)} + pm.mappingsByID[src.ID] = mi + return mi + } + m := &Mapping{ + ID: uint64(len(pm.p.Mapping) + 1), + Start: src.Start, + Limit: src.Limit, + Offset: src.Offset, + File: src.File, + KernelRelocationSymbol: src.KernelRelocationSymbol, + BuildID: src.BuildID, + HasFunctions: src.HasFunctions, + HasFilenames: src.HasFilenames, + HasLineNumbers: src.HasLineNumbers, + HasInlineFrames: src.HasInlineFrames, + } + pm.p.Mapping = append(pm.p.Mapping, m) + + // Update memoization tables. + pm.mappings[mk] = m + mi := mapInfo{m, 0} + pm.mappingsByID[src.ID] = mi + return mi +} + +// key generates encoded strings of Mapping to be used as a key for +// maps. +func (m *Mapping) key() mappingKey { + // Normalize addresses to handle address space randomization. + // Round up to next 4K boundary to avoid minor discrepancies. + const mapsizeRounding = 0x1000 + + size := m.Limit - m.Start + size = size + mapsizeRounding - 1 + size = size - (size % mapsizeRounding) + key := mappingKey{ + size: size, + offset: m.Offset, + } + + switch { + case m.BuildID != "": + key.buildIDOrFile = m.BuildID + case m.File != "": + key.buildIDOrFile = m.File + default: + // A mapping containing neither build ID nor file name is a fake mapping. A + // key with empty buildIDOrFile is used for fake mappings so that they are + // treated as the same mapping during merging. + } + return key +} + +type mappingKey struct { + size, offset uint64 + buildIDOrFile string +} + +func (pm *profileMerger) mapLine(src Line) Line { + ln := Line{ + Function: pm.mapFunction(src.Function), + Line: src.Line, + } + return ln +} + +func (pm *profileMerger) mapFunction(src *Function) *Function { + if src == nil { + return nil + } + if f, ok := pm.functionsByID[src.ID]; ok { + return f + } + k := src.key() + if f, ok := pm.functions[k]; ok { + pm.functionsByID[src.ID] = f + return f + } + f := &Function{ + ID: uint64(len(pm.p.Function) + 1), + Name: src.Name, + SystemName: src.SystemName, + Filename: src.Filename, + StartLine: src.StartLine, + } + pm.functions[k] = f + pm.functionsByID[src.ID] = f + pm.p.Function = append(pm.p.Function, f) + return f +} + +// key generates a struct to be used as a key for maps. +func (f *Function) key() functionKey { + return functionKey{ + f.StartLine, + f.Name, + f.SystemName, + f.Filename, + } +} + +type functionKey struct { + startLine int64 + name, systemName, fileName string +} + +// combineHeaders checks that all profiles can be merged and returns +// their combined profile. +func combineHeaders(srcs []*Profile) (*Profile, error) { + for _, s := range srcs[1:] { + if err := srcs[0].compatible(s); err != nil { + return nil, err + } + } + + var timeNanos, durationNanos, period int64 + var comments []string + seenComments := map[string]bool{} + var defaultSampleType string + for _, s := range srcs { + if timeNanos == 0 || s.TimeNanos < timeNanos { + timeNanos = s.TimeNanos + } + durationNanos += s.DurationNanos + if period == 0 || period < s.Period { + period = s.Period + } + for _, c := range s.Comments { + if seen := seenComments[c]; !seen { + comments = append(comments, c) + seenComments[c] = true + } + } + if defaultSampleType == "" { + defaultSampleType = s.DefaultSampleType + } + } + + p := &Profile{ + SampleType: make([]*ValueType, len(srcs[0].SampleType)), + + DropFrames: srcs[0].DropFrames, + KeepFrames: srcs[0].KeepFrames, + + TimeNanos: timeNanos, + DurationNanos: durationNanos, + PeriodType: srcs[0].PeriodType, + Period: period, + + Comments: comments, + DefaultSampleType: defaultSampleType, + } + copy(p.SampleType, srcs[0].SampleType) + return p, nil +} + +// compatible determines if two profiles can be compared/merged. +// returns nil if the profiles are compatible; otherwise an error with +// details on the incompatibility. +func (p *Profile) compatible(pb *Profile) error { + if !equalValueType(p.PeriodType, pb.PeriodType) { + return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) + } + + if len(p.SampleType) != len(pb.SampleType) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + + for i := range p.SampleType { + if !equalValueType(p.SampleType[i], pb.SampleType[i]) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + } + return nil +} + +// equalValueType returns true if the two value types are semantically +// equal. It ignores the internal fields used during encode/decode. +func equalValueType(st1, st2 *ValueType) bool { + return st1.Type == st2.Type && st1.Unit == st2.Unit +} + +// locationIDMap is like a map[uint64]*Location, but provides efficiency for +// ids that are densely numbered, which is often the case. +type locationIDMap struct { + dense []*Location // indexed by id for id < len(dense) + sparse map[uint64]*Location // indexed by id for id >= len(dense) +} + +func makeLocationIDMap(n int) locationIDMap { + return locationIDMap{ + dense: make([]*Location, n), + sparse: map[uint64]*Location{}, + } +} + +func (lm locationIDMap) get(id uint64) *Location { + if id < uint64(len(lm.dense)) { + return lm.dense[int(id)] + } + return lm.sparse[id] +} + +func (lm locationIDMap) set(id uint64, loc *Location) { + if id < uint64(len(lm.dense)) { + lm.dense[id] = loc + return + } + lm.sparse[id] = loc +} + +// CompatibilizeSampleTypes makes profiles compatible to be compared/merged. It +// keeps sample types that appear in all profiles only and drops/reorders the +// sample types as necessary. +// +// In the case of sample types order is not the same for given profiles the +// order is derived from the first profile. +// +// Profiles are modified in-place. +// +// It returns an error if the sample type's intersection is empty. +func CompatibilizeSampleTypes(ps []*Profile) error { + sTypes := commonSampleTypes(ps) + if len(sTypes) == 0 { + return fmt.Errorf("profiles have empty common sample type list") + } + for _, p := range ps { + if err := compatibilizeSampleTypes(p, sTypes); err != nil { + return err + } + } + return nil +} + +// commonSampleTypes returns sample types that appear in all profiles in the +// order how they ordered in the first profile. +func commonSampleTypes(ps []*Profile) []string { + if len(ps) == 0 { + return nil + } + sTypes := map[string]int{} + for _, p := range ps { + for _, st := range p.SampleType { + sTypes[st.Type]++ + } + } + var res []string + for _, st := range ps[0].SampleType { + if sTypes[st.Type] == len(ps) { + res = append(res, st.Type) + } + } + return res +} + +// compatibilizeSampleTypes drops sample types that are not present in sTypes +// list and reorder them if needed. +// +// It sets DefaultSampleType to sType[0] if it is not in sType list. +// +// It assumes that all sample types from the sTypes list are present in the +// given profile otherwise it returns an error. +func compatibilizeSampleTypes(p *Profile, sTypes []string) error { + if len(sTypes) == 0 { + return fmt.Errorf("sample type list is empty") + } + defaultSampleType := sTypes[0] + reMap, needToModify := make([]int, len(sTypes)), false + for i, st := range sTypes { + if st == p.DefaultSampleType { + defaultSampleType = p.DefaultSampleType + } + idx := searchValueType(p.SampleType, st) + if idx < 0 { + return fmt.Errorf("%q sample type is not found in profile", st) + } + reMap[i] = idx + if idx != i { + needToModify = true + } + } + if !needToModify && len(sTypes) == len(p.SampleType) { + return nil + } + p.DefaultSampleType = defaultSampleType + oldSampleTypes := p.SampleType + p.SampleType = make([]*ValueType, len(sTypes)) + for i, idx := range reMap { + p.SampleType[i] = oldSampleTypes[idx] + } + values := make([]int64, len(sTypes)) + for _, s := range p.Sample { + for i, idx := range reMap { + values[i] = s.Value[idx] + } + s.Value = s.Value[:len(values)] + copy(s.Value, values) + } + return nil +} + +func searchValueType(vts []*ValueType, s string) int { + for i, vt := range vts { + if vt.Type == s { + return i + } + } + return -1 +} diff --git a/vendor/github.com/google/pprof/profile/profile.go b/vendor/github.com/google/pprof/profile/profile.go new file mode 100644 index 000000000..60ef7e926 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/profile.go @@ -0,0 +1,856 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package profile provides a representation of profile.proto and +// methods to encode/decode profiles in this format. +package profile + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "math" + "path/filepath" + "regexp" + "sort" + "strings" + "sync" + "time" +) + +// Profile is an in-memory representation of profile.proto. +type Profile struct { + SampleType []*ValueType + DefaultSampleType string + Sample []*Sample + Mapping []*Mapping + Location []*Location + Function []*Function + Comments []string + + DropFrames string + KeepFrames string + + TimeNanos int64 + DurationNanos int64 + PeriodType *ValueType + Period int64 + + // The following fields are modified during encoding and copying, + // so are protected by a Mutex. + encodeMu sync.Mutex + + commentX []int64 + dropFramesX int64 + keepFramesX int64 + stringTable []string + defaultSampleTypeX int64 +} + +// ValueType corresponds to Profile.ValueType +type ValueType struct { + Type string // cpu, wall, inuse_space, etc + Unit string // seconds, nanoseconds, bytes, etc + + typeX int64 + unitX int64 +} + +// Sample corresponds to Profile.Sample +type Sample struct { + Location []*Location + Value []int64 + // Label is a per-label-key map to values for string labels. + // + // In general, having multiple values for the given label key is strongly + // discouraged - see docs for the sample label field in profile.proto. The + // main reason this unlikely state is tracked here is to make the + // decoding->encoding roundtrip not lossy. But we expect that the value + // slices present in this map are always of length 1. + Label map[string][]string + // NumLabel is a per-label-key map to values for numeric labels. See a note + // above on handling multiple values for a label. + NumLabel map[string][]int64 + // NumUnit is a per-label-key map to the unit names of corresponding numeric + // label values. The unit info may be missing even if the label is in + // NumLabel, see the docs in profile.proto for details. When the value is + // slice is present and not nil, its length must be equal to the length of + // the corresponding value slice in NumLabel. + NumUnit map[string][]string + + locationIDX []uint64 + labelX []label +} + +// label corresponds to Profile.Label +type label struct { + keyX int64 + // Exactly one of the two following values must be set + strX int64 + numX int64 // Integer value for this label + // can be set if numX has value + unitX int64 +} + +// Mapping corresponds to Profile.Mapping +type Mapping struct { + ID uint64 + Start uint64 + Limit uint64 + Offset uint64 + File string + BuildID string + HasFunctions bool + HasFilenames bool + HasLineNumbers bool + HasInlineFrames bool + + fileX int64 + buildIDX int64 + + // Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File. + // For linux kernel mappings generated by some tools, correct symbolization depends + // on knowing which of the two possible relocation symbols was used for `Start`. + // This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext"). + // + // Note, this public field is not persisted in the proto. For the purposes of + // copying / merging / hashing profiles, it is considered subsumed by `File`. + KernelRelocationSymbol string +} + +// Location corresponds to Profile.Location +type Location struct { + ID uint64 + Mapping *Mapping + Address uint64 + Line []Line + IsFolded bool + + mappingIDX uint64 +} + +// Line corresponds to Profile.Line +type Line struct { + Function *Function + Line int64 + + functionIDX uint64 +} + +// Function corresponds to Profile.Function +type Function struct { + ID uint64 + Name string + SystemName string + Filename string + StartLine int64 + + nameX int64 + systemNameX int64 + filenameX int64 +} + +// Parse parses a profile and checks for its validity. The input +// may be a gzip-compressed encoded protobuf or one of many legacy +// profile formats which may be unsupported in the future. +func Parse(r io.Reader) (*Profile, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + return ParseData(data) +} + +// ParseData parses a profile from a buffer and checks for its +// validity. +func ParseData(data []byte) (*Profile, error) { + var p *Profile + var err error + if len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err == nil { + data, err = io.ReadAll(gz) + } + if err != nil { + return nil, fmt.Errorf("decompressing profile: %v", err) + } + } + if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile { + p, err = parseLegacy(data) + } + + if err != nil { + return nil, fmt.Errorf("parsing profile: %v", err) + } + + if err := p.CheckValid(); err != nil { + return nil, fmt.Errorf("malformed profile: %v", err) + } + return p, nil +} + +var errUnrecognized = fmt.Errorf("unrecognized profile format") +var errMalformed = fmt.Errorf("malformed profile format") +var errNoData = fmt.Errorf("empty input file") +var errConcatProfile = fmt.Errorf("concatenated profiles detected") + +func parseLegacy(data []byte) (*Profile, error) { + parsers := []func([]byte) (*Profile, error){ + parseCPU, + parseHeap, + parseGoCount, // goroutine, threadcreate + parseThread, + parseContention, + parseJavaProfile, + } + + for _, parser := range parsers { + p, err := parser(data) + if err == nil { + p.addLegacyFrameInfo() + return p, nil + } + if err != errUnrecognized { + return nil, err + } + } + return nil, errUnrecognized +} + +// ParseUncompressed parses an uncompressed protobuf into a profile. +func ParseUncompressed(data []byte) (*Profile, error) { + if len(data) == 0 { + return nil, errNoData + } + p := &Profile{} + if err := unmarshal(data, p); err != nil { + return nil, err + } + + if err := p.postDecode(); err != nil { + return nil, err + } + + return p, nil +} + +var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`) + +// massageMappings applies heuristic-based changes to the profile +// mappings to account for quirks of some environments. +func (p *Profile) massageMappings() { + // Merge adjacent regions with matching names, checking that the offsets match + if len(p.Mapping) > 1 { + mappings := []*Mapping{p.Mapping[0]} + for _, m := range p.Mapping[1:] { + lm := mappings[len(mappings)-1] + if adjacent(lm, m) { + lm.Limit = m.Limit + if m.File != "" { + lm.File = m.File + } + if m.BuildID != "" { + lm.BuildID = m.BuildID + } + p.updateLocationMapping(m, lm) + continue + } + mappings = append(mappings, m) + } + p.Mapping = mappings + } + + // Use heuristics to identify main binary and move it to the top of the list of mappings + for i, m := range p.Mapping { + file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1)) + if len(file) == 0 { + continue + } + if len(libRx.FindStringSubmatch(file)) > 0 { + continue + } + if file[0] == '[' { + continue + } + // Swap what we guess is main to position 0. + p.Mapping[0], p.Mapping[i] = p.Mapping[i], p.Mapping[0] + break + } + + // Keep the mapping IDs neatly sorted + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } +} + +// adjacent returns whether two mapping entries represent the same +// mapping that has been split into two. Check that their addresses are adjacent, +// and if the offsets match, if they are available. +func adjacent(m1, m2 *Mapping) bool { + if m1.File != "" && m2.File != "" { + if m1.File != m2.File { + return false + } + } + if m1.BuildID != "" && m2.BuildID != "" { + if m1.BuildID != m2.BuildID { + return false + } + } + if m1.Limit != m2.Start { + return false + } + if m1.Offset != 0 && m2.Offset != 0 { + offset := m1.Offset + (m1.Limit - m1.Start) + if offset != m2.Offset { + return false + } + } + return true +} + +func (p *Profile) updateLocationMapping(from, to *Mapping) { + for _, l := range p.Location { + if l.Mapping == from { + l.Mapping = to + } + } +} + +func serialize(p *Profile) []byte { + p.encodeMu.Lock() + p.preEncode() + b := marshal(p) + p.encodeMu.Unlock() + return b +} + +// Write writes the profile as a gzip-compressed marshaled protobuf. +func (p *Profile) Write(w io.Writer) error { + zw := gzip.NewWriter(w) + defer zw.Close() + _, err := zw.Write(serialize(p)) + return err +} + +// WriteUncompressed writes the profile as a marshaled protobuf. +func (p *Profile) WriteUncompressed(w io.Writer) error { + _, err := w.Write(serialize(p)) + return err +} + +// CheckValid tests whether the profile is valid. Checks include, but are +// not limited to: +// - len(Profile.Sample[n].value) == len(Profile.value_unit) +// - Sample.id has a corresponding Profile.Location +func (p *Profile) CheckValid() error { + // Check that sample values are consistent + sampleLen := len(p.SampleType) + if sampleLen == 0 && len(p.Sample) != 0 { + return fmt.Errorf("missing sample type information") + } + for _, s := range p.Sample { + if s == nil { + return fmt.Errorf("profile has nil sample") + } + if len(s.Value) != sampleLen { + return fmt.Errorf("mismatch: sample has %d values vs. %d types", len(s.Value), len(p.SampleType)) + } + for _, l := range s.Location { + if l == nil { + return fmt.Errorf("sample has nil location") + } + } + } + + // Check that all mappings/locations/functions are in the tables + // Check that there are no duplicate ids + mappings := make(map[uint64]*Mapping, len(p.Mapping)) + for _, m := range p.Mapping { + if m == nil { + return fmt.Errorf("profile has nil mapping") + } + if m.ID == 0 { + return fmt.Errorf("found mapping with reserved ID=0") + } + if mappings[m.ID] != nil { + return fmt.Errorf("multiple mappings with same id: %d", m.ID) + } + mappings[m.ID] = m + } + functions := make(map[uint64]*Function, len(p.Function)) + for _, f := range p.Function { + if f == nil { + return fmt.Errorf("profile has nil function") + } + if f.ID == 0 { + return fmt.Errorf("found function with reserved ID=0") + } + if functions[f.ID] != nil { + return fmt.Errorf("multiple functions with same id: %d", f.ID) + } + functions[f.ID] = f + } + locations := make(map[uint64]*Location, len(p.Location)) + for _, l := range p.Location { + if l == nil { + return fmt.Errorf("profile has nil location") + } + if l.ID == 0 { + return fmt.Errorf("found location with reserved id=0") + } + if locations[l.ID] != nil { + return fmt.Errorf("multiple locations with same id: %d", l.ID) + } + locations[l.ID] = l + if m := l.Mapping; m != nil { + if m.ID == 0 || mappings[m.ID] != m { + return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) + } + } + for _, ln := range l.Line { + f := ln.Function + if f == nil { + return fmt.Errorf("location id: %d has a line with nil function", l.ID) + } + if f.ID == 0 || functions[f.ID] != f { + return fmt.Errorf("inconsistent function %p: %d", f, f.ID) + } + } + } + return nil +} + +// 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 { + for _, m := range p.Mapping { + m.HasInlineFrames = m.HasInlineFrames && inlineFrame + m.HasFunctions = m.HasFunctions && function + m.HasFilenames = m.HasFilenames && filename + m.HasLineNumbers = m.HasLineNumbers && linenumber + } + + // Aggregate functions + if !function || !filename { + for _, f := range p.Function { + if !function { + f.Name = "" + f.SystemName = "" + } + if !filename { + f.Filename = "" + } + } + } + + // Aggregate locations + if !inlineFrame || !address || !linenumber { + for _, l := range p.Location { + if !inlineFrame && len(l.Line) > 1 { + l.Line = l.Line[len(l.Line)-1:] + } + if !linenumber { + for i := range l.Line { + l.Line[i].Line = 0 + } + } + if !address { + l.Address = 0 + } + } + } + + return p.CheckValid() +} + +// NumLabelUnits returns a map of numeric label keys to the units +// associated with those keys and a map of those keys to any units +// that were encountered but not used. +// Unit for a given key is the first encountered unit for that key. If multiple +// units are encountered for values paired with a particular key, then the first +// unit encountered is used and all other units are returned in sorted order +// in map of ignored units. +// If no units are encountered for a particular key, the unit is then inferred +// based on the key. +func (p *Profile) NumLabelUnits() (map[string]string, map[string][]string) { + numLabelUnits := map[string]string{} + ignoredUnits := map[string]map[string]bool{} + encounteredKeys := map[string]bool{} + + // Determine units based on numeric tags for each sample. + for _, s := range p.Sample { + for k := range s.NumLabel { + encounteredKeys[k] = true + for _, unit := range s.NumUnit[k] { + if unit == "" { + continue + } + if wantUnit, ok := numLabelUnits[k]; !ok { + numLabelUnits[k] = unit + } else if wantUnit != unit { + if v, ok := ignoredUnits[k]; ok { + v[unit] = true + } else { + ignoredUnits[k] = map[string]bool{unit: true} + } + } + } + } + } + // Infer units for keys without any units associated with + // numeric tag values. + for key := range encounteredKeys { + unit := numLabelUnits[key] + if unit == "" { + switch key { + case "alignment", "request": + numLabelUnits[key] = "bytes" + default: + numLabelUnits[key] = key + } + } + } + + // Copy ignored units into more readable format + unitsIgnored := make(map[string][]string, len(ignoredUnits)) + for key, values := range ignoredUnits { + units := make([]string, len(values)) + i := 0 + for unit := range values { + units[i] = unit + i++ + } + sort.Strings(units) + unitsIgnored[key] = units + } + + return numLabelUnits, unitsIgnored +} + +// String dumps a text representation of a profile. Intended mainly +// for debugging purposes. +func (p *Profile) String() string { + ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location)) + for _, c := range p.Comments { + ss = append(ss, "Comment: "+c) + } + if pt := p.PeriodType; pt != nil { + ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) + } + ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) + if p.TimeNanos != 0 { + ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) + } + if p.DurationNanos != 0 { + ss = append(ss, fmt.Sprintf("Duration: %.4v", time.Duration(p.DurationNanos))) + } + + ss = append(ss, "Samples:") + var sh1 string + for _, s := range p.SampleType { + dflt := "" + if s.Type == p.DefaultSampleType { + dflt = "[dflt]" + } + sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt) + } + ss = append(ss, strings.TrimSpace(sh1)) + for _, s := range p.Sample { + ss = append(ss, s.string()) + } + + ss = append(ss, "Locations") + for _, l := range p.Location { + ss = append(ss, l.string()) + } + + ss = append(ss, "Mappings") + for _, m := range p.Mapping { + ss = append(ss, m.string()) + } + + return strings.Join(ss, "\n") + "\n" +} + +// string dumps a text representation of a mapping. Intended mainly +// for debugging purposes. +func (m *Mapping) string() string { + bits := "" + if m.HasFunctions { + bits = bits + "[FN]" + } + if m.HasFilenames { + bits = bits + "[FL]" + } + if m.HasLineNumbers { + bits = bits + "[LN]" + } + if m.HasInlineFrames { + bits = bits + "[IN]" + } + return fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", + m.ID, + m.Start, m.Limit, m.Offset, + m.File, + m.BuildID, + bits) +} + +// string dumps a text representation of a location. Intended mainly +// for debugging purposes. +func (l *Location) string() string { + ss := []string{} + locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) + if m := l.Mapping; m != nil { + locStr = locStr + fmt.Sprintf("M=%d ", m.ID) + } + if l.IsFolded { + locStr = locStr + "[F] " + } + if len(l.Line) == 0 { + ss = append(ss, locStr) + } + for li := range l.Line { + lnStr := "??" + if fn := l.Line[li].Function; fn != nil { + lnStr = fmt.Sprintf("%s %s:%d s=%d", + fn.Name, + fn.Filename, + l.Line[li].Line, + fn.StartLine) + if fn.Name != fn.SystemName { + lnStr = lnStr + "(" + fn.SystemName + ")" + } + } + ss = append(ss, locStr+lnStr) + // Do not print location details past the first line + locStr = " " + } + return strings.Join(ss, "\n") +} + +// string dumps a text representation of a sample. Intended mainly +// for debugging purposes. +func (s *Sample) string() string { + ss := []string{} + var sv string + for _, v := range s.Value { + sv = fmt.Sprintf("%s %10d", sv, v) + } + sv = sv + ": " + for _, l := range s.Location { + sv = sv + fmt.Sprintf("%d ", l.ID) + } + ss = append(ss, sv) + const labelHeader = " " + if len(s.Label) > 0 { + ss = append(ss, labelHeader+labelsToString(s.Label)) + } + if len(s.NumLabel) > 0 { + ss = append(ss, labelHeader+numLabelsToString(s.NumLabel, s.NumUnit)) + } + return strings.Join(ss, "\n") +} + +// labelsToString returns a string representation of a +// map representing labels. +func labelsToString(labels map[string][]string) string { + ls := []string{} + for k, v := range labels { + ls = append(ls, fmt.Sprintf("%s:%v", k, v)) + } + sort.Strings(ls) + return strings.Join(ls, " ") +} + +// numLabelsToString returns a string representation of a map +// representing numeric labels. +func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]string) string { + ls := []string{} + for k, v := range numLabels { + units := numUnits[k] + var labelString string + if len(units) == len(v) { + values := make([]string, len(v)) + for i, vv := range v { + values[i] = fmt.Sprintf("%d %s", vv, units[i]) + } + labelString = fmt.Sprintf("%s:%v", k, values) + } else { + labelString = fmt.Sprintf("%s:%v", k, v) + } + ls = append(ls, labelString) + } + sort.Strings(ls) + return strings.Join(ls, " ") +} + +// SetLabel sets the specified key to the specified value for all samples in the +// profile. +func (p *Profile) SetLabel(key string, value []string) { + for _, sample := range p.Sample { + if sample.Label == nil { + sample.Label = map[string][]string{key: value} + } else { + sample.Label[key] = value + } + } +} + +// RemoveLabel removes all labels associated with the specified key for all +// samples in the profile. +func (p *Profile) RemoveLabel(key string) { + for _, sample := range p.Sample { + delete(sample.Label, key) + } +} + +// HasLabel returns true if a sample has a label with indicated key and value. +func (s *Sample) HasLabel(key, value string) bool { + for _, v := range s.Label[key] { + if v == value { + return true + } + } + return false +} + +// SetNumLabel sets the specified key to the specified value for all samples in the +// profile. "unit" is a slice that describes the units that each corresponding member +// of "values" is measured in (e.g. bytes or seconds). If there is no relevant +// unit for a given value, that member of "unit" should be the empty string. +// "unit" must either have the same length as "value", or be nil. +func (p *Profile) SetNumLabel(key string, value []int64, unit []string) { + for _, sample := range p.Sample { + if sample.NumLabel == nil { + sample.NumLabel = map[string][]int64{key: value} + } else { + sample.NumLabel[key] = value + } + if sample.NumUnit == nil { + sample.NumUnit = map[string][]string{key: unit} + } else { + sample.NumUnit[key] = unit + } + } +} + +// RemoveNumLabel removes all numerical labels associated with the specified key for all +// samples in the profile. +func (p *Profile) RemoveNumLabel(key string) { + for _, sample := range p.Sample { + delete(sample.NumLabel, key) + delete(sample.NumUnit, key) + } +} + +// DiffBaseSample returns true if a sample belongs to the diff base and false +// otherwise. +func (s *Sample) DiffBaseSample() bool { + return s.HasLabel("pprof::base", "true") +} + +// Scale multiplies all sample values in a profile by a constant and keeps +// only samples that have at least one non-zero value. +func (p *Profile) Scale(ratio float64) { + if ratio == 1 { + return + } + ratios := make([]float64, len(p.SampleType)) + for i := range p.SampleType { + ratios[i] = ratio + } + p.ScaleN(ratios) +} + +// ScaleN multiplies each sample values in a sample by a different amount +// and keeps only samples that have at least one non-zero value. +func (p *Profile) ScaleN(ratios []float64) error { + if len(p.SampleType) != len(ratios) { + return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType)) + } + allOnes := true + for _, r := range ratios { + if r != 1 { + allOnes = false + break + } + } + if allOnes { + return nil + } + fillIdx := 0 + for _, s := range p.Sample { + keepSample := false + for i, v := range s.Value { + if ratios[i] != 1 { + val := int64(math.Round(float64(v) * ratios[i])) + s.Value[i] = val + keepSample = keepSample || val != 0 + } + } + if keepSample { + p.Sample[fillIdx] = s + fillIdx++ + } + } + p.Sample = p.Sample[:fillIdx] + return nil +} + +// HasFunctions determines if all locations in this profile have +// symbolized function information. +func (p *Profile) HasFunctions() bool { + for _, l := range p.Location { + if l.Mapping != nil && !l.Mapping.HasFunctions { + return false + } + } + return true +} + +// HasFileLines determines if all locations in this profile have +// symbolized file and line number information. +func (p *Profile) HasFileLines() bool { + for _, l := range p.Location { + if l.Mapping != nil && (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { + return false + } + } + return true +} + +// 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. +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/") +} + +// Copy makes a fully independent copy of a profile. +func (p *Profile) Copy() *Profile { + pp := &Profile{} + if err := unmarshal(serialize(p), pp); err != nil { + panic(err) + } + if err := pp.postDecode(); err != nil { + panic(err) + } + + return pp +} diff --git a/vendor/github.com/google/pprof/profile/proto.go b/vendor/github.com/google/pprof/profile/proto.go new file mode 100644 index 000000000..a15696ba1 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/proto.go @@ -0,0 +1,367 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is a simple protocol buffer encoder and decoder. +// The format is described at +// https://developers.google.com/protocol-buffers/docs/encoding +// +// A protocol message must implement the message interface: +// decoder() []decoder +// encode(*buffer) +// +// The decode method returns a slice indexed by field number that gives the +// function to decode that field. +// The encode method encodes its receiver into the given buffer. +// +// The two methods are simple enough to be implemented by hand rather than +// by using a protocol compiler. +// +// See profile.go for examples of messages implementing this interface. +// +// There is no support for groups, message sets, or "has" bits. + +package profile + +import ( + "errors" + "fmt" +) + +type buffer struct { + field int // field tag + typ int // proto wire type code for field + u64 uint64 + data []byte + tmp [16]byte + tmpLines []Line // temporary storage used while decoding "repeated Line". +} + +type decoder func(*buffer, message) error + +type message interface { + decoder() []decoder + encode(*buffer) +} + +func marshal(m message) []byte { + var b buffer + m.encode(&b) + return b.data +} + +func encodeVarint(b *buffer, x uint64) { + for x >= 128 { + b.data = append(b.data, byte(x)|0x80) + x >>= 7 + } + b.data = append(b.data, byte(x)) +} + +func encodeLength(b *buffer, tag int, len int) { + encodeVarint(b, uint64(tag)<<3|2) + encodeVarint(b, uint64(len)) +} + +func encodeUint64(b *buffer, tag int, x uint64) { + // append varint to b.data + encodeVarint(b, uint64(tag)<<3) + encodeVarint(b, x) +} + +func encodeUint64s(b *buffer, tag int, x []uint64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, u) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeUint64(b, tag, u) + } +} + +func encodeUint64Opt(b *buffer, tag int, x uint64) { + if x == 0 { + return + } + encodeUint64(b, tag, x) +} + +func encodeInt64(b *buffer, tag int, x int64) { + u := uint64(x) + encodeUint64(b, tag, u) +} + +func encodeInt64s(b *buffer, tag int, x []int64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, uint64(u)) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeInt64(b, tag, u) + } +} + +func encodeInt64Opt(b *buffer, tag int, x int64) { + if x == 0 { + return + } + encodeInt64(b, tag, x) +} + +func encodeString(b *buffer, tag int, x string) { + encodeLength(b, tag, len(x)) + b.data = append(b.data, x...) +} + +func encodeStrings(b *buffer, tag int, x []string) { + for _, s := range x { + encodeString(b, tag, s) + } +} + +func encodeBool(b *buffer, tag int, x bool) { + if x { + encodeUint64(b, tag, 1) + } else { + encodeUint64(b, tag, 0) + } +} + +func encodeBoolOpt(b *buffer, tag int, x bool) { + if x { + encodeBool(b, tag, x) + } +} + +func encodeMessage(b *buffer, tag int, m message) { + n1 := len(b.data) + m.encode(b) + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) +} + +func unmarshal(data []byte, m message) (err error) { + b := buffer{data: data, typ: 2} + return decodeMessage(&b, m) +} + +func le64(p []byte) uint64 { + return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 +} + +func le32(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 +} + +func decodeVarint(data []byte) (uint64, []byte, error) { + var u uint64 + for i := 0; ; i++ { + if i >= 10 || i >= len(data) { + return 0, nil, errors.New("bad varint") + } + u |= uint64(data[i]&0x7F) << uint(7*i) + if data[i]&0x80 == 0 { + return u, data[i+1:], nil + } + } +} + +func decodeField(b *buffer, data []byte) ([]byte, error) { + x, data, err := decodeVarint(data) + if err != nil { + return nil, err + } + b.field = int(x >> 3) + b.typ = int(x & 7) + b.data = nil + b.u64 = 0 + switch b.typ { + case 0: + b.u64, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + case 1: + if len(data) < 8 { + return nil, errors.New("not enough data") + } + b.u64 = le64(data[:8]) + data = data[8:] + case 2: + var n uint64 + n, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + if n > uint64(len(data)) { + return nil, errors.New("too much data") + } + b.data = data[:n] + data = data[n:] + case 5: + if len(data) < 4 { + return nil, errors.New("not enough data") + } + b.u64 = uint64(le32(data[:4])) + data = data[4:] + default: + return nil, fmt.Errorf("unknown wire type: %d", b.typ) + } + + return data, nil +} + +func checkType(b *buffer, typ int) error { + if b.typ != typ { + return errors.New("type mismatch") + } + return nil +} + +func decodeMessage(b *buffer, m message) error { + if err := checkType(b, 2); err != nil { + return err + } + dec := m.decoder() + data := b.data + for len(data) > 0 { + // pull varint field# + type + var err error + data, err = decodeField(b, data) + if err != nil { + return err + } + if b.field >= len(dec) || dec[b.field] == nil { + continue + } + if err := dec[b.field](b, m); err != nil { + return err + } + } + return nil +} + +func decodeInt64(b *buffer, x *int64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = int64(b.u64) + return nil +} + +func decodeInt64s(b *buffer, x *[]int64) error { + if b.typ == 2 { + // Packed encoding + data := b.data + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, int64(u)) + } + return nil + } + var i int64 + if err := decodeInt64(b, &i); err != nil { + return err + } + *x = append(*x, i) + return nil +} + +func decodeUint64(b *buffer, x *uint64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = b.u64 + return nil +} + +func decodeUint64s(b *buffer, x *[]uint64) error { + if b.typ == 2 { + data := b.data + // Packed encoding + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, u) + } + return nil + } + var u uint64 + if err := decodeUint64(b, &u); err != nil { + return err + } + *x = append(*x, u) + return nil +} + +func decodeString(b *buffer, x *string) error { + if err := checkType(b, 2); err != nil { + return err + } + *x = string(b.data) + return nil +} + +func decodeStrings(b *buffer, x *[]string) error { + var s string + if err := decodeString(b, &s); err != nil { + return err + } + *x = append(*x, s) + return nil +} + +func decodeBool(b *buffer, x *bool) error { + if err := checkType(b, 0); err != nil { + return err + } + if int64(b.u64) == 0 { + *x = false + } else { + *x = true + } + return nil +} diff --git a/vendor/github.com/google/pprof/profile/prune.go b/vendor/github.com/google/pprof/profile/prune.go new file mode 100644 index 000000000..b2f9fd546 --- /dev/null +++ b/vendor/github.com/google/pprof/profile/prune.go @@ -0,0 +1,194 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Implements methods to remove frames from profiles. + +package profile + +import ( + "fmt" + "regexp" + "strings" +) + +var ( + reservedNames = []string{"(anonymous namespace)", "operator()"} + bracketRx = func() *regexp.Regexp { + var quotedNames []string + for _, name := range append(reservedNames, "(") { + quotedNames = append(quotedNames, regexp.QuoteMeta(name)) + } + return regexp.MustCompile(strings.Join(quotedNames, "|")) + }() +) + +// simplifyFunc does some primitive simplification of function names. +func simplifyFunc(f string) string { + // Account for leading '.' on the PPC ELF v1 ABI. + funcName := strings.TrimPrefix(f, ".") + // Account for unsimplified names -- try to remove the argument list by trimming + // starting from the first '(', but skipping reserved names that have '('. + for _, ind := range bracketRx.FindAllStringSubmatchIndex(funcName, -1) { + foundReserved := false + for _, res := range reservedNames { + if funcName[ind[0]:ind[1]] == res { + foundReserved = true + break + } + } + if !foundReserved { + funcName = funcName[:ind[0]] + break + } + } + return funcName +} + +// Prune removes all nodes beneath a node matching dropRx, and not +// matching keepRx. If the root node of a Sample matches, the sample +// will have an empty stack. +func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { + prune := make(map[uint64]bool) + pruneBeneath := make(map[uint64]bool) + + // simplifyFunc can be expensive, so cache results. + // Note that the same function name can be encountered many times due + // different lines and addresses in the same function. + pruneCache := map[string]bool{} // Map from function to whether or not to prune + pruneFromHere := func(s string) bool { + if r, ok := pruneCache[s]; ok { + return r + } + funcName := simplifyFunc(s) + if dropRx.MatchString(funcName) { + if keepRx == nil || !keepRx.MatchString(funcName) { + pruneCache[s] = true + return true + } + } + pruneCache[s] = false + return false + } + + for _, loc := range p.Location { + var i int + for i = len(loc.Line) - 1; i >= 0; i-- { + if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { + if pruneFromHere(fn.Name) { + break + } + } + } + + if i >= 0 { + // Found matching entry to prune. + pruneBeneath[loc.ID] = true + + // Remove the matching location. + if i == len(loc.Line)-1 { + // Matched the top entry: prune the whole location. + prune[loc.ID] = true + } else { + loc.Line = loc.Line[i+1:] + } + } + } + + // Prune locs from each Sample + for _, sample := range p.Sample { + // Scan from the root to the leaves to find the prune location. + // Do not prune frames before the first user frame, to avoid + // pruning everything. + foundUser := false + for i := len(sample.Location) - 1; i >= 0; i-- { + id := sample.Location[i].ID + if !prune[id] && !pruneBeneath[id] { + foundUser = true + continue + } + if !foundUser { + continue + } + if prune[id] { + sample.Location = sample.Location[i+1:] + break + } + if pruneBeneath[id] { + sample.Location = sample.Location[i:] + break + } + } + } +} + +// RemoveUninteresting prunes and elides profiles using built-in +// tables of uninteresting function names. +func (p *Profile) RemoveUninteresting() error { + var keep, drop *regexp.Regexp + var err error + + if p.DropFrames != "" { + if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) + } + if p.KeepFrames != "" { + if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) + } + } + p.Prune(drop, keep) + } + return nil +} + +// PruneFrom removes all nodes beneath the lowest node matching dropRx, not including itself. +// +// Please see the example below to understand this method as well as +// the difference from Prune method. +// +// A sample contains Location of [A,B,C,B,D] where D is the top frame and there's no inline. +// +// PruneFrom(A) returns [A,B,C,B,D] because there's no node beneath A. +// Prune(A, nil) returns [B,C,B,D] by removing A itself. +// +// PruneFrom(B) returns [B,C,B,D] by removing all nodes beneath the first B when scanning from the bottom. +// Prune(B, nil) returns [D] because a matching node is found by scanning from the root. +func (p *Profile) PruneFrom(dropRx *regexp.Regexp) { + pruneBeneath := make(map[uint64]bool) + + for _, loc := range p.Location { + for i := 0; i < len(loc.Line); i++ { + if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { + funcName := simplifyFunc(fn.Name) + if dropRx.MatchString(funcName) { + // Found matching entry to prune. + pruneBeneath[loc.ID] = true + loc.Line = loc.Line[i:] + break + } + } + } + } + + // Prune locs from each Sample + for _, sample := range p.Sample { + // Scan from the bottom leaf to the root to find the prune location. + for i, loc := range sample.Location { + if pruneBeneath[loc.ID] { + sample.Location = sample.Location[i:] + break + } + } + } +} diff --git a/vendor/github.com/google/uuid/.travis.yml b/vendor/github.com/google/uuid/.travis.yml deleted file mode 100644 index d8156a60b..000000000 --- a/vendor/github.com/google/uuid/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go - -go: - - 1.4.3 - - 1.5.3 - - tip - -script: - - go test -v ./... diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md new file mode 100644 index 000000000..7ec5ac7ea --- /dev/null +++ b/vendor/github.com/google/uuid/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16) + + +### Features + +* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3)) + + +### Bug Fixes + +* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06)) +* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6)) + +## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12) + + +### Features + +* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29)) + +## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) + + +### Features + +* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) + +### Fixes + +* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) + +## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) + + +### Bug Fixes + +* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) + +## Changelog diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md index 04fdf09f1..a502fdc51 100644 --- a/vendor/github.com/google/uuid/CONTRIBUTING.md +++ b/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -2,6 +2,22 @@ We definitely welcome patches and contribution to this project! +### Tips + +Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). + +Always try to include a test case! If it is not possible or not necessary, +please explain why in the pull request description. + +### Releasing + +Commits that would precipitate a SemVer change, as described in the Conventional +Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) +to create a release candidate pull request. Once submitted, `release-please` +will create a release. + +For tips on how to work with `release-please`, see its documentation. + ### Legal requirements In order to protect both you and ourselves, you will need to sign the diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md index f765a46f9..3e9a61889 100644 --- a/vendor/github.com/google/uuid/README.md +++ b/vendor/github.com/google/uuid/README.md @@ -1,6 +1,6 @@ -# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +# uuid The uuid package generates and inspects UUIDs based on -[RFC 4122](http://tools.ietf.org/html/rfc4122) +[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) and DCE 1.1: Authentication and Security Services. This package is based on the github.com/pborman/uuid package (previously named @@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this change is the ability to represent an invalid UUID (vs a NIL UUID). ###### Install -`go get github.com/google/uuid` +```sh +go get github.com/google/uuid +``` ###### Documentation -[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) +[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here: diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go index b404f4bec..dc60082d3 100644 --- a/vendor/github.com/google/uuid/hash.go +++ b/vendor/github.com/google/uuid/hash.go @@ -17,6 +17,12 @@ var ( NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) Nil UUID // empty UUID, all zeros + + // The Max UUID is special form of UUID that is specified to have all 128 bits set to 1. + Max = UUID{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + } ) // NewHash returns a new UUID derived from the hash of space concatenated with diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go index 24b78edc9..b2a0bc871 100644 --- a/vendor/github.com/google/uuid/node_js.go +++ b/vendor/github.com/google/uuid/node_js.go @@ -7,6 +7,6 @@ package uuid // getHardwareInterface returns nil values for the JS version of the code. -// This remvoves the "net" dependency, because it is not used in the browser. +// This removes the "net" dependency, because it is not used in the browser. // Using the "net" library inflates the size of the transpiled JS code by 673k bytes. func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go index e6ef06cdc..c35112927 100644 --- a/vendor/github.com/google/uuid/time.go +++ b/vendor/github.com/google/uuid/time.go @@ -108,12 +108,23 @@ func setClockSequence(seq int) { } // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in -// uuid. The time is only defined for version 1 and 2 UUIDs. +// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs. func (uuid UUID) Time() Time { - time := int64(binary.BigEndian.Uint32(uuid[0:4])) - time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 - time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 - return Time(time) + var t Time + switch uuid.Version() { + case 6: + time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110 + t = Time(time) + case 7: + time := binary.BigEndian.Uint64(uuid[:8]) + t = Time((time>>16)*10000 + g1582ns100) + default: // forward compatible + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + t = Time(time) + } + return t } // ClockSequence returns the clock sequence encoded in uuid. diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index a57207aeb..5232b4867 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool { return ok } -// Parse decodes s into a UUID or returns an error. Both the standard UUID -// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the -// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex -// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. +// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both +// the standard UUID forms defined in RFC 4122 +// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, +// Parse accepts non-standard strings such as the raw hex encoding +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, +// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are +// examined in the latter case. Parse should not be used to validate strings as +// it parses non-standard encodings as indicated above. func Parse(s string) (UUID, error) { var uuid UUID switch len(s) { @@ -69,7 +73,7 @@ func Parse(s string) (UUID, error) { // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: - if strings.ToLower(s[:9]) != "urn:uuid:" { + if !strings.EqualFold(s[:9], "urn:uuid:") { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] @@ -101,7 +105,8 @@ func Parse(s string) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(s[x], s[x+1]) if !ok { return uuid, errors.New("invalid UUID format") @@ -117,7 +122,7 @@ func ParseBytes(b []byte) (UUID, error) { switch len(b) { case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] @@ -145,7 +150,8 @@ func ParseBytes(b []byte) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(b[x], b[x+1]) if !ok { return uuid, errors.New("invalid UUID format") @@ -180,6 +186,59 @@ func Must(uuid UUID, err error) UUID { return uuid } +// Validate returns an error if s is not a properly formatted UUID in one of the following formats: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} +// It returns an error if the format is invalid, otherwise nil. +func Validate(s string) error { + switch len(s) { + // Standard UUID format + case 36: + + // UUID with "urn:uuid:" prefix + case 36 + 9: + if !strings.EqualFold(s[:9], "urn:uuid:") { + return fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // UUID enclosed in braces + case 36 + 2: + if s[0] != '{' || s[len(s)-1] != '}' { + return fmt.Errorf("invalid bracketed UUID format") + } + s = s[1 : len(s)-1] + + // UUID without hyphens + case 32: + for i := 0; i < len(s); i += 2 { + _, ok := xtob(s[i], s[i+1]) + if !ok { + return errors.New("invalid UUID format") + } + } + + default: + return invalidLengthError{len(s)} + } + + // Check for standard UUID format + if len(s) == 36 { + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return errors.New("invalid UUID format") + } + for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { + if _, ok := xtob(s[x], s[x+1]); !ok { + return errors.New("invalid UUID format") + } + } + } + + return nil +} + // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // , or "" if uuid is invalid. func (uuid UUID) String() string { @@ -292,3 +351,15 @@ func DisableRandPool() { poolMu.Lock() poolPos = randPoolSize } + +// UUIDs is a slice of UUID types. +type UUIDs []UUID + +// Strings returns a string slice containing the string form of each UUID in uuids. +func (uuids UUIDs) Strings() []string { + var uuidStrs = make([]string, len(uuids)) + for i, uuid := range uuids { + uuidStrs[i] = uuid.String() + } + return uuidStrs +} diff --git a/vendor/github.com/google/uuid/version6.go b/vendor/github.com/google/uuid/version6.go new file mode 100644 index 000000000..339a959a7 --- /dev/null +++ b/vendor/github.com/google/uuid/version6.go @@ -0,0 +1,56 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "encoding/binary" + +// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality. +// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs. +// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6 +// +// NewV6 returns a Version 6 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewV6 returns Nil and an error. +func NewV6() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_high | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_mid | time_low_and_version | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |clk_seq_hi_res | clk_seq_low | node (0-1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | node (2-5) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + binary.BigEndian.PutUint64(uuid[0:], uint64(now)) + binary.BigEndian.PutUint16(uuid[8:], seq) + + uuid[6] = 0x60 | (uuid[6] & 0x0F) + uuid[8] = 0x80 | (uuid[8] & 0x3F) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/vendor/github.com/google/uuid/version7.go b/vendor/github.com/google/uuid/version7.go new file mode 100644 index 000000000..3167b643d --- /dev/null +++ b/vendor/github.com/google/uuid/version7.go @@ -0,0 +1,104 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// UUID version 7 features a time-ordered value field derived from the widely +// implemented and well known Unix Epoch timestamp source, +// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded. +// As well as improved entropy characteristics over versions 1 or 6. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7 +// +// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible. +// +// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch). +// Uses the randomness pool if it was enabled with EnableRandPool. +// On error, NewV7 returns Nil and an error +func NewV7() (UUID, error) { + uuid, err := NewRandom() + if err != nil { + return uuid, err + } + makeV7(uuid[:]) + return uuid, nil +} + +// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch). +// it use NewRandomFromReader fill random bits. +// On error, NewV7FromReader returns Nil and an error. +func NewV7FromReader(r io.Reader) (UUID, error) { + uuid, err := NewRandomFromReader(r) + if err != nil { + return uuid, err + } + + makeV7(uuid[:]) + return uuid, nil +} + +// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6]) +// uuid[8] already has the right version number (Variant is 10) +// see function NewV7 and NewV7FromReader +func makeV7(uuid []byte) { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | ver | rand_a (12 bit seq) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |var| rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + _ = uuid[15] // bounds check + + t, s := getV7Time() + + uuid[0] = byte(t >> 40) + uuid[1] = byte(t >> 32) + uuid[2] = byte(t >> 24) + uuid[3] = byte(t >> 16) + uuid[4] = byte(t >> 8) + uuid[5] = byte(t) + + uuid[6] = 0x70 | (0x0F & byte(s>>8)) + uuid[7] = byte(s) +} + +// lastV7time is the last time we returned stored as: +// +// 52 bits of time in milliseconds since epoch +// 12 bits of (fractional nanoseconds) >> 8 +var lastV7time int64 + +const nanoPerMilli = 1000000 + +// getV7Time returns the time in milliseconds and nanoseconds / 256. +// The returned (milli << 12 + seq) is guarenteed to be greater than +// (milli << 12 + seq) returned by any previous call to getV7Time. +func getV7Time() (milli, seq int64) { + timeMu.Lock() + defer timeMu.Unlock() + + nano := timeNow().UnixNano() + milli = nano / nanoPerMilli + // Sequence number is between 0 and 3906 (nanoPerMilli>>8) + seq = (nano - milli*nanoPerMilli) >> 8 + now := milli<<12 + seq + if now <= lastV7time { + now = lastV7time + 1 + milli = now >> 12 + seq = now & 0xfff + } + lastV7time = now + return milli, seq +} diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE new file mode 100644 index 000000000..c33dcc7c9 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor†+ + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version†+ + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution†+ + means Covered Software of a particular Contributor. + +1.4. “Covered Software†+ + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses†+ means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form†+ + means any form of the work other than Source Code Form. + +1.7. “Larger Work†+ + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License†+ + means this document. + +1.9. “Licensable†+ + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications†+ + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims†of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License†+ + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form†+ + means the form of the work preferred for making modifications. + +1.14. “You†(or “Yourâ€) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You†includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control†means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is†basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses†Notice + + This Source Code Form is “Incompatible + With Secondary Licensesâ€, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md new file mode 100644 index 000000000..444df08f8 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/README.md @@ -0,0 +1,89 @@ +# errwrap + +`errwrap` is a package for Go that formalizes the pattern of wrapping errors +and checking if an error contains another error. + +There is a common pattern in Go of taking a returned `error` value and +then wrapping it (such as with `fmt.Errorf`) before returning it. The problem +with this pattern is that you completely lose the original `error` structure. + +Arguably the _correct_ approach is that you should make a custom structure +implementing the `error` interface, and have the original error as a field +on that structure, such [as this example](http://golang.org/pkg/os/#PathError). +This is a good approach, but you have to know the entire chain of possible +rewrapping that happens, when you might just care about one. + +`errwrap` formalizes this pattern (it doesn't matter what approach you use +above) by giving a single interface for wrapping errors, checking if a specific +error is wrapped, and extracting that error. + +## Installation and Docs + +Install using `go get github.com/hashicorp/errwrap`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/errwrap + +## Usage + +#### Basic Usage + +Below is a very basic example of its usage: + +```go +// A function that always returns an error, but wraps it, like a real +// function might. +func tryOpen() error { + _, err := os.Open("/i/dont/exist") + if err != nil { + return errwrap.Wrapf("Doesn't exist: {{err}}", err) + } + + return nil +} + +func main() { + err := tryOpen() + + // We can use the Contains helpers to check if an error contains + // another error. It is safe to do this with a nil error, or with + // an error that doesn't even use the errwrap package. + if errwrap.Contains(err, "does not exist") { + // Do something + } + if errwrap.ContainsType(err, new(os.PathError)) { + // Do something + } + + // Or we can use the associated `Get` functions to just extract + // a specific error. This would return nil if that specific error doesn't + // exist. + perr := errwrap.GetType(err, new(os.PathError)) +} +``` + +#### Custom Types + +If you're already making custom types that properly wrap errors, then +you can get all the functionality of `errwraps.Contains` and such by +implementing the `Wrapper` interface with just one function. Example: + +```go +type AppError { + Code ErrorCode + Err error +} + +func (e *AppError) WrappedErrors() []error { + return []error{e.Err} +} +``` + +Now this works: + +```go +err := &AppError{Err: fmt.Errorf("an error")} +if errwrap.ContainsType(err, fmt.Errorf("")) { + // This will work! +} +``` diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go new file mode 100644 index 000000000..44e368e56 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/errwrap.go @@ -0,0 +1,178 @@ +// Package errwrap implements methods to formalize error wrapping in Go. +// +// All of the top-level functions that take an `error` are built to be able +// to take any error, not just wrapped errors. This allows you to use errwrap +// without having to type-check and type-cast everywhere. +package errwrap + +import ( + "errors" + "reflect" + "strings" +) + +// WalkFunc is the callback called for Walk. +type WalkFunc func(error) + +// Wrapper is an interface that can be implemented by custom types to +// have all the Contains, Get, etc. functions in errwrap work. +// +// When Walk reaches a Wrapper, it will call the callback for every +// wrapped error in addition to the wrapper itself. Since all the top-level +// functions in errwrap use Walk, this means that all those functions work +// with your custom type. +type Wrapper interface { + WrappedErrors() []error +} + +// Wrap defines that outer wraps inner, returning an error type that +// can be cleanly used with the other methods in this package, such as +// Contains, GetAll, etc. +// +// This function won't modify the error message at all (the outer message +// will be used). +func Wrap(outer, inner error) error { + return &wrappedError{ + Outer: outer, + Inner: inner, + } +} + +// Wrapf wraps an error with a formatting message. This is similar to using +// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap +// errors, you should replace it with this. +// +// format is the format of the error message. The string '{{err}}' will +// be replaced with the original error message. +// +// Deprecated: Use fmt.Errorf() +func Wrapf(format string, err error) error { + outerMsg := "" + if err != nil { + outerMsg = err.Error() + } + + outer := errors.New(strings.Replace( + format, "{{err}}", outerMsg, -1)) + + return Wrap(outer, err) +} + +// Contains checks if the given error contains an error with the +// message msg. If err is not a wrapped error, this will always return +// false unless the error itself happens to match this msg. +func Contains(err error, msg string) bool { + return len(GetAll(err, msg)) > 0 +} + +// ContainsType checks if the given error contains an error with +// the same concrete type as v. If err is not a wrapped error, this will +// check the err itself. +func ContainsType(err error, v interface{}) bool { + return len(GetAllType(err, v)) > 0 +} + +// Get is the same as GetAll but returns the deepest matching error. +func Get(err error, msg string) error { + es := GetAll(err, msg) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetType is the same as GetAllType but returns the deepest matching error. +func GetType(err error, v interface{}) error { + es := GetAllType(err, v) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetAll gets all the errors that might be wrapped in err with the +// given message. The order of the errors is such that the outermost +// matching error (the most recent wrap) is index zero, and so on. +func GetAll(err error, msg string) []error { + var result []error + + Walk(err, func(err error) { + if err.Error() == msg { + result = append(result, err) + } + }) + + return result +} + +// GetAllType gets all the errors that are the same type as v. +// +// The order of the return value is the same as described in GetAll. +func GetAllType(err error, v interface{}) []error { + var result []error + + var search string + if v != nil { + search = reflect.TypeOf(v).String() + } + Walk(err, func(err error) { + var needle string + if err != nil { + needle = reflect.TypeOf(err).String() + } + + if needle == search { + result = append(result, err) + } + }) + + return result +} + +// Walk walks all the wrapped errors in err and calls the callback. If +// err isn't a wrapped error, this will be called once for err. If err +// is a wrapped error, the callback will be called for both the wrapper +// that implements error as well as the wrapped error itself. +func Walk(err error, cb WalkFunc) { + if err == nil { + return + } + + switch e := err.(type) { + case *wrappedError: + cb(e.Outer) + Walk(e.Inner, cb) + case Wrapper: + cb(err) + + for _, err := range e.WrappedErrors() { + Walk(err, cb) + } + case interface{ Unwrap() error }: + cb(err) + Walk(e.Unwrap(), cb) + default: + cb(err) + } +} + +// wrappedError is an implementation of error that has both the +// outer and inner errors. +type wrappedError struct { + Outer error + Inner error +} + +func (w *wrappedError) Error() string { + return w.Outer.Error() +} + +func (w *wrappedError) WrappedErrors() []error { + return []error{w.Outer, w.Inner} +} + +func (w *wrappedError) Unwrap() error { + return w.Inner +} diff --git a/vendor/github.com/hashicorp/go-multierror/LICENSE b/vendor/github.com/hashicorp/go-multierror/LICENSE new file mode 100644 index 000000000..82b4de97c --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor†+ + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version†+ + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution†+ + means Covered Software of a particular Contributor. + +1.4. “Covered Software†+ + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses†+ means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form†+ + means any form of the work other than Source Code Form. + +1.7. “Larger Work†+ + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License†+ + means this document. + +1.9. “Licensable†+ + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications†+ + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims†of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License†+ + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form†+ + means the form of the work preferred for making modifications. + +1.14. “You†(or “Yourâ€) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You†includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control†means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is†basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses†Notice + + This Source Code Form is “Incompatible + With Secondary Licensesâ€, as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-multierror/Makefile b/vendor/github.com/hashicorp/go-multierror/Makefile new file mode 100644 index 000000000..b97cd6ed0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/Makefile @@ -0,0 +1,31 @@ +TEST?=./... + +default: test + +# test runs the test suite and vets the code. +test: generate + @echo "==> Running tests..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} + +# testrace runs the race checker +testrace: generate + @echo "==> Running tests (race)..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -race ${TESTARGS} + +# updatedeps installs all the dependencies needed to run and build. +updatedeps: + @sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" + +# generate runs `go generate` to build the dynamically generated source files. +generate: + @echo "==> Generating..." + @find . -type f -name '.DS_Store' -delete + @go list ./... \ + | grep -v "/vendor/" \ + | xargs -n1 go generate + +.PHONY: default test testrace updatedeps generate diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md new file mode 100644 index 000000000..71dd308ed --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/README.md @@ -0,0 +1,150 @@ +# go-multierror + +[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror) +[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror) + +[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror +[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror + +`go-multierror` is a package for Go that provides a mechanism for +representing a list of `error` values as a single `error`. + +This allows a function in Go to return an `error` that might actually +be a list of errors. If the caller knows this, they can unwrap the +list and access the errors. If the caller doesn't know, the error +formats to a nice human-readable format. + +`go-multierror` is fully compatible with the Go standard library +[errors](https://golang.org/pkg/errors/) package, including the +functions `As`, `Is`, and `Unwrap`. This provides a standardized approach +for introspecting on error values. + +## Installation and Docs + +Install using `go get github.com/hashicorp/go-multierror`. + +Full documentation is available at +https://pkg.go.dev/github.com/hashicorp/go-multierror + +### Requires go version 1.13 or newer + +`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced +[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which +this library takes advantage of. + +If you need to use an earlier version of go, you can use the +[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0) +tag, which doesn't rely on features in go 1.13. + +If you see compile errors that look like the below, it's likely that +you're on an older version of go: + +``` +/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As +/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is +``` + +## Usage + +go-multierror is easy to use and purposely built to be unobtrusive in +existing Go applications/libraries that may not be aware of it. + +**Building a list of errors** + +The `Append` function is used to create a list of errors. This function +behaves a lot like the Go built-in `append` function: it doesn't matter +if the first argument is nil, a `multierror.Error`, or any other `error`, +the function behaves as you would expect. + +```go +var result error + +if err := step1(); err != nil { + result = multierror.Append(result, err) +} +if err := step2(); err != nil { + result = multierror.Append(result, err) +} + +return result +``` + +**Customizing the formatting of the errors** + +By specifying a custom `ErrorFormat`, you can customize the format +of the `Error() string` function: + +```go +var result *multierror.Error + +// ... accumulate errors here, maybe using Append + +if result != nil { + result.ErrorFormat = func([]error) string { + return "errors!" + } +} +``` + +**Accessing the list of errors** + +`multierror.Error` implements `error` so if the caller doesn't know about +multierror, it will work just fine. But if you're aware a multierror might +be returned, you can use type switches to access the list of errors: + +```go +if err := something(); err != nil { + if merr, ok := err.(*multierror.Error); ok { + // Use merr.Errors + } +} +``` + +You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap) +function. This will continue to unwrap into subsequent errors until none exist. + +**Extracting an error** + +The standard library [`errors.As`](https://golang.org/pkg/errors/#As) +function can be used directly with a multierror to extract a specific error: + +```go +// Assume err is a multierror value +err := somefunc() + +// We want to know if "err" has a "RichErrorType" in it and extract it. +var errRich RichErrorType +if errors.As(err, &errRich) { + // It has it, and now errRich is populated. +} +``` + +**Checking for an exact error value** + +Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables) +error in the `os` package. You can check if this error is present by using +the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function. + +```go +// Assume err is a multierror value +err := somefunc() +if errors.Is(err, os.ErrNotExist) { + // err contains os.ErrNotExist +} +``` + +**Returning a multierror only if there are errors** + +If you build a `multierror.Error`, you can use the `ErrorOrNil` function +to return an `error` implementation only if there are errors to return: + +```go +var result *multierror.Error + +// ... accumulate errors here + +// Return the `error` only if errors were added to the multierror, otherwise +// return nil since there are no errors. +return result.ErrorOrNil() +``` diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go new file mode 100644 index 000000000..3e2589bfd --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/append.go @@ -0,0 +1,43 @@ +package multierror + +// Append is a helper function that will append more errors +// onto an Error in order to create a larger multi-error. +// +// If err is not a multierror.Error, then it will be turned into +// one. If any of the errs are multierr.Error, they will be flattened +// one level into err. +// Any nil errors within errs will be ignored. If err is nil, a new +// *Error will be returned. +func Append(err error, errs ...error) *Error { + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Go through each error and flatten + for _, e := range errs { + switch e := e.(type) { + case *Error: + if e != nil { + err.Errors = append(err.Errors, e.Errors...) + } + default: + if e != nil { + err.Errors = append(err.Errors, e) + } + } + } + + return err + default: + newErrs := make([]error, 0, len(errs)+1) + if err != nil { + newErrs = append(newErrs, err) + } + newErrs = append(newErrs, errs...) + + return Append(&Error{}, newErrs...) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go new file mode 100644 index 000000000..aab8e9abe --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/flatten.go @@ -0,0 +1,26 @@ +package multierror + +// Flatten flattens the given error, merging any *Errors together into +// a single *Error. +func Flatten(err error) error { + // If it isn't an *Error, just return the error as-is + if _, ok := err.(*Error); !ok { + return err + } + + // Otherwise, make the result and flatten away! + flatErr := new(Error) + flatten(err, flatErr) + return flatErr +} + +func flatten(err error, flatErr *Error) { + switch err := err.(type) { + case *Error: + for _, e := range err.Errors { + flatten(e, flatErr) + } + default: + flatErr.Errors = append(flatErr.Errors, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go new file mode 100644 index 000000000..47f13c49a --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/format.go @@ -0,0 +1,27 @@ +package multierror + +import ( + "fmt" + "strings" +) + +// ErrorFormatFunc is a function callback that is called by Error to +// turn the list of errors into a string. +type ErrorFormatFunc func([]error) string + +// ListFormatFunc is a basic formatter that outputs the number of errors +// that occurred along with a bullet point list of the errors. +func ListFormatFunc(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + + points := make([]string, len(es)) + for i, err := range es { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) +} diff --git a/vendor/github.com/hashicorp/go-multierror/group.go b/vendor/github.com/hashicorp/go-multierror/group.go new file mode 100644 index 000000000..9c29efb7f --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/group.go @@ -0,0 +1,38 @@ +package multierror + +import "sync" + +// Group is a collection of goroutines which return errors that need to be +// coalesced. +type Group struct { + mutex sync.Mutex + err *Error + wg sync.WaitGroup +} + +// Go calls the given function in a new goroutine. +// +// If the function returns an error it is added to the group multierror which +// is returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.mutex.Lock() + g.err = Append(g.err, err) + g.mutex.Unlock() + } + }() +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the multierror. +func (g *Group) Wait() *Error { + g.wg.Wait() + g.mutex.Lock() + defer g.mutex.Unlock() + return g.err +} diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go new file mode 100644 index 000000000..f54574326 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/multierror.go @@ -0,0 +1,121 @@ +package multierror + +import ( + "errors" + "fmt" +) + +// Error is an error type to track multiple errors. This is used to +// accumulate errors in cases and return them as a single "error". +type Error struct { + Errors []error + ErrorFormat ErrorFormatFunc +} + +func (e *Error) Error() string { + fn := e.ErrorFormat + if fn == nil { + fn = ListFormatFunc + } + + return fn(e.Errors) +} + +// ErrorOrNil returns an error interface if this Error represents +// a list of errors, or returns nil if the list of errors is empty. This +// function is useful at the end of accumulation to make sure that the value +// returned represents the existence of errors. +func (e *Error) ErrorOrNil() error { + if e == nil { + return nil + } + if len(e.Errors) == 0 { + return nil + } + + return e +} + +func (e *Error) GoString() string { + return fmt.Sprintf("*%#v", *e) +} + +// WrappedErrors returns the list of errors that this Error is wrapping. It is +// an implementation of the errwrap.Wrapper interface so that multierror.Error +// can be used with that library. +// +// This method is not safe to be called concurrently. Unlike accessing the +// Errors field directly, this function also checks if the multierror is nil to +// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. +func (e *Error) WrappedErrors() []error { + if e == nil { + return nil + } + return e.Errors +} + +// Unwrap returns an error from Error (or nil if there are no errors). +// This error returned will further support Unwrap to get the next error, +// etc. The order will match the order of Errors in the multierror.Error +// at the time of calling. +// +// The resulting error supports errors.As/Is/Unwrap so you can continue +// to use the stdlib errors package to introspect further. +// +// This will perform a shallow copy of the errors slice. Any errors appended +// to this error after calling Unwrap will not be available until a new +// Unwrap is called on the multierror.Error. +func (e *Error) Unwrap() error { + // If we have no errors then we do nothing + if e == nil || len(e.Errors) == 0 { + return nil + } + + // If we have exactly one error, we can just return that directly. + if len(e.Errors) == 1 { + return e.Errors[0] + } + + // Shallow copy the slice + errs := make([]error, len(e.Errors)) + copy(errs, e.Errors) + return chain(errs) +} + +// chain implements the interfaces necessary for errors.Is/As/Unwrap to +// work in a deterministic way with multierror. A chain tracks a list of +// errors while accounting for the current represented error. This lets +// Is/As be meaningful. +// +// Unwrap returns the next error. In the cleanest form, Unwrap would return +// the wrapped error here but we can't do that if we want to properly +// get access to all the errors. Instead, users are recommended to use +// Is/As to get the correct error type out. +// +// Precondition: []error is non-empty (len > 0) +type chain []error + +// Error implements the error interface +func (e chain) Error() string { + return e[0].Error() +} + +// Unwrap implements errors.Unwrap by returning the next error in the +// chain or nil if there are no more errors. +func (e chain) Unwrap() error { + if len(e) == 1 { + return nil + } + + return e[1:] +} + +// As implements errors.As by attempting to map to the current value. +func (e chain) As(target interface{}) bool { + return errors.As(e[0], target) +} + +// Is implements errors.Is by comparing the current value directly. +func (e chain) Is(target error) bool { + return errors.Is(e[0], target) +} diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go new file mode 100644 index 000000000..5c477abe4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/prefix.go @@ -0,0 +1,37 @@ +package multierror + +import ( + "fmt" + + "github.com/hashicorp/errwrap" +) + +// Prefix is a helper function that will prefix some text +// to the given error. If the error is a multierror.Error, then +// it will be prefixed to each wrapped error. +// +// This is useful to use when appending multiple multierrors +// together in order to give better scoping. +func Prefix(err error, prefix string) error { + if err == nil { + return nil + } + + format := fmt.Sprintf("%s {{err}}", prefix) + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Wrap each of the errors + for i, e := range err.Errors { + err.Errors[i] = errwrap.Wrapf(format, e) + } + + return err + default: + return errwrap.Wrapf(format, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go new file mode 100644 index 000000000..fecb14e81 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/sort.go @@ -0,0 +1,16 @@ +package multierror + +// Len implements sort.Interface function for length +func (err Error) Len() int { + return len(err.Errors) +} + +// Swap implements sort.Interface function for swapping elements +func (err Error) Swap(i, j int) { + err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] +} + +// Less implements sort.Interface function for determining order +func (err Error) Less(i, j int) bool { + return err.Errors[i].Error() < err.Errors[j].Error() +} diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_others.go b/vendor/github.com/inconshreveable/mousetrap/trap_others.go index 9d2d8a4ba..06a91f086 100644 --- a/vendor/github.com/inconshreveable/mousetrap/trap_others.go +++ b/vendor/github.com/inconshreveable/mousetrap/trap_others.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package mousetrap diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_windows.go b/vendor/github.com/inconshreveable/mousetrap/trap_windows.go index 336142a5e..0c5688021 100644 --- a/vendor/github.com/inconshreveable/mousetrap/trap_windows.go +++ b/vendor/github.com/inconshreveable/mousetrap/trap_windows.go @@ -1,81 +1,32 @@ -// +build windows -// +build !go1.4 - package mousetrap import ( - "fmt" - "os" "syscall" "unsafe" ) -const ( - // defined by the Win32 API - th32cs_snapprocess uintptr = 0x2 -) - -var ( - kernel = syscall.MustLoadDLL("kernel32.dll") - CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot") - Process32First = kernel.MustFindProc("Process32FirstW") - Process32Next = kernel.MustFindProc("Process32NextW") -) - -// ProcessEntry32 structure defined by the Win32 API -type processEntry32 struct { - dwSize uint32 - cntUsage uint32 - th32ProcessID uint32 - th32DefaultHeapID int - th32ModuleID uint32 - cntThreads uint32 - th32ParentProcessID uint32 - pcPriClassBase int32 - dwFlags uint32 - szExeFile [syscall.MAX_PATH]uint16 -} - -func getProcessEntry(pid int) (pe *processEntry32, err error) { - snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0)) - if snapshot == uintptr(syscall.InvalidHandle) { - err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1) - return +func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { + snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) + if err != nil { + return nil, err } - defer syscall.CloseHandle(syscall.Handle(snapshot)) - - var processEntry processEntry32 - processEntry.dwSize = uint32(unsafe.Sizeof(processEntry)) - ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry))) - if ok == 0 { - err = fmt.Errorf("Process32First: %v", e1) - return + defer syscall.CloseHandle(snapshot) + var procEntry syscall.ProcessEntry32 + procEntry.Size = uint32(unsafe.Sizeof(procEntry)) + if err = syscall.Process32First(snapshot, &procEntry); err != nil { + return nil, err } - for { - if processEntry.th32ProcessID == uint32(pid) { - pe = &processEntry - return + if procEntry.ProcessID == uint32(pid) { + return &procEntry, nil } - - ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry))) - if ok == 0 { - err = fmt.Errorf("Process32Next: %v", e1) - return + err = syscall.Process32Next(snapshot, &procEntry) + if err != nil { + return nil, err } } } -func getppid() (pid int, err error) { - pe, err := getProcessEntry(os.Getpid()) - if err != nil { - return - } - - pid = int(pe.th32ParentProcessID) - return -} - // StartedByExplorer returns true if the program was invoked by the user double-clicking // on the executable from explorer.exe // @@ -83,16 +34,9 @@ func getppid() (pid int, err error) { // It does not guarantee that the program was run from a terminal. It only can tell you // whether it was launched from explorer.exe func StartedByExplorer() bool { - ppid, err := getppid() + pe, err := getProcessEntry(syscall.Getppid()) if err != nil { return false } - - pe, err := getProcessEntry(ppid) - if err != nil { - return false - } - - name := syscall.UTF16ToString(pe.szExeFile[:]) - return name == "explorer.exe" + return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) } diff --git a/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go b/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go deleted file mode 100644 index 9a28e57c3..000000000 --- a/vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build windows -// +build go1.4 - -package mousetrap - -import ( - "os" - "syscall" - "unsafe" -) - -func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { - snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) - if err != nil { - return nil, err - } - defer syscall.CloseHandle(snapshot) - var procEntry syscall.ProcessEntry32 - procEntry.Size = uint32(unsafe.Sizeof(procEntry)) - if err = syscall.Process32First(snapshot, &procEntry); err != nil { - return nil, err - } - for { - if procEntry.ProcessID == uint32(pid) { - return &procEntry, nil - } - err = syscall.Process32Next(snapshot, &procEntry) - if err != nil { - return nil, err - } - } -} - -// StartedByExplorer returns true if the program was invoked by the user double-clicking -// on the executable from explorer.exe -// -// It is conservative and returns false if any of the internal calls fail. -// It does not guarantee that the program was run from a terminal. It only can tell you -// whether it was launched from explorer.exe -func StartedByExplorer() bool { - pe, err := getProcessEntry(os.Getppid()) - if err != nil { - return false - } - return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) -} diff --git a/vendor/github.com/mailru/easyjson/.gitignore b/vendor/github.com/mailru/easyjson/.gitignore deleted file mode 100644 index fbfaf7a3f..000000000 --- a/vendor/github.com/mailru/easyjson/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.root -*_easyjson.go -*.iml -.idea -*.swp -bin/* diff --git a/vendor/github.com/mailru/easyjson/.travis.yml b/vendor/github.com/mailru/easyjson/.travis.yml deleted file mode 100644 index 1e0fa4c6a..000000000 --- a/vendor/github.com/mailru/easyjson/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -arch: - - amd64 - - ppc64le -language: go - -go: - - tip - - stable - -matrix: - allow_failures: - - go: tip - -install: - - go get golang.org/x/lint/golint diff --git a/vendor/github.com/mailru/easyjson/Makefile b/vendor/github.com/mailru/easyjson/Makefile deleted file mode 100644 index c5273407b..000000000 --- a/vendor/github.com/mailru/easyjson/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -all: test - -clean: - rm -rf bin - rm -rf tests/*_easyjson.go - rm -rf benchmark/*_easyjson.go - -build: - go build -i -o ./bin/easyjson ./easyjson - -generate: build - bin/easyjson -stubs \ - ./tests/snake.go \ - ./tests/data.go \ - ./tests/omitempty.go \ - ./tests/nothing.go \ - ./tests/named_type.go \ - ./tests/custom_map_key_type.go \ - ./tests/embedded_type.go \ - ./tests/reference_to_pointer.go \ - ./tests/html.go \ - ./tests/unknown_fields.go \ - ./tests/type_declaration.go \ - ./tests/type_declaration_skip.go \ - ./tests/members_escaped.go \ - ./tests/members_unescaped.go \ - ./tests/intern.go \ - ./tests/nocopy.go \ - ./tests/escaping.go - bin/easyjson -all \ - ./tests/data.go \ - ./tests/nothing.go \ - ./tests/errors.go \ - ./tests/html.go \ - ./tests/type_declaration_skip.go - bin/easyjson \ - ./tests/nested_easy.go \ - ./tests/named_type.go \ - ./tests/custom_map_key_type.go \ - ./tests/embedded_type.go \ - ./tests/reference_to_pointer.go \ - ./tests/key_marshaler_map.go \ - ./tests/unknown_fields.go \ - ./tests/type_declaration.go \ - ./tests/members_escaped.go \ - ./tests/intern.go \ - ./tests/nocopy.go \ - ./tests/escaping.go \ - ./tests/nested_marshaler.go - bin/easyjson -snake_case ./tests/snake.go - bin/easyjson -omit_empty ./tests/omitempty.go - bin/easyjson -build_tags=use_easyjson -disable_members_unescape ./benchmark/data.go - bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go - bin/easyjson -disable_members_unescape ./tests/members_unescaped.go - -test: generate - go test \ - ./tests \ - ./jlexer \ - ./gen \ - ./buffer - cd benchmark && go test -benchmem -tags use_easyjson -bench . - golint -set_exit_status ./tests/*_easyjson.go - -bench-other: generate - cd benchmark && make - -bench-python: - benchmark/ujson.sh - - -.PHONY: clean generate test build diff --git a/vendor/github.com/mailru/easyjson/README.md b/vendor/github.com/mailru/easyjson/README.md deleted file mode 100644 index 952575b9d..000000000 --- a/vendor/github.com/mailru/easyjson/README.md +++ /dev/null @@ -1,387 +0,0 @@ -# easyjson [![Build Status](https://travis-ci.org/mailru/easyjson.svg?branch=master)](https://travis-ci.org/mailru/easyjson) [![Go Report Card](https://goreportcard.com/badge/github.com/mailru/easyjson)](https://goreportcard.com/report/github.com/mailru/easyjson) - -Package easyjson provides a fast and easy way to marshal/unmarshal Go structs -to/from JSON without the use of reflection. In performance tests, easyjson -outperforms the standard `encoding/json` package by a factor of 4-5x, and other -JSON encoding packages by a factor of 2-3x. - -easyjson aims to keep generated Go code simple enough so that it can be easily -optimized or fixed. Another goal is to provide users with the ability to -customize the generated code by providing options not available with the -standard `encoding/json` package, such as generating "snake_case" names or -enabling `omitempty` behavior by default. - -## Usage -```sh -# install -go get -u github.com/mailru/easyjson/... - -# run -easyjson -all .go -``` - -The above will generate `_easyjson.go` containing the appropriate marshaler and -unmarshaler funcs for all structs contained in `.go`. - -Please note that easyjson requires a full Go build environment and the `GOPATH` -environment variable to be set. This is because easyjson code generation -invokes `go run` on a temporary file (an approach to code generation borrowed -from [ffjson](https://github.com/pquerna/ffjson)). - -## Options -```txt -Usage of easyjson: - -all - generate marshaler/unmarshalers for all structs in a file - -build_tags string - build tags to add to generated file - -gen_build_flags string - build flags when running the generator while bootstrapping - -byte - use simple bytes instead of Base64Bytes for slice of bytes - -leave_temps - do not delete temporary files - -no_std_marshalers - don't generate MarshalJSON/UnmarshalJSON funcs - -noformat - do not run 'gofmt -w' on output file - -omit_empty - omit empty fields by default - -output_filename string - specify the filename of the output - -pkg - process the whole package instead of just the given file - -snake_case - use snake_case names instead of CamelCase by default - -lower_camel_case - use lowerCamelCase instead of CamelCase by default - -stubs - only generate stubs for marshaler/unmarshaler funcs - -disallow_unknown_fields - return error if some unknown field in json appeared - -disable_members_unescape - disable unescaping of \uXXXX string sequences in member names -``` - -Using `-all` will generate marshalers/unmarshalers for all Go structs in the -file excluding those structs whose preceding comment starts with `easyjson:skip`. -For example: - -```go -//easyjson:skip -type A struct {} -``` - -If `-all` is not provided, then only those structs whose preceding -comment starts with `easyjson:json` will have marshalers/unmarshalers -generated. For example: - -```go -//easyjson:json -type A struct {} -``` - -Additional option notes: - -* `-snake_case` tells easyjson to generate snake\_case field names by default - (unless overridden by a field tag). The CamelCase to snake\_case conversion - algorithm should work in most cases (ie, HTTPVersion will be converted to - "http_version"). - -* `-build_tags` will add the specified build tags to generated Go sources. - -* `-gen_build_flags` will execute the easyjson bootstapping code to launch the - actual generator command with provided flags. Multiple arguments should be - separated by space e.g. `-gen_build_flags="-mod=mod -x"`. - -## Structure json tag options - -Besides standart json tag options like 'omitempty' the following are supported: - -* 'nocopy' - disables allocation and copying of string values, making them - refer to original json buffer memory. This works great for short lived - objects which are not hold in memory after decoding and immediate usage. - Note if string requires unescaping it will be processed as normally. -* 'intern' - string "interning" (deduplication) to save memory when the very - same string dictionary values are often met all over the structure. - See below for more details. - -## Generated Marshaler/Unmarshaler Funcs - -For Go struct types, easyjson generates the funcs `MarshalEasyJSON` / -`UnmarshalEasyJSON` for marshaling/unmarshaling JSON. In turn, these satisfy -the `easyjson.Marshaler` and `easyjson.Unmarshaler` interfaces and when used in -conjunction with `easyjson.Marshal` / `easyjson.Unmarshal` avoid unnecessary -reflection / type assertions during marshaling/unmarshaling to/from JSON for Go -structs. - -easyjson also generates `MarshalJSON` and `UnmarshalJSON` funcs for Go struct -types compatible with the standard `json.Marshaler` and `json.Unmarshaler` -interfaces. Please be aware that using the standard `json.Marshal` / -`json.Unmarshal` for marshaling/unmarshaling will incur a significant -performance penalty when compared to using `easyjson.Marshal` / -`easyjson.Unmarshal`. - -Additionally, easyjson exposes utility funcs that use the `MarshalEasyJSON` and -`UnmarshalEasyJSON` for marshaling/unmarshaling to and from standard readers -and writers. For example, easyjson provides `easyjson.MarshalToHTTPResponseWriter` -which marshals to the standard `http.ResponseWriter`. Please see the [GoDoc -listing](https://godoc.org/github.com/mailru/easyjson) for the full listing of -utility funcs that are available. - -## Controlling easyjson Marshaling and Unmarshaling Behavior - -Go types can provide their own `MarshalEasyJSON` and `UnmarshalEasyJSON` funcs -that satisfy the `easyjson.Marshaler` / `easyjson.Unmarshaler` interfaces. -These will be used by `easyjson.Marshal` and `easyjson.Unmarshal` when defined -for a Go type. - -Go types can also satisfy the `easyjson.Optional` interface, which allows the -type to define its own `omitempty` logic. - -## Type Wrappers - -easyjson provides additional type wrappers defined in the `easyjson/opt` -package. These wrap the standard Go primitives and in turn satisfy the -easyjson interfaces. - -The `easyjson/opt` type wrappers are useful when needing to distinguish between -a missing value and/or when needing to specifying a default value. Type -wrappers allow easyjson to avoid additional pointers and heap allocations and -can significantly increase performance when used properly. - -## Memory Pooling - -easyjson uses a buffer pool that allocates data in increasing chunks from 128 -to 32768 bytes. Chunks of 512 bytes and larger will be reused with the help of -`sync.Pool`. The maximum size of a chunk is bounded to reduce redundant memory -allocation and to allow larger reusable buffers. - -easyjson's custom allocation buffer pool is defined in the `easyjson/buffer` -package, and the default behavior pool behavior can be modified (if necessary) -through a call to `buffer.Init()` prior to any marshaling or unmarshaling. -Please see the [GoDoc listing](https://godoc.org/github.com/mailru/easyjson/buffer) -for more information. - -## String interning - -During unmarshaling, `string` field values can be optionally -[interned](https://en.wikipedia.org/wiki/String_interning) to reduce memory -allocations and usage by deduplicating strings in memory, at the expense of slightly -increased CPU usage. - -This will work effectively only for `string` fields being decoded that have frequently -the same value (e.g. if you have a string field that can only assume a small number -of possible values). - -To enable string interning, add the `intern` keyword tag to your `json` tag on `string` -fields, e.g.: - -```go -type Foo struct { - UUID string `json:"uuid"` // will not be interned during unmarshaling - State string `json:"state,intern"` // will be interned during unmarshaling -} -``` - -## Issues, Notes, and Limitations - -* easyjson is still early in its development. As such, there are likely to be - bugs and missing features when compared to `encoding/json`. In the case of a - missing feature or bug, please create a GitHub issue. Pull requests are - welcome! - -* Unlike `encoding/json`, object keys are case-sensitive. Case-insensitive - matching is not currently provided due to the significant performance hit - when doing case-insensitive key matching. In the future, case-insensitive - object key matching may be provided via an option to the generator. - -* easyjson makes use of `unsafe`, which simplifies the code and - provides significant performance benefits by allowing no-copy - conversion from `[]byte` to `string`. That said, `unsafe` is used - only when unmarshaling and parsing JSON, and any `unsafe` operations - / memory allocations done will be safely deallocated by - easyjson. Set the build tag `easyjson_nounsafe` to compile it - without `unsafe`. - -* easyjson is compatible with Google App Engine. The `appengine` build - tag (set by App Engine's environment) will automatically disable the - use of `unsafe`, which is not allowed in App Engine's Standard - Environment. Note that the use with App Engine is still experimental. - -* Floats are formatted using the default precision from Go's `strconv` package. - As such, easyjson will not correctly handle high precision floats when - marshaling/unmarshaling JSON. Note, however, that there are very few/limited - uses where this behavior is not sufficient for general use. That said, a - different package may be needed if precise marshaling/unmarshaling of high - precision floats to/from JSON is required. - -* While unmarshaling, the JSON parser does the minimal amount of work needed to - skip over unmatching parens, and as such full validation is not done for the - entire JSON value being unmarshaled/parsed. - -* Currently there is no true streaming support for encoding/decoding as - typically for many uses/protocols the final, marshaled length of the JSON - needs to be known prior to sending the data. Currently this is not possible - with easyjson's architecture. - -* easyjson parser and codegen based on reflection, so it won't work on `package main` - files, because they cant be imported by parser. - -## Benchmarks - -Most benchmarks were done using the example -[13kB example JSON](https://dev.twitter.com/rest/reference/get/search/tweets) -(9k after eliminating whitespace). This example is similar to real-world data, -is well-structured, and contains a healthy variety of different types, making -it ideal for JSON serialization benchmarks. - -Note: - -* For small request benchmarks, an 80 byte portion of the above example was - used. - -* For large request marshaling benchmarks, a struct containing 50 regular - samples was used, making a ~500kB output JSON. - -* Benchmarks are showing the results of easyjson's default behaviour, - which makes use of `unsafe`. - -Benchmarks are available in the repository and can be run by invoking `make`. - -### easyjson vs. encoding/json - -easyjson is roughly 5-6 times faster than the standard `encoding/json` for -unmarshaling, and 3-4 times faster for non-concurrent marshaling. Concurrent -marshaling is 6-7x faster if marshaling to a writer. - -### easyjson vs. ffjson - -easyjson uses the same approach for JSON marshaling as -[ffjson](https://github.com/pquerna/ffjson), but takes a significantly -different approach to lexing and parsing JSON during unmarshaling. This means -easyjson is roughly 2-3x faster for unmarshaling and 1.5-2x faster for -non-concurrent unmarshaling. - -As of this writing, `ffjson` seems to have issues when used concurrently: -specifically, large request pooling hurts `ffjson`'s performance and causes -scalability issues. These issues with `ffjson` can likely be fixed, but as of -writing remain outstanding/known issues with `ffjson`. - -easyjson and `ffjson` have similar performance for small requests, however -easyjson outperforms `ffjson` by roughly 2-5x times for large requests when -used with a writer. - -### easyjson vs. go/codec - -[go/codec](https://github.com/ugorji/go) provides -compile-time helpers for JSON generation. In this case, helpers do not work -like marshalers as they are encoding-independent. - -easyjson is generally 2x faster than `go/codec` for non-concurrent benchmarks -and about 3x faster for concurrent encoding (without marshaling to a writer). - -In an attempt to measure marshaling performance of `go/codec` (as opposed to -allocations/memcpy/writer interface invocations), a benchmark was done with -resetting length of a byte slice rather than resetting the whole slice to nil. -However, the optimization in this exact form may not be applicable in practice, -since the memory is not freed between marshaling operations. - -### easyjson vs 'ujson' python module - -[ujson](https://github.com/esnme/ultrajson) is using C code for parsing, so it -is interesting to see how plain golang compares to that. It is important to note -that the resulting object for python is slower to access, since the library -parses JSON object into dictionaries. - -easyjson is slightly faster for unmarshaling and 2-3x faster than `ujson` for -marshaling. - -### Benchmark Results - -`ffjson` results are from February 4th, 2016, using the latest `ffjson` and go1.6. -`go/codec` results are from March 4th, 2016, using the latest `go/codec` and go1.6. - -#### Unmarshaling - -| lib | json size | MB/s | allocs/op | B/op | -|:---------|:----------|-----:|----------:|------:| -| standard | regular | 22 | 218 | 10229 | -| standard | small | 9.7 | 14 | 720 | -| | | | | | -| easyjson | regular | 125 | 128 | 9794 | -| easyjson | small | 67 | 3 | 128 | -| | | | | | -| ffjson | regular | 66 | 141 | 9985 | -| ffjson | small | 17.6 | 10 | 488 | -| | | | | | -| codec | regular | 55 | 434 | 19299 | -| codec | small | 29 | 7 | 336 | -| | | | | | -| ujson | regular | 103 | N/A | N/A | - -#### Marshaling, one goroutine. - -| lib | json size | MB/s | allocs/op | B/op | -|:----------|:----------|-----:|----------:|------:| -| standard | regular | 75 | 9 | 23256 | -| standard | small | 32 | 3 | 328 | -| standard | large | 80 | 17 | 1.2M | -| | | | | | -| easyjson | regular | 213 | 9 | 10260 | -| easyjson* | regular | 263 | 8 | 742 | -| easyjson | small | 125 | 1 | 128 | -| easyjson | large | 212 | 33 | 490k | -| easyjson* | large | 262 | 25 | 2879 | -| | | | | | -| ffjson | regular | 122 | 153 | 21340 | -| ffjson** | regular | 146 | 152 | 4897 | -| ffjson | small | 36 | 5 | 384 | -| ffjson** | small | 64 | 4 | 128 | -| ffjson | large | 134 | 7317 | 818k | -| ffjson** | large | 125 | 7320 | 827k | -| | | | | | -| codec | regular | 80 | 17 | 33601 | -| codec*** | regular | 108 | 9 | 1153 | -| codec | small | 42 | 3 | 304 | -| codec*** | small | 56 | 1 | 48 | -| codec | large | 73 | 483 | 2.5M | -| codec*** | large | 103 | 451 | 66007 | -| | | | | | -| ujson | regular | 92 | N/A | N/A | - -\* marshaling to a writer, -\*\* using `ffjson.Pool()`, -\*\*\* reusing output slice instead of resetting it to nil - -#### Marshaling, concurrent. - -| lib | json size | MB/s | allocs/op | B/op | -|:----------|:----------|-----:|----------:|------:| -| standard | regular | 252 | 9 | 23257 | -| standard | small | 124 | 3 | 328 | -| standard | large | 289 | 17 | 1.2M | -| | | | | | -| easyjson | regular | 792 | 9 | 10597 | -| easyjson* | regular | 1748 | 8 | 779 | -| easyjson | small | 333 | 1 | 128 | -| easyjson | large | 718 | 36 | 548k | -| easyjson* | large | 2134 | 25 | 4957 | -| | | | | | -| ffjson | regular | 301 | 153 | 21629 | -| ffjson** | regular | 707 | 152 | 5148 | -| ffjson | small | 62 | 5 | 384 | -| ffjson** | small | 282 | 4 | 128 | -| ffjson | large | 438 | 7330 | 1.0M | -| ffjson** | large | 131 | 7319 | 820k | -| | | | | | -| codec | regular | 183 | 17 | 33603 | -| codec*** | regular | 671 | 9 | 1157 | -| codec | small | 147 | 3 | 304 | -| codec*** | small | 299 | 1 | 48 | -| codec | large | 190 | 483 | 2.5M | -| codec*** | large | 752 | 451 | 77574 | - -\* marshaling to a writer, -\*\* using `ffjson.Pool()`, -\*\*\* reusing output slice instead of resetting it to nil diff --git a/vendor/github.com/mailru/easyjson/helpers.go b/vendor/github.com/mailru/easyjson/helpers.go deleted file mode 100644 index 78dacb1b7..000000000 --- a/vendor/github.com/mailru/easyjson/helpers.go +++ /dev/null @@ -1,114 +0,0 @@ -// Package easyjson contains marshaler/unmarshaler interfaces and helper functions. -package easyjson - -import ( - "io" - "io/ioutil" - "net/http" - "strconv" - "unsafe" - - "github.com/mailru/easyjson/jlexer" - "github.com/mailru/easyjson/jwriter" -) - -// Marshaler is an easyjson-compatible marshaler interface. -type Marshaler interface { - MarshalEasyJSON(w *jwriter.Writer) -} - -// Marshaler is an easyjson-compatible unmarshaler interface. -type Unmarshaler interface { - UnmarshalEasyJSON(w *jlexer.Lexer) -} - -// MarshalerUnmarshaler is an easyjson-compatible marshaler/unmarshaler interface. -type MarshalerUnmarshaler interface { - Marshaler - Unmarshaler -} - -// Optional defines an undefined-test method for a type to integrate with 'omitempty' logic. -type Optional interface { - IsDefined() bool -} - -// UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want -type UnknownsUnmarshaler interface { - UnmarshalUnknown(in *jlexer.Lexer, key string) -} - -// UnknownsMarshaler provides a method to write additional struct fields -type UnknownsMarshaler interface { - MarshalUnknowns(w *jwriter.Writer, first bool) -} - -func isNilInterface(i interface{}) bool { - return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0 -} - -// Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied -// from a chain of smaller chunks. -func Marshal(v Marshaler) ([]byte, error) { - if isNilInterface(v) { - return nullBytes, nil - } - - w := jwriter.Writer{} - v.MarshalEasyJSON(&w) - return w.BuildBytes() -} - -// MarshalToWriter marshals the data to an io.Writer. -func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) { - if isNilInterface(v) { - return w.Write(nullBytes) - } - - jw := jwriter.Writer{} - v.MarshalEasyJSON(&jw) - return jw.DumpTo(w) -} - -// MarshalToHTTPResponseWriter sets Content-Length and Content-Type headers for the -// http.ResponseWriter, and send the data to the writer. started will be equal to -// false if an error occurred before any http.ResponseWriter methods were actually -// invoked (in this case a 500 reply is possible). -func MarshalToHTTPResponseWriter(v Marshaler, w http.ResponseWriter) (started bool, written int, err error) { - if isNilInterface(v) { - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Content-Length", strconv.Itoa(len(nullBytes))) - written, err = w.Write(nullBytes) - return true, written, err - } - - jw := jwriter.Writer{} - v.MarshalEasyJSON(&jw) - if jw.Error != nil { - return false, 0, jw.Error - } - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Content-Length", strconv.Itoa(jw.Size())) - - started = true - written, err = jw.DumpTo(w) - return -} - -// Unmarshal decodes the JSON in data into the object. -func Unmarshal(data []byte, v Unmarshaler) error { - l := jlexer.Lexer{Data: data} - v.UnmarshalEasyJSON(&l) - return l.Error() -} - -// UnmarshalFromReader reads all the data in the reader and decodes as JSON into the object. -func UnmarshalFromReader(r io.Reader, v Unmarshaler) error { - data, err := ioutil.ReadAll(r) - if err != nil { - return err - } - l := jlexer.Lexer{Data: data} - v.UnmarshalEasyJSON(&l) - return l.Error() -} diff --git a/vendor/github.com/mailru/easyjson/raw.go b/vendor/github.com/mailru/easyjson/raw.go deleted file mode 100644 index 81bd002e1..000000000 --- a/vendor/github.com/mailru/easyjson/raw.go +++ /dev/null @@ -1,45 +0,0 @@ -package easyjson - -import ( - "github.com/mailru/easyjson/jlexer" - "github.com/mailru/easyjson/jwriter" -) - -// RawMessage is a raw piece of JSON (number, string, bool, object, array or -// null) that is extracted without parsing and output as is during marshaling. -type RawMessage []byte - -// MarshalEasyJSON does JSON marshaling using easyjson interface. -func (v *RawMessage) MarshalEasyJSON(w *jwriter.Writer) { - if len(*v) == 0 { - w.RawString("null") - } else { - w.Raw(*v, nil) - } -} - -// UnmarshalEasyJSON does JSON unmarshaling using easyjson interface. -func (v *RawMessage) UnmarshalEasyJSON(l *jlexer.Lexer) { - *v = RawMessage(l.Raw()) -} - -// UnmarshalJSON implements encoding/json.Unmarshaler interface. -func (v *RawMessage) UnmarshalJSON(data []byte) error { - *v = data - return nil -} - -var nullBytes = []byte("null") - -// MarshalJSON implements encoding/json.Marshaler interface. -func (v RawMessage) MarshalJSON() ([]byte, error) { - if len(v) == 0 { - return nullBytes, nil - } - return v, nil -} - -// IsDefined is required for integration with omitempty easyjson logic. -func (v *RawMessage) IsDefined() bool { - return len(*v) > 0 -} diff --git a/vendor/github.com/mailru/easyjson/unknown_fields.go b/vendor/github.com/mailru/easyjson/unknown_fields.go deleted file mode 100644 index 55538eac9..000000000 --- a/vendor/github.com/mailru/easyjson/unknown_fields.go +++ /dev/null @@ -1,32 +0,0 @@ -package easyjson - -import ( - jlexer "github.com/mailru/easyjson/jlexer" - "github.com/mailru/easyjson/jwriter" -) - -// UnknownFieldsProxy implemets UnknownsUnmarshaler and UnknownsMarshaler -// use it as embedded field in your structure to parse and then serialize unknown struct fields -type UnknownFieldsProxy struct { - unknownFields map[string][]byte -} - -func (s *UnknownFieldsProxy) UnmarshalUnknown(in *jlexer.Lexer, key string) { - if s.unknownFields == nil { - s.unknownFields = make(map[string][]byte, 1) - } - s.unknownFields[key] = in.Raw() -} - -func (s UnknownFieldsProxy) MarshalUnknowns(out *jwriter.Writer, first bool) { - for key, val := range s.unknownFields { - if first { - first = false - } else { - out.RawByte(',') - } - out.String(string(key)) - out.RawByte(':') - out.Raw(val, nil) - } -} diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE index 55f12ab77..852ab9ced 100644 --- a/vendor/github.com/miekg/dns/LICENSE +++ b/vendor/github.com/miekg/dns/LICENSE @@ -1,30 +1,29 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +BSD 3-Clause License + +Copyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. +All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -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. +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. -As this is fork of the official Go code the same license applies. -Extensions of the original work are copyright (c) 2011 Miek Gieben +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 5a799d88f..e57d86afe 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -77,6 +77,12 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://ping.sx/dig * https://fleetdeck.io/ * https://github.com/markdingo/autoreverse +* https://github.com/slackhq/nebula +* https://addr.tools/ +* https://dnscheck.tools/ +* https://github.com/egbakou/domainverifier +* https://github.com/semihalev/sdns +* https://github.com/wintbiit/NineDNS Send pull request if you want to be listed here. @@ -120,6 +126,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. *all of them* * 103{4,5} - DNS standard +* 1183 - ISDN, X25 and other deprecated records * 1348 - NSAP record (removed the record) * 1982 - Serial Arithmetic * 1876 - LOC record @@ -140,6 +147,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY * 3597 - Unknown RRs +* 4025 - A Method for Storing IPsec Keying Material in DNS * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP record * 4343 - Case insensitivity @@ -175,6 +183,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 8080 - EdDSA for DNSSEC * 8499 - DNS Terminology * 8659 - DNS Certification Authority Authorization (CAA) Resource Record +* 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery * 8914 - Extended DNS Errors * 8976 - Message Digest for DNS Zones (ZONEMD RR) diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go index ac479db95..1a59a854e 100644 --- a/vendor/github.com/miekg/dns/acceptfunc.go +++ b/vendor/github.com/miekg/dns/acceptfunc.go @@ -10,8 +10,6 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction // // * opcode isn't OpcodeQuery or OpcodeNotify // -// * Zero bit isn't zero -// // * does not have exactly 1 question in the question section // // * has more than 1 RR in the Answer section @@ -19,7 +17,6 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction // * has more than 0 RRs in the Authority section // // * has more than 2 RRs in the Additional section -// var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc // MsgAcceptAction represents the action to be taken. diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 9aa658530..9549fa923 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -6,7 +6,6 @@ import ( "context" "crypto/tls" "encoding/binary" - "fmt" "io" "net" "strings" @@ -56,14 +55,20 @@ type Client struct { // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout, // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and // Client.Dialer) or context.Context.Deadline (see ExchangeContext) - Timeout time.Duration - DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. - SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass - group singleflight + Timeout time.Duration + DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) + TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. + + // SingleInflight previously serialised multiple concurrent queries for the + // same Qname, Qtype and Qclass to ensure only one would be in flight at a + // time. + // + // Deprecated: This is a no-op. Callers should implement their own in flight + // query caching if needed. See github.com/miekg/dns/issues/1449. + SingleInflight bool } // Exchange performs a synchronous UDP query. It sends the message m to the address @@ -106,7 +111,6 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { } // DialContext connects to the address on the named network, with a context.Context. -// For TLS over TCP (DoT) the context isn't used yet. This will be enabled when Go 1.18 is released. func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, err error) { // create a new dialer with the appropriate timeout var d net.Dialer @@ -127,15 +131,11 @@ func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, e if useTLS { network = strings.TrimSuffix(network, "-tls") - // TODO(miekg): Enable after Go 1.18 is released, to be able to support two prev. releases. - /* - tlsDialer := tls.Dialer{ - NetDialer: &d, - Config: c.TLSConfig, - } - conn.Conn, err = tlsDialer.DialContext(ctx, network, address) - */ - conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig) + tlsDialer := tls.Dialer{ + NetDialer: &d, + Config: c.TLSConfig, + } + conn.Conn, err = tlsDialer.DialContext(ctx, network, address) } else { conn.Conn, err = d.DialContext(ctx, network, address) } @@ -183,33 +183,13 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er // This allows users of the library to implement their own connection management, // as opposed to Exchange, which will always use new connections and incur the added overhead // that entails when using "tcp" and especially "tcp-tls" clients. -// -// When the singleflight is set for this client the context is _not_ forwarded to the (shared) exchange, to -// prevent one cancelation from canceling all outstanding requests. func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { - return c.exchangeWithConnContext(context.Background(), m, conn) -} - -func (c *Client) exchangeWithConnContext(ctx context.Context, m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { - if !c.SingleInflight { - return c.exchangeContext(ctx, m, conn) - } - - q := m.Question[0] - key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass) - r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) { - // When we're doing singleflight we don't want one context cancelation, cancel _all_ outstanding queries. - // Hence we ignore the context and use Background(). - return c.exchangeContext(context.Background(), m, conn) - }) - if r != nil && shared { - r = r.Copy() - } - - return r, rtt, err + return c.ExchangeWithConnContext(context.Background(), m, conn) } -func (c *Client) exchangeContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { +// ExchangeWithConnContext has the same behaviour as ExchangeWithConn and +// additionally obeys deadlines from the passed Context. +func (c *Client) ExchangeWithConnContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { opt := m.IsEdns0() // If EDNS0 is used use that for size. if opt != nil && opt.UDPSize() >= MinMsgSize { @@ -431,7 +411,6 @@ func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) // co.WriteMsg(m) // in, _ := co.ReadMsg() // co.Close() -// func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { println("dns: ExchangeConn: this function is deprecated") co := new(Conn) @@ -480,5 +459,5 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, } defer conn.Close() - return c.exchangeWithConnContext(ctx, m, conn) + return c.ExchangeWithConnContext(ctx, m, conn) } diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go index e11b630df..d00ac62fb 100644 --- a/vendor/github.com/miekg/dns/clientconfig.go +++ b/vendor/github.com/miekg/dns/clientconfig.go @@ -68,7 +68,7 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) { } case "search": // set search path to given servers - c.Search = append([]string(nil), f[1:]...) + c.Search = cloneSlice(f[1:]) case "options": // magic options for _, s := range f[1:] { diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index f2cdbf430..02d9199a4 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -22,8 +22,7 @@ func (dns *Msg) SetReply(request *Msg) *Msg { } dns.Rcode = RcodeSuccess if len(request.Question) > 0 { - dns.Question = make([]Question, 1) - dns.Question[0] = request.Question[0] + dns.Question = []Question{request.Question[0]} } return dns } @@ -208,7 +207,7 @@ func IsDomainName(s string) (labels int, ok bool) { } // check for \DDD - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + if isDDD(s[i+1:]) { i += 3 begin += 3 } else { @@ -272,40 +271,39 @@ func IsMsg(buf []byte) error { // IsFqdn checks if a domain name is fully qualified. func IsFqdn(s string) bool { - s2 := strings.TrimSuffix(s, ".") - if s == s2 { + // Check for (and remove) a trailing dot, returning if there isn't one. + if s == "" || s[len(s)-1] != '.' { return false } + s = s[:len(s)-1] - i := strings.LastIndexFunc(s2, func(r rune) bool { + // If we don't have an escape sequence before the final dot, we know it's + // fully qualified and can return here. + if s == "" || s[len(s)-1] != '\\' { + return true + } + + // Otherwise we have to check if the dot is escaped or not by checking if + // there are an odd or even number of escape sequences before the dot. + i := strings.LastIndexFunc(s, func(r rune) bool { return r != '\\' }) - - // Test whether we have an even number of escape sequences before - // the dot or none. - return (len(s2)-i)%2 != 0 + return (len(s)-i)%2 != 0 } -// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181. -// This means the RRs need to have the same type, name, and class. Returns true -// if the RR set is valid, otherwise false. +// IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181. +// This means the RRs need to have the same type, name, and class. func IsRRset(rrset []RR) bool { if len(rrset) == 0 { return false } - if len(rrset) == 1 { - return true - } - rrHeader := rrset[0].Header() - rrType := rrHeader.Rrtype - rrClass := rrHeader.Class - rrName := rrHeader.Name + baseH := rrset[0].Header() for _, rr := range rrset[1:] { - curRRHeader := rr.Header() - if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName { + curH := rr.Header() + if curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name { // Mismatch between the records, so this is not a valid rrset for - //signing/verifying + // signing/verifying return false } } @@ -323,9 +321,15 @@ func Fqdn(s string) string { } // CanonicalName returns the domain name in canonical form. A name in canonical -// form is lowercase and fully qualified. See Section 6.2 in RFC 4034. +// form is lowercase and fully qualified. Only US-ASCII letters are affected. See +// Section 6.2 in RFC 4034. func CanonicalName(s string) string { - return strings.ToLower(Fqdn(s)) + return strings.Map(func(r rune) rune { + if r >= 'A' && r <= 'Z' { + r += 'a' - 'A' + } + return r + }, Fqdn(s)) } // Copied from the official Go code. diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index ea01aa81f..1be87eae6 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -128,10 +128,6 @@ type dnskeyWireFmt struct { /* Nothing is left out */ } -func divRoundUp(a, b int) int { - return (a + b - 1) / b -} - // KeyTag calculates the keytag (or key-id) of the DNSKEY. func (k *DNSKEY) KeyTag() uint16 { if k == nil { @@ -417,11 +413,11 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { return err } - sigbuf := rr.sigBuf() // Get the binary signature data - if rr.Algorithm == PRIVATEDNS { // PRIVATEOID - // TODO(miek) - // remove the domain name and assume its ours? - } + sigbuf := rr.sigBuf() // Get the binary signature data + // TODO(miek) + // remove the domain name and assume its ours? + // if rr.Algorithm == PRIVATEDNS { // PRIVATEOID + // } h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go index f79658169..9c9972db6 100644 --- a/vendor/github.com/miekg/dns/dnssec_keyscan.go +++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go @@ -37,7 +37,8 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er return nil, ErrPrivKey } // TODO(mg): check if the pubkey matches the private key - algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8) + algoStr, _, _ := strings.Cut(m["algorithm"], " ") + algo, err := strconv.ParseUint(algoStr, 10, 8) if err != nil { return nil, ErrPrivKey } @@ -159,7 +160,7 @@ func parseKey(r io.Reader, file string) (map[string]string, error) { k = l.token case zValue: if k == "" { - return nil, &ParseError{file, "no private key seen", l} + return nil, &ParseError{file: file, err: "no private key seen", lex: l} } m[strings.ToLower(k)] = l.token diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index f00f5722c..586ab6917 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -13,28 +13,28 @@ names in a message will result in a packing failure. Resource records are native types. They are not stored in wire format. Basic usage pattern for creating a new resource record: - r := new(dns.MX) - r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} - r.Preference = 10 - r.Mx = "mx.miek.nl." + r := new(dns.MX) + r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} + r.Preference = 10 + r.Mx = "mx.miek.nl." Or directly from a string: - mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") + mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") Or when the default origin (.) and TTL (3600) and class (IN) suit you: - mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") + mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") Or even: - mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") + mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") In the DNS messages are exchanged, these messages contain resource records (sets). Use pattern for creating a message: - m := new(dns.Msg) - m.SetQuestion("miek.nl.", dns.TypeMX) + m := new(dns.Msg) + m.SetQuestion("miek.nl.", dns.TypeMX) Or when not certain if the domain name is fully qualified: @@ -45,17 +45,17 @@ records for the miek.nl. zone. The following is slightly more verbose, but more flexible: - m1 := new(dns.Msg) - m1.Id = dns.Id() - m1.RecursionDesired = true - m1.Question = make([]dns.Question, 1) - m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.RecursionDesired = true + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} After creating a message it can be sent. Basic use pattern for synchronous querying the DNS at a server configured on 127.0.0.1 and port 53: - c := new(dns.Client) - in, rtt, err := c.Exchange(m1, "127.0.0.1:53") + c := new(dns.Client) + in, rtt, err := c.Exchange(m1, "127.0.0.1:53") Suppressing multiple outstanding queries (with the same question, type and class) is as easy as setting: @@ -72,7 +72,7 @@ and port to use for the connection: Port: 12345, Zone: "", } - c.Dialer := &net.Dialer{ + c.Dialer = &net.Dialer{ Timeout: 200 * time.Millisecond, LocalAddr: &laddr, } @@ -96,7 +96,7 @@ the Answer section: // do something with t.Txt } -Domain Name and TXT Character String Representations +# Domain Name and TXT Character String Representations Both domain names and TXT character strings are converted to presentation form both when unpacked and when converted to strings. @@ -108,7 +108,7 @@ be escaped. Bytes below 32 and above 127 will be converted to \DDD form. For domain names, in addition to the above rules brackets, periods, spaces, semicolons and the at symbol are escaped. -DNSSEC +# DNSSEC DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses public key cryptography to sign resource records. The public keys are stored in @@ -117,12 +117,12 @@ DNSKEY records and the signatures in RRSIG records. Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit to a request. - m := new(dns.Msg) - m.SetEdns0(4096, true) + m := new(dns.Msg) + m.SetEdns0(4096, true) Signature generation, signature verification and key generation are all supported. -DYNAMIC UPDATES +# DYNAMIC UPDATES Dynamic updates reuses the DNS message format, but renames three of the sections. Question is Zone, Answer is Prerequisite, Authority is Update, only @@ -133,30 +133,30 @@ certain resource records or names in a zone to specify if resource records should be added or removed. The table from RFC 2136 supplemented with the Go DNS function shows which functions exist to specify the prerequisites. - 3.2.4 - Table Of Metavalues Used In Prerequisite Section + 3.2.4 - Table Of Metavalues Used In Prerequisite Section - CLASS TYPE RDATA Meaning Function - -------------------------------------------------------------- - ANY ANY empty Name is in use dns.NameUsed - ANY rrset empty RRset exists (value indep) dns.RRsetUsed - NONE ANY empty Name is not in use dns.NameNotUsed - NONE rrset empty RRset does not exist dns.RRsetNotUsed - zone rrset rr RRset exists (value dep) dns.Used + CLASS TYPE RDATA Meaning Function + -------------------------------------------------------------- + ANY ANY empty Name is in use dns.NameUsed + ANY rrset empty RRset exists (value indep) dns.RRsetUsed + NONE ANY empty Name is not in use dns.NameNotUsed + NONE rrset empty RRset does not exist dns.RRsetNotUsed + zone rrset rr RRset exists (value dep) dns.Used The prerequisite section can also be left empty. If you have decided on the prerequisites you can tell what RRs should be added or deleted. The next table shows the options you have and what functions to call. - 3.4.2.6 - Table Of Metavalues Used In Update Section + 3.4.2.6 - Table Of Metavalues Used In Update Section - CLASS TYPE RDATA Meaning Function - --------------------------------------------------------------- - ANY ANY empty Delete all RRsets from name dns.RemoveName - ANY rrset empty Delete an RRset dns.RemoveRRset - NONE rrset rr Delete an RR from RRset dns.Remove - zone rrset rr Add to an RRset dns.Insert + CLASS TYPE RDATA Meaning Function + --------------------------------------------------------------- + ANY ANY empty Delete all RRsets from name dns.RemoveName + ANY rrset empty Delete an RRset dns.RemoveRRset + NONE rrset rr Delete an RR from RRset dns.Remove + zone rrset rr Add to an RRset dns.Insert -TRANSACTION SIGNATURE +# TRANSACTION SIGNATURE An TSIG or transaction signature adds a HMAC TSIG record to each message sent. The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512. @@ -239,7 +239,7 @@ Basic use pattern validating and replying to a message that has TSIG set. w.WriteMsg(m) } -PRIVATE RRS +# PRIVATE RRS RFC 6895 sets aside a range of type codes for private use. This range is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these @@ -248,7 +248,7 @@ can be used, before requesting an official type code from IANA. See https://miek.nl/2014/september/21/idn-and-private-rr-in-go-dns/ for more information. -EDNS0 +# EDNS0 EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by RFC 6891. It defines a new RR type, the OPT RR, which is then completely @@ -279,9 +279,9 @@ SIG(0) From RFC 2931: - SIG(0) provides protection for DNS transactions and requests .... - ... protection for glue records, DNS requests, protection for message headers - on requests and responses, and protection of the overall integrity of a response. + SIG(0) provides protection for DNS transactions and requests .... + ... protection for glue records, DNS requests, protection for message headers + on requests and responses, and protection of the overall integrity of a response. It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared secret approach in TSIG. Supported algorithms: ECDSAP256SHA256, diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 14568c2e9..1b58e8f0a 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -78,7 +78,10 @@ func (rr *OPT) String() string { if rr.Do() { s += "flags: do; " } else { - s += "flags: ; " + s += "flags:; " + } + if rr.Hdr.Ttl&0x7FFF != 0 { + s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF) } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) @@ -98,6 +101,8 @@ func (rr *OPT) String() string { s += "\n; SUBNET: " + o.String() case *EDNS0_COOKIE: s += "\n; COOKIE: " + o.String() + case *EDNS0_EXPIRE: + s += "\n; EXPIRE: " + o.String() case *EDNS0_TCP_KEEPALIVE: s += "\n; KEEPALIVE: " + o.String() case *EDNS0_UL: @@ -180,7 +185,7 @@ func (rr *OPT) Do() bool { // SetDo sets the DO (DNSSEC OK) bit. // If we pass an argument, set the DO bit to that value. -// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored. +// It is possible to pass 2 or more arguments, but they will be ignored. func (rr *OPT) SetDo(do ...bool) { if len(do) == 1 { if do[0] { @@ -258,7 +263,7 @@ func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_SUBNET) -// e.Code = dns.EDNS0SUBNET +// e.Code = dns.EDNS0SUBNET // by default this is filled in through unpacking OPT packets (unpackDataOpt) // e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 // e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6 // e.SourceScope = 0 @@ -503,6 +508,7 @@ func (e *EDNS0_LLQ) String() string { " " + strconv.FormatUint(uint64(e.LeaseLife), 10) return s } + func (e *EDNS0_LLQ) copy() EDNS0 { return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} } @@ -515,8 +521,8 @@ type EDNS0_DAU struct { // Option implements the EDNS0 interface. func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } -func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_DAU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DAU) String() string { s := "" @@ -539,8 +545,8 @@ type EDNS0_DHU struct { // Option implements the EDNS0 interface. func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } -func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_DHU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DHU) String() string { s := "" @@ -563,8 +569,8 @@ type EDNS0_N3U struct { // Option implements the EDNS0 interface. func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } -func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_N3U) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_N3U) String() string { // Re-use the hash map @@ -641,30 +647,21 @@ type EDNS0_LOCAL struct { // Option implements the EDNS0 interface. func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } + func (e *EDNS0_LOCAL) String() string { return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) } + func (e *EDNS0_LOCAL) copy() EDNS0 { - b := make([]byte, len(e.Data)) - copy(b, e.Data) - return &EDNS0_LOCAL{e.Code, b} + return &EDNS0_LOCAL{e.Code, cloneSlice(e.Data)} } func (e *EDNS0_LOCAL) pack() ([]byte, error) { - b := make([]byte, len(e.Data)) - copied := copy(b, e.Data) - if copied != len(e.Data) { - return nil, ErrBuf - } - return b, nil + return cloneSlice(e.Data), nil } func (e *EDNS0_LOCAL) unpack(b []byte) error { - e.Data = make([]byte, len(b)) - copied := copy(e.Data, b) - if copied != len(b) { - return ErrBuf - } + e.Data = cloneSlice(b) return nil } @@ -727,14 +724,10 @@ type EDNS0_PADDING struct { // Option implements the EDNS0 interface. func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } -func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } -func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil } +func (e *EDNS0_PADDING) pack() ([]byte, error) { return cloneSlice(e.Padding), nil } +func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = cloneSlice(b); return nil } func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } -func (e *EDNS0_PADDING) copy() EDNS0 { - b := make([]byte, len(e.Padding)) - copy(b, e.Padding) - return &EDNS0_PADDING{b} -} +func (e *EDNS0_PADDING) copy() EDNS0 { return &EDNS0_PADDING{cloneSlice(e.Padding)} } // Extended DNS Error Codes (RFC 8914). const ( @@ -821,7 +814,7 @@ func (e *EDNS0_EDE) String() string { func (e *EDNS0_EDE) pack() ([]byte, error) { b := make([]byte, 2+len(e.ExtraText)) binary.BigEndian.PutUint16(b[0:], e.InfoCode) - copy(b[2:], []byte(e.ExtraText)) + copy(b[2:], e.ExtraText) return b, nil } diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go index 57410acda..505ae4308 100644 --- a/vendor/github.com/miekg/dns/fuzz.go +++ b/vendor/github.com/miekg/dns/fuzz.go @@ -1,3 +1,4 @@ +//go:build fuzz // +build fuzz package dns diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go index ac8df34dd..a81d2bc51 100644 --- a/vendor/github.com/miekg/dns/generate.go +++ b/vendor/github.com/miekg/dns/generate.go @@ -35,17 +35,17 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { token = token[:i] } - sx := strings.SplitN(token, "-", 2) - if len(sx) != 2 { + startStr, endStr, ok := strings.Cut(token, "-") + if !ok { return zp.setParseError("bad start-stop in $GENERATE range", l) } - start, err := strconv.ParseInt(sx[0], 10, 64) + start, err := strconv.ParseInt(startStr, 10, 64) if err != nil { return zp.setParseError("bad start in $GENERATE range", l) } - end, err := strconv.ParseInt(sx[1], 10, 64) + end, err := strconv.ParseInt(endStr, 10, 64) if err != nil { return zp.setParseError("bad stop in $GENERATE range", l) } @@ -54,7 +54,7 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { } // _BLANK - l, ok := zp.c.Next() + l, ok = zp.c.Next() if !ok || l.value != zBlank { return zp.setParseError("garbage after $GENERATE range", l) } @@ -116,7 +116,7 @@ func (r *generateReader) parseError(msg string, end int) *ParseError { l.token = r.s[r.si-1 : end] l.column += r.si // l.column starts one zBLANK before r.s - return &ParseError{r.file, msg, l} + return &ParseError{file: r.file, err: msg, lex: l} } func (r *generateReader) Read(p []byte) (int, error) { @@ -211,15 +211,16 @@ func (r *generateReader) ReadByte() (byte, error) { func modToPrintf(s string) (string, int64, string) { // Modifier is { offset [ ,width [ ,base ] ] } - provide default // values for optional width and type, if necessary. - var offStr, widthStr, base string - switch xs := strings.Split(s, ","); len(xs) { - case 1: - offStr, widthStr, base = xs[0], "0", "d" - case 2: - offStr, widthStr, base = xs[0], xs[1], "d" - case 3: - offStr, widthStr, base = xs[0], xs[1], xs[2] - default: + offStr, s, ok0 := strings.Cut(s, ",") + widthStr, s, ok1 := strings.Cut(s, ",") + base, _, ok2 := strings.Cut(s, ",") + if !ok0 { + widthStr = "0" + } + if !ok1 { + base = "d" + } + if ok2 { return "", 0, "bad modifier in $GENERATE" } @@ -234,8 +235,8 @@ func modToPrintf(s string) (string, int64, string) { return "", 0, "bad offset in $GENERATE" } - width, err := strconv.ParseInt(widthStr, 10, 64) - if err != nil || width < 0 || width > 255 { + width, err := strconv.ParseUint(widthStr, 10, 8) + if err != nil { return "", 0, "bad width in $GENERATE" } diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index f9faacfeb..cd498d2e9 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -122,7 +122,7 @@ func Split(s string) []int { } // NextLabel returns the index of the start of the next label in the -// string s starting at offset. +// string s starting at offset. A negative offset will cause a panic. // The bool end is true when the end of the string has been reached. // Also see PrevLabel. func NextLabel(s string, offset int) (i int, end bool) { diff --git a/vendor/github.com/miekg/dns/listen_no_reuseport.go b/vendor/github.com/miekg/dns/listen_no_reuseport.go index b9201417a..8cebb2f17 100644 --- a/vendor/github.com/miekg/dns/listen_no_reuseport.go +++ b/vendor/github.com/miekg/dns/listen_no_reuseport.go @@ -1,4 +1,5 @@ -// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd package dns @@ -6,16 +7,18 @@ import "net" const supportsReusePort = false -func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { - if reuseport { +func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) { + if reuseport || reuseaddr { // TODO(tmthrgd): return an error? } return net.Listen(network, addr) } -func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { - if reuseport { +const supportsReuseAddr = false + +func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) { + if reuseport || reuseaddr { // TODO(tmthrgd): return an error? } diff --git a/vendor/github.com/miekg/dns/listen_reuseport.go b/vendor/github.com/miekg/dns/listen_reuseport.go index fad195cfe..41326f20b 100644 --- a/vendor/github.com/miekg/dns/listen_reuseport.go +++ b/vendor/github.com/miekg/dns/listen_reuseport.go @@ -1,4 +1,4 @@ -// +build go1.11 +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd // +build aix darwin dragonfly freebsd linux netbsd openbsd package dns @@ -25,19 +25,41 @@ func reuseportControl(network, address string, c syscall.RawConn) error { return opErr } -func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { +const supportsReuseAddr = true + +func reuseaddrControl(network, address string, c syscall.RawConn) error { + var opErr error + err := c.Control(func(fd uintptr) { + opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + }) + if err != nil { + return err + } + + return opErr +} + +func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) { var lc net.ListenConfig - if reuseport { + switch { + case reuseaddr && reuseport: + case reuseport: lc.Control = reuseportControl + case reuseaddr: + lc.Control = reuseaddrControl } return lc.Listen(context.Background(), network, addr) } -func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { +func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) { var lc net.ListenConfig - if reuseport { + switch { + case reuseaddr && reuseport: + case reuseport: lc.Control = reuseportControl + case reuseaddr: + lc.Control = reuseaddrControl } return lc.ListenPacket(context.Background(), network, addr) diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 89ebb64ab..8294d0395 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -252,7 +252,7 @@ loop: } // check for \DDD - if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { + if isDDD(bs[i+1:]) { bs[i] = dddToByte(bs[i+1:]) copy(bs[i+1:ls-3], bs[i+4:]) ls -= 3 @@ -448,7 +448,7 @@ Loop: return string(s), off1, nil } -func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { +func packTxt(txt []string, msg []byte, offset int) (int, error) { if len(txt) == 0 { if offset >= len(msg) { return offset, ErrBuf @@ -458,10 +458,7 @@ func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { } var err error for _, s := range txt { - if len(s) > len(tmp) { - return offset, ErrBuf - } - offset, err = packTxtString(s, msg, offset, tmp) + offset, err = packTxtString(s, msg, offset) if err != nil { return offset, err } @@ -469,32 +466,30 @@ func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } -func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { +func packTxtString(s string, msg []byte, offset int) (int, error) { lenByteOffset := offset - if offset >= len(msg) || len(s) > len(tmp) { + if offset >= len(msg) || len(s) > 256*4+1 /* If all \DDD */ { return offset, ErrBuf } offset++ - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { + for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } - if bs[i] == '\\' { + if s[i] == '\\' { i++ - if i == len(bs) { + if i == len(s) { break } // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) + if isDDD(s[i:]) { + msg[offset] = dddToByte(s[i:]) i += 2 } else { - msg[offset] = bs[i] + msg[offset] = s[i] } } else { - msg[offset] = bs[i] + msg[offset] = s[i] } offset++ } @@ -506,30 +501,28 @@ func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } -func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { - if offset >= len(msg) || len(s) > len(tmp) { +func packOctetString(s string, msg []byte, offset int) (int, error) { + if offset >= len(msg) || len(s) > 256*4+1 { return offset, ErrBuf } - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { + for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } - if bs[i] == '\\' { + if s[i] == '\\' { i++ - if i == len(bs) { + if i == len(s) { break } // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) + if isDDD(s[i:]) { + msg[offset] = dddToByte(s[i:]) i += 2 } else { - msg[offset] = bs[i] + msg[offset] = s[i] } } else { - msg[offset] = bs[i] + msg[offset] = s[i] } offset++ } @@ -551,12 +544,11 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { // Helpers for dealing with escaped bytes func isDigit(b byte) bool { return b >= '0' && b <= '9' } -func dddToByte(s []byte) byte { - _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 - return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) +func isDDD[T ~[]byte | ~string](s T) bool { + return len(s) >= 3 && isDigit(s[0]) && isDigit(s[1]) && isDigit(s[2]) } -func dddStringToByte(s string) byte { +func dddToByte[T ~[]byte | ~string](s T) byte { _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } @@ -680,9 +672,9 @@ func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) // Convert a MsgHdr to a string, with dig-like headers: // -//;; opcode: QUERY, status: NOERROR, id: 48404 +// ;; opcode: QUERY, status: NOERROR, id: 48404 // -//;; flags: qr aa rd ra; +// ;; flags: qr aa rd ra; func (h *MsgHdr) String() string { if h == nil { return " MsgHdr" @@ -866,7 +858,7 @@ func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // The header counts might have been wrong so we need to update it dh.Nscount = uint16(len(dns.Ns)) if err == nil { - dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off) + dns.Extra, _, err = unpackRRslice(int(dh.Arcount), msg, off) } // The header counts might have been wrong so we need to update it dh.Arcount = uint16(len(dns.Extra)) @@ -876,11 +868,11 @@ func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { dns.Rcode |= opt.ExtendedRcode() } - if off != len(msg) { - // TODO(miek) make this an error? - // use PackOpt to let people tell how detailed the error reporting should be? - // println("dns: extra bytes in dns packet", off, "<", len(msg)) - } + // TODO(miek) make this an error? + // use PackOpt to let people tell how detailed the error reporting should be? + // if off != len(msg) { + // // println("dns: extra bytes in dns packet", off, "<", len(msg)) + // } return err } @@ -902,23 +894,38 @@ func (dns *Msg) String() string { return " MsgHdr" } s := dns.MsgHdr.String() + " " - s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " - s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " - s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " - s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "ZONE: " + strconv.Itoa(len(dns.Question)) + ", " + s += "PREREQ: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "UPDATE: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } else { + s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " + s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } opt := dns.IsEdns0() if opt != nil { // OPT PSEUDOSECTION s += opt.String() + "\n" } if len(dns.Question) > 0 { - s += "\n;; QUESTION SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; ZONE SECTION:\n" + } else { + s += "\n;; QUESTION SECTION:\n" + } for _, r := range dns.Question { s += r.String() + "\n" } } if len(dns.Answer) > 0 { - s += "\n;; ANSWER SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; PREREQUISITE SECTION:\n" + } else { + s += "\n;; ANSWER SECTION:\n" + } for _, r := range dns.Answer { if r != nil { s += r.String() + "\n" @@ -926,7 +933,11 @@ func (dns *Msg) String() string { } } if len(dns.Ns) > 0 { - s += "\n;; AUTHORITY SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; UPDATE SECTION:\n" + } else { + s += "\n;; AUTHORITY SECTION:\n" + } for _, r := range dns.Ns { if r != nil { s += r.String() + "\n" @@ -1024,7 +1035,7 @@ func escapedNameLen(s string) int { continue } - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + if isDDD(s[i+1:]) { nameLen -= 3 i += 3 } else { @@ -1065,8 +1076,8 @@ func (dns *Msg) CopyTo(r1 *Msg) *Msg { r1.Compress = dns.Compress if len(dns.Question) > 0 { - r1.Question = make([]Question, len(dns.Question)) - copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy + // TODO(miek): Question is an immutable value, ok to do a shallow-copy + r1.Question = cloneSlice(dns.Question) } rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index ea2035cd2..acec21f7d 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -20,9 +20,7 @@ func unpackDataA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv4len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking a"} } - a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...) - off += net.IPv4len - return a, off, nil + return cloneSlice(msg[off : off+net.IPv4len]), off + net.IPv4len, nil } func packDataA(a net.IP, msg []byte, off int) (int, error) { @@ -47,9 +45,7 @@ func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv6len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking aaaa"} } - aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...) - off += net.IPv6len - return aaaa, off, nil + return cloneSlice(msg[off : off+net.IPv6len]), off + net.IPv6len, nil } func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) { @@ -299,8 +295,7 @@ func unpackString(msg []byte, off int) (string, int, error) { } func packString(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packTxtString(s, msg, off, txtTmp) + off, err := packTxtString(s, msg, off) if err != nil { return len(msg), err } @@ -402,8 +397,7 @@ func unpackStringTxt(msg []byte, off int) ([]string, int, error) { } func packStringTxt(s []string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many. - off, err := packTxt(s, msg, off, txtTmp) + off, err := packTxt(s, msg, off) if err != nil { return len(msg), err } @@ -412,29 +406,24 @@ func packStringTxt(s []string, msg []byte, off int) (int, error) { func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) { var edns []EDNS0 -Option: - var code uint16 - if off+4 > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - code = binary.BigEndian.Uint16(msg[off:]) - off += 2 - optlen := binary.BigEndian.Uint16(msg[off:]) - off += 2 - if off+int(optlen) > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - e := makeDataOpt(code) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - - if off < len(msg) { - goto Option + for off < len(msg) { + if off+4 > len(msg) { + return nil, len(msg), &Error{err: "overflow unpacking opt"} + } + code := binary.BigEndian.Uint16(msg[off:]) + off += 2 + optlen := binary.BigEndian.Uint16(msg[off:]) + off += 2 + if off+int(optlen) > len(msg) { + return nil, len(msg), &Error{err: "overflow unpacking opt"} + } + opt := makeDataOpt(code) + if err := opt.unpack(msg[off : off+int(optlen)]); err != nil { + return nil, len(msg), err + } + edns = append(edns, opt) + off += int(optlen) } - return edns, off, nil } @@ -463,8 +452,7 @@ func unpackStringOctet(msg []byte, off int) (string, int, error) { } func packStringOctet(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packOctetString(s, msg, off, txtTmp) + off, err := packOctetString(s, msg, off) if err != nil { return len(msg), err } @@ -625,7 +613,7 @@ func unpackDataSVCB(msg []byte, off int) ([]SVCBKeyValue, int, error) { } func packDataSVCB(pairs []SVCBKeyValue, msg []byte, off int) (int, error) { - pairs = append([]SVCBKeyValue(nil), pairs...) + pairs = cloneSlice(pairs) sort.Slice(pairs, func(i, j int) bool { return pairs[i].Key() < pairs[j].Key() }) @@ -810,3 +798,37 @@ func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) { Network: ipnet, }, off, nil } + +func unpackIPSECGateway(msg []byte, off int, gatewayType uint8) (net.IP, string, int, error) { + var retAddr net.IP + var retString string + var err error + + switch gatewayType { + case IPSECGatewayNone: // do nothing + case IPSECGatewayIPv4: + retAddr, off, err = unpackDataA(msg, off) + case IPSECGatewayIPv6: + retAddr, off, err = unpackDataAAAA(msg, off) + case IPSECGatewayHost: + retString, off, err = UnpackDomainName(msg, off) + } + + return retAddr, retString, off, err +} + +func packIPSECGateway(gatewayAddr net.IP, gatewayString string, msg []byte, off int, gatewayType uint8, compression compressionMap, compress bool) (int, error) { + var err error + + switch gatewayType { + case IPSECGatewayNone: // do nothing + case IPSECGatewayIPv4: + off, err = packDataA(gatewayAddr, msg, off) + case IPSECGatewayIPv6: + off, err = packDataAAAA(gatewayAddr, msg, off) + case IPSECGatewayHost: + off, err = packDomainName(gatewayString, msg, off, compression, compress) + } + + return off, err +} diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index d256b652e..350ea5a47 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -84,7 +84,7 @@ Fetch: err := r.Data.Parse(text) if err != nil { - return &ParseError{"", err.Error(), l} + return &ParseError{wrappedErr: err, lex: l} } return nil diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index 57be98827..1f92ae421 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -4,19 +4,21 @@ import ( "bufio" "fmt" "io" + "io/fs" "os" + "path" "path/filepath" "strconv" "strings" ) -const maxTok = 2048 // Largest token we can return. +const maxTok = 512 // Token buffer start size, and growth size amount. // The maximum depth of $INCLUDE directives supported by the // ZoneParser API. const maxIncludeDepth = 7 -// Tokinize a RFC 1035 zone file. The tokenizer will normalize it: +// Tokenize a RFC 1035 zone file. The tokenizer will normalize it: // * Add ownernames if they are left blank; // * Suppress sequences of spaces; // * Make each RR fit on one line (_NEWLINE is send as last) @@ -64,20 +66,26 @@ const ( // ParseError is a parsing error. It contains the parse error and the location in the io.Reader // where the error occurred. type ParseError struct { - file string - err string - lex lex + file string + err string + wrappedErr error + lex lex } func (e *ParseError) Error() (s string) { if e.file != "" { s = e.file + ": " } + if e.err == "" && e.wrappedErr != nil { + e.err = e.wrappedErr.Error() + } s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " + strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column) return } +func (e *ParseError) Unwrap() error { return e.wrappedErr } + type lex struct { token string // text of the token err bool // when true, token text has lexer error @@ -168,8 +176,9 @@ type ZoneParser struct { // sub is used to parse $INCLUDE files and $GENERATE directives. // Next, by calling subNext, forwards the resulting RRs from this // sub parser to the calling code. - sub *ZoneParser - osFile *os.File + sub *ZoneParser + r io.Reader + fsys fs.FS includeDepth uint8 @@ -188,7 +197,7 @@ func NewZoneParser(r io.Reader, origin, file string) *ZoneParser { if origin != "" { origin = Fqdn(origin) if _, ok := IsDomainName(origin); !ok { - pe = &ParseError{file, "bad initial origin name", lex{}} + pe = &ParseError{file: file, err: "bad initial origin name"} } } @@ -220,6 +229,24 @@ func (zp *ZoneParser) SetIncludeAllowed(v bool) { zp.includeAllowed = v } +// SetIncludeFS provides an [fs.FS] to use when looking for the target of +// $INCLUDE directives. ($INCLUDE must still be enabled separately by calling +// [ZoneParser.SetIncludeAllowed].) If fsys is nil, [os.Open] will be used. +// +// When fsys is an on-disk FS, the ability of $INCLUDE to reach files from +// outside its root directory depends upon the FS implementation. For +// instance, [os.DirFS] will refuse to open paths like "../../etc/passwd", +// however it will still follow links which may point anywhere on the system. +// +// FS paths are slash-separated on all systems, even Windows. $INCLUDE paths +// containing other characters such as backslash and colon may be accepted as +// valid, but those characters will never be interpreted by an FS +// implementation as path element separators. See [fs.ValidPath] for more +// details. +func (zp *ZoneParser) SetIncludeFS(fsys fs.FS) { + zp.fsys = fsys +} + // Err returns the first non-EOF error that was encountered by the // ZoneParser. func (zp *ZoneParser) Err() error { @@ -237,7 +264,7 @@ func (zp *ZoneParser) Err() error { } func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) { - zp.parseErr = &ParseError{zp.file, err, l} + zp.parseErr = &ParseError{file: zp.file, err: err, lex: l} return nil, false } @@ -260,9 +287,11 @@ func (zp *ZoneParser) subNext() (RR, bool) { return rr, true } - if zp.sub.osFile != nil { - zp.sub.osFile.Close() - zp.sub.osFile = nil + if zp.sub.r != nil { + if c, ok := zp.sub.r.(io.Closer); ok { + c.Close() + } + zp.sub.r = nil } if zp.sub.Err() != nil { @@ -402,24 +431,44 @@ func (zp *ZoneParser) Next() (RR, bool) { // Start with the new file includePath := l.token - if !filepath.IsAbs(includePath) { - includePath = filepath.Join(filepath.Dir(zp.file), includePath) - } + var r1 io.Reader + var e1 error + if zp.fsys != nil { + // fs.FS always uses / as separator, even on Windows, so use + // path instead of filepath here: + if !path.IsAbs(includePath) { + includePath = path.Join(path.Dir(zp.file), includePath) + } - r1, e1 := os.Open(includePath) + // os.DirFS, and probably others, expect all paths to be + // relative, so clean the path and remove leading / if + // present: + includePath = strings.TrimLeft(path.Clean(includePath), "/") + + r1, e1 = zp.fsys.Open(includePath) + } else { + if !filepath.IsAbs(includePath) { + includePath = filepath.Join(filepath.Dir(zp.file), includePath) + } + r1, e1 = os.Open(includePath) + } if e1 != nil { var as string - if !filepath.IsAbs(l.token) { + if includePath != l.token { as = fmt.Sprintf(" as `%s'", includePath) } - - msg := fmt.Sprintf("failed to open `%s'%s: %v", l.token, as, e1) - return zp.setParseError(msg, l) + zp.parseErr = &ParseError{ + file: zp.file, + wrappedErr: fmt.Errorf("failed to open `%s'%s: %w", l.token, as, e1), + lex: l, + } + return nil, false } zp.sub = NewZoneParser(r1, neworigin, includePath) - zp.sub.defttl, zp.sub.includeDepth, zp.sub.osFile = zp.defttl, zp.includeDepth+1, r1 + zp.sub.defttl, zp.sub.includeDepth, zp.sub.r = zp.defttl, zp.includeDepth+1, r1 zp.sub.SetIncludeAllowed(true) + zp.sub.SetIncludeFS(zp.fsys) return zp.subNext() case zExpectDirTTLBl: if l.value != zBlank { @@ -605,8 +654,6 @@ func (zp *ZoneParser) Next() (RR, bool) { if !isPrivate && zp.c.Peek().token == "" { // This is a dynamic update rr. - // TODO(tmthrgd): Previously slurpRemainder was only called - // for certain RR types, which may have been important. if err := slurpRemainder(zp.c); err != nil { return zp.setParseError(err.err, err.lex) } @@ -765,8 +812,8 @@ func (zl *zlexer) Next() (lex, bool) { } var ( - str [maxTok]byte // Hold string text - com [maxTok]byte // Hold comment text + str = make([]byte, maxTok) // Hold string text + com = make([]byte, maxTok) // Hold comment text stri int // Offset in str (0 means empty) comi int // Offset in com (0 means empty) @@ -785,14 +832,12 @@ func (zl *zlexer) Next() (lex, bool) { l.line, l.column = zl.line, zl.column if stri >= len(str) { - l.token = "token length insufficient for parsing" - l.err = true - return *l, true + // if buffer length is insufficient, increase it. + str = append(str[:], make([]byte, maxTok)...) } if comi >= len(com) { - l.token = "comment length insufficient for parsing" - l.err = true - return *l, true + // if buffer length is insufficient, increase it. + com = append(com[:], make([]byte, maxTok)...) } switch x { @@ -816,7 +861,7 @@ func (zl *zlexer) Next() (lex, bool) { if stri == 0 { // Space directly in the beginning, handled in the grammar } else if zl.owner { - // If we have a string and its the first, make it an owner + // If we have a string and it's the first, make it an owner l.value = zOwner l.token = string(str[:stri]) @@ -1218,42 +1263,34 @@ func stringToCm(token string) (e, m uint8, ok bool) { if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' { token = token[0 : len(token)-1] } - s := strings.SplitN(token, ".", 2) - var meters, cmeters, val int - var err error - switch len(s) { - case 2: - if cmeters, err = strconv.Atoi(s[1]); err != nil { - return - } + + var ( + meters, cmeters, val int + err error + ) + mStr, cmStr, hasCM := strings.Cut(token, ".") + if hasCM { // There's no point in having more than 2 digits in this part, and would rather make the implementation complicated ('123' should be treated as '12'). // So we simply reject it. // We also make sure the first character is a digit to reject '+-' signs. - if len(s[1]) > 2 || s[1][0] < '0' || s[1][0] > '9' { + cmeters, err = strconv.Atoi(cmStr) + if err != nil || len(cmStr) > 2 || cmStr[0] < '0' || cmStr[0] > '9' { return } - if len(s[1]) == 1 { + if len(cmStr) == 1 { // 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm. cmeters *= 10 } - if s[0] == "" { - // This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). - break - } - fallthrough - case 1: - if meters, err = strconv.Atoi(s[0]); err != nil { - return - } + } + // This slighly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). + if !hasCM || mStr != "" { + meters, err = strconv.Atoi(mStr) // RFC1876 states the max value is 90000000.00. The latter two conditions enforce it. - if s[0][0] < '0' || s[0][0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) { + if err != nil || mStr[0] < '0' || mStr[0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) { return } - case 0: - // huh? - return 0, 0, false } - ok = true + if meters > 0 { e = 2 val = meters @@ -1265,8 +1302,7 @@ func stringToCm(token string) (e, m uint8, ok bool) { e++ val /= 10 } - m = uint8(val) - return + return e, uint8(val), true } func toAbsoluteName(name, origin string) (absolute string, ok bool) { @@ -1339,12 +1375,12 @@ func slurpRemainder(c *zlexer) *ParseError { case zBlank: l, _ = c.Next() if l.value != zNewline && l.value != zEOF { - return &ParseError{"", "garbage after rdata", l} + return &ParseError{err: "garbage after rdata", lex: l} } case zNewline: case zEOF: default: - return &ParseError{"", "garbage after rdata", l} + return &ParseError{err: "garbage after rdata", lex: l} } return nil } @@ -1353,16 +1389,16 @@ func slurpRemainder(c *zlexer) *ParseError { // Used for NID and L64 record. func stringToNodeID(l lex) (uint64, *ParseError) { if len(l.token) < 19 { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} + return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } // There must be three colons at fixes positions, if not its a parse error if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} + return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19] u, err := strconv.ParseUint(s, 16, 64) if err != nil { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} + return 0, &ParseError{file: l.token, err: "bad NID/L64 NodeID/Locator64", lex: l} } return u, nil } diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index e398484da..1a90c61f8 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -1,8 +1,9 @@ package dns import ( - "bytes" "encoding/base64" + "errors" + "fmt" "net" "strconv" "strings" @@ -11,23 +12,23 @@ import ( // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error func endingToString(c *zlexer, errstr string) (string, *ParseError) { - var buffer bytes.Buffer + var s strings.Builder l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { - return buffer.String(), &ParseError{"", errstr, l} + return s.String(), &ParseError{err: errstr, lex: l} } switch l.value { case zString: - buffer.WriteString(l.token) + s.WriteString(l.token) case zBlank: // Ok default: - return "", &ParseError{"", errstr, l} + return "", &ParseError{err: errstr, lex: l} } l, _ = c.Next() } - return buffer.String(), nil + return s.String(), nil } // A remainder of the rdata with embedded spaces, split on unquoted whitespace @@ -36,7 +37,7 @@ func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { // Get the remaining data until we see a zNewline l, _ := c.Next() if l.err { - return nil, &ParseError{"", errstr, l} + return nil, &ParseError{err: errstr, lex: l} } // Build the slice @@ -45,7 +46,7 @@ func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { empty := false for l.value != zNewline && l.value != zEOF { if l.err { - return nil, &ParseError{"", errstr, l} + return nil, &ParseError{err: errstr, lex: l} } switch l.value { case zString: @@ -72,7 +73,7 @@ func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { case zBlank: if quote { // zBlank can only be seen in between txt parts. - return nil, &ParseError{"", errstr, l} + return nil, &ParseError{err: errstr, lex: l} } case zQuote: if empty && quote { @@ -81,13 +82,13 @@ func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { quote = !quote empty = true default: - return nil, &ParseError{"", errstr, l} + return nil, &ParseError{err: errstr, lex: l} } l, _ = c.Next() } if quote { - return nil, &ParseError{"", errstr, l} + return nil, &ParseError{err: errstr, lex: l} } return s, nil @@ -102,7 +103,7 @@ func (rr *A) parse(c *zlexer, o string) *ParseError { // IPv4. isIPv4 := !strings.Contains(l.token, ":") if rr.A == nil || !isIPv4 || l.err { - return &ParseError{"", "bad A A", l} + return &ParseError{err: "bad A A", lex: l} } return slurpRemainder(c) } @@ -114,7 +115,7 @@ func (rr *AAAA) parse(c *zlexer, o string) *ParseError { // addresses cannot include ":". isIPv6 := strings.Contains(l.token, ":") if rr.AAAA == nil || !isIPv6 || l.err { - return &ParseError{"", "bad AAAA AAAA", l} + return &ParseError{err: "bad AAAA AAAA", lex: l} } return slurpRemainder(c) } @@ -123,7 +124,7 @@ func (rr *NS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad NS Ns", l} + return &ParseError{err: "bad NS Ns", lex: l} } rr.Ns = name return slurpRemainder(c) @@ -133,7 +134,7 @@ func (rr *PTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad PTR Ptr", l} + return &ParseError{err: "bad PTR Ptr", lex: l} } rr.Ptr = name return slurpRemainder(c) @@ -143,7 +144,7 @@ func (rr *NSAPPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad NSAP-PTR Ptr", l} + return &ParseError{err: "bad NSAP-PTR Ptr", lex: l} } rr.Ptr = name return slurpRemainder(c) @@ -153,7 +154,7 @@ func (rr *RP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { - return &ParseError{"", "bad RP Mbox", l} + return &ParseError{err: "bad RP Mbox", lex: l} } rr.Mbox = mbox @@ -163,7 +164,7 @@ func (rr *RP) parse(c *zlexer, o string) *ParseError { txt, txtOk := toAbsoluteName(l.token, o) if l.err || !txtOk { - return &ParseError{"", "bad RP Txt", l} + return &ParseError{err: "bad RP Txt", lex: l} } rr.Txt = txt @@ -174,7 +175,7 @@ func (rr *MR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MR Mr", l} + return &ParseError{err: "bad MR Mr", lex: l} } rr.Mr = name return slurpRemainder(c) @@ -184,7 +185,7 @@ func (rr *MB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MB Mb", l} + return &ParseError{err: "bad MB Mb", lex: l} } rr.Mb = name return slurpRemainder(c) @@ -194,7 +195,7 @@ func (rr *MG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MG Mg", l} + return &ParseError{err: "bad MG Mg", lex: l} } rr.Mg = name return slurpRemainder(c) @@ -219,6 +220,29 @@ func (rr *HINFO) parse(c *zlexer, o string) *ParseError { rr.Cpu = chunks[0] rr.Os = strings.Join(chunks[1:], " ") + return nil +} + +// according to RFC 1183 the parsing is identical to HINFO, so just use that code. +func (rr *ISDN) parse(c *zlexer, o string) *ParseError { + chunks, e := endingToTxtSlice(c, "bad ISDN Fields") + if e != nil { + return e + } + + if ln := len(chunks); ln == 0 { + return nil + } else if ln == 1 { + // Can we split it? + if out := strings.Fields(chunks[0]); len(out) > 1 { + chunks = out + } else { + chunks = append(chunks, "") + } + } + + rr.Address = chunks[0] + rr.SubAddress = strings.Join(chunks[1:], " ") return nil } @@ -227,7 +251,7 @@ func (rr *MINFO) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() rmail, rmailOk := toAbsoluteName(l.token, o) if l.err || !rmailOk { - return &ParseError{"", "bad MINFO Rmail", l} + return &ParseError{err: "bad MINFO Rmail", lex: l} } rr.Rmail = rmail @@ -237,7 +261,7 @@ func (rr *MINFO) parse(c *zlexer, o string) *ParseError { email, emailOk := toAbsoluteName(l.token, o) if l.err || !emailOk { - return &ParseError{"", "bad MINFO Email", l} + return &ParseError{err: "bad MINFO Email", lex: l} } rr.Email = email @@ -248,7 +272,7 @@ func (rr *MF) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MF Mf", l} + return &ParseError{err: "bad MF Mf", lex: l} } rr.Mf = name return slurpRemainder(c) @@ -258,7 +282,7 @@ func (rr *MD) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MD Md", l} + return &ParseError{err: "bad MD Md", lex: l} } rr.Md = name return slurpRemainder(c) @@ -268,7 +292,7 @@ func (rr *MX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad MX Pref", l} + return &ParseError{err: "bad MX Pref", lex: l} } rr.Preference = uint16(i) @@ -278,7 +302,7 @@ func (rr *MX) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad MX Mx", l} + return &ParseError{err: "bad MX Mx", lex: l} } rr.Mx = name @@ -289,7 +313,7 @@ func (rr *RT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil { - return &ParseError{"", "bad RT Preference", l} + return &ParseError{err: "bad RT Preference", lex: l} } rr.Preference = uint16(i) @@ -299,7 +323,7 @@ func (rr *RT) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad RT Host", l} + return &ParseError{err: "bad RT Host", lex: l} } rr.Host = name @@ -310,7 +334,7 @@ func (rr *AFSDB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad AFSDB Subtype", l} + return &ParseError{err: "bad AFSDB Subtype", lex: l} } rr.Subtype = uint16(i) @@ -320,7 +344,7 @@ func (rr *AFSDB) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad AFSDB Hostname", l} + return &ParseError{err: "bad AFSDB Hostname", lex: l} } rr.Hostname = name return slurpRemainder(c) @@ -329,7 +353,7 @@ func (rr *AFSDB) parse(c *zlexer, o string) *ParseError { func (rr *X25) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if l.err { - return &ParseError{"", "bad X25 PSDNAddress", l} + return &ParseError{err: "bad X25 PSDNAddress", lex: l} } rr.PSDNAddress = l.token return slurpRemainder(c) @@ -339,7 +363,7 @@ func (rr *KX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad KX Pref", l} + return &ParseError{err: "bad KX Pref", lex: l} } rr.Preference = uint16(i) @@ -349,7 +373,7 @@ func (rr *KX) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad KX Exchanger", l} + return &ParseError{err: "bad KX Exchanger", lex: l} } rr.Exchanger = name return slurpRemainder(c) @@ -359,7 +383,7 @@ func (rr *CNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad CNAME Target", l} + return &ParseError{err: "bad CNAME Target", lex: l} } rr.Target = name return slurpRemainder(c) @@ -369,7 +393,7 @@ func (rr *DNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad DNAME Target", l} + return &ParseError{err: "bad DNAME Target", lex: l} } rr.Target = name return slurpRemainder(c) @@ -379,7 +403,7 @@ func (rr *SOA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() ns, nsOk := toAbsoluteName(l.token, o) if l.err || !nsOk { - return &ParseError{"", "bad SOA Ns", l} + return &ParseError{err: "bad SOA Ns", lex: l} } rr.Ns = ns @@ -389,7 +413,7 @@ func (rr *SOA) parse(c *zlexer, o string) *ParseError { mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { - return &ParseError{"", "bad SOA Mbox", l} + return &ParseError{err: "bad SOA Mbox", lex: l} } rr.Mbox = mbox @@ -402,16 +426,16 @@ func (rr *SOA) parse(c *zlexer, o string) *ParseError { for i := 0; i < 5; i++ { l, _ = c.Next() if l.err { - return &ParseError{"", "bad SOA zone parameter", l} + return &ParseError{err: "bad SOA zone parameter", lex: l} } if j, err := strconv.ParseUint(l.token, 10, 32); err != nil { if i == 0 { // Serial must be a number - return &ParseError{"", "bad SOA zone parameter", l} + return &ParseError{err: "bad SOA zone parameter", lex: l} } // We allow other fields to be unitful duration strings if v, ok = stringToTTL(l.token); !ok { - return &ParseError{"", "bad SOA zone parameter", l} + return &ParseError{err: "bad SOA zone parameter", lex: l} } } else { @@ -441,7 +465,7 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad SRV Priority", l} + return &ParseError{err: "bad SRV Priority", lex: l} } rr.Priority = uint16(i) @@ -449,7 +473,7 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { - return &ParseError{"", "bad SRV Weight", l} + return &ParseError{err: "bad SRV Weight", lex: l} } rr.Weight = uint16(i) @@ -457,7 +481,7 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { - return &ParseError{"", "bad SRV Port", l} + return &ParseError{err: "bad SRV Port", lex: l} } rr.Port = uint16(i) @@ -467,7 +491,7 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad SRV Target", l} + return &ParseError{err: "bad SRV Target", lex: l} } rr.Target = name return slurpRemainder(c) @@ -477,7 +501,7 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad NAPTR Order", l} + return &ParseError{err: "bad NAPTR Order", lex: l} } rr.Order = uint16(i) @@ -485,7 +509,7 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { - return &ParseError{"", "bad NAPTR Preference", l} + return &ParseError{err: "bad NAPTR Preference", lex: l} } rr.Preference = uint16(i) @@ -493,57 +517,57 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Flags", l} + return &ParseError{err: "bad NAPTR Flags", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Flags = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Flags", l} + return &ParseError{err: "bad NAPTR Flags", lex: l} } } else if l.value == zQuote { rr.Flags = "" } else { - return &ParseError{"", "bad NAPTR Flags", l} + return &ParseError{err: "bad NAPTR Flags", lex: l} } // Service c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Service", l} + return &ParseError{err: "bad NAPTR Service", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Service = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Service", l} + return &ParseError{err: "bad NAPTR Service", lex: l} } } else if l.value == zQuote { rr.Service = "" } else { - return &ParseError{"", "bad NAPTR Service", l} + return &ParseError{err: "bad NAPTR Service", lex: l} } // Regexp c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Regexp", l} + return &ParseError{err: "bad NAPTR Regexp", lex: l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Regexp = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{"", "bad NAPTR Regexp", l} + return &ParseError{err: "bad NAPTR Regexp", lex: l} } } else if l.value == zQuote { rr.Regexp = "" } else { - return &ParseError{"", "bad NAPTR Regexp", l} + return &ParseError{err: "bad NAPTR Regexp", lex: l} } // After quote no space?? @@ -553,7 +577,7 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad NAPTR Replacement", l} + return &ParseError{err: "bad NAPTR Replacement", lex: l} } rr.Replacement = name return slurpRemainder(c) @@ -563,7 +587,7 @@ func (rr *TALINK) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() previousName, previousNameOk := toAbsoluteName(l.token, o) if l.err || !previousNameOk { - return &ParseError{"", "bad TALINK PreviousName", l} + return &ParseError{err: "bad TALINK PreviousName", lex: l} } rr.PreviousName = previousName @@ -573,7 +597,7 @@ func (rr *TALINK) parse(c *zlexer, o string) *ParseError { nextName, nextNameOk := toAbsoluteName(l.token, o) if l.err || !nextNameOk { - return &ParseError{"", "bad TALINK NextName", l} + return &ParseError{err: "bad TALINK NextName", lex: l} } rr.NextName = nextName @@ -591,7 +615,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err || i > 90 { - return &ParseError{"", "bad LOC Latitude", l} + return &ParseError{err: "bad LOC Latitude", lex: l} } rr.Latitude = 1000 * 60 * 60 * uint32(i) @@ -602,7 +626,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { goto East } if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 { - return &ParseError{"", "bad LOC Latitude minutes", l} + return &ParseError{err: "bad LOC Latitude minutes", lex: l} } else { rr.Latitude += 1000 * 60 * uint32(i) } @@ -610,7 +634,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { - return &ParseError{"", "bad LOC Latitude seconds", l} + return &ParseError{err: "bad LOC Latitude seconds", lex: l} } else { rr.Latitude += uint32(1000 * i) } @@ -621,14 +645,14 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { goto East } // If still alive, flag an error - return &ParseError{"", "bad LOC Latitude North/South", l} + return &ParseError{err: "bad LOC Latitude North/South", lex: l} East: // East c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 180 { - return &ParseError{"", "bad LOC Longitude", l} + return &ParseError{err: "bad LOC Longitude", lex: l} } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } @@ -639,14 +663,14 @@ East: goto Altitude } if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 { - return &ParseError{"", "bad LOC Longitude minutes", l} + return &ParseError{err: "bad LOC Longitude minutes", lex: l} } else { rr.Longitude += 1000 * 60 * uint32(i) } c.Next() // zBlank l, _ = c.Next() if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { - return &ParseError{"", "bad LOC Longitude seconds", l} + return &ParseError{err: "bad LOC Longitude seconds", lex: l} } else { rr.Longitude += uint32(1000 * i) } @@ -657,19 +681,19 @@ East: goto Altitude } // If still alive, flag an error - return &ParseError{"", "bad LOC Longitude East/West", l} + return &ParseError{err: "bad LOC Longitude East/West", lex: l} Altitude: c.Next() // zBlank l, _ = c.Next() if l.token == "" || l.err { - return &ParseError{"", "bad LOC Altitude", l} + return &ParseError{err: "bad LOC Altitude", lex: l} } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } if i, err := strconv.ParseFloat(l.token, 64); err != nil { - return &ParseError{"", "bad LOC Altitude", l} + return &ParseError{err: "bad LOC Altitude", lex: l} } else { rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) } @@ -684,19 +708,19 @@ Altitude: case 0: // Size exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{"", "bad LOC Size", l} + return &ParseError{err: "bad LOC Size", lex: l} } rr.Size = exp&0x0f | m<<4&0xf0 case 1: // HorizPre exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{"", "bad LOC HorizPre", l} + return &ParseError{err: "bad LOC HorizPre", lex: l} } rr.HorizPre = exp&0x0f | m<<4&0xf0 case 2: // VertPre exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{"", "bad LOC VertPre", l} + return &ParseError{err: "bad LOC VertPre", lex: l} } rr.VertPre = exp&0x0f | m<<4&0xf0 } @@ -704,7 +728,7 @@ Altitude: case zBlank: // Ok default: - return &ParseError{"", "bad LOC Size, HorizPre or VertPre", l} + return &ParseError{err: "bad LOC Size, HorizPre or VertPre", lex: l} } l, _ = c.Next() } @@ -716,14 +740,14 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad HIP PublicKeyAlgorithm", l} + return &ParseError{err: "bad HIP PublicKeyAlgorithm", lex: l} } rr.PublicKeyAlgorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if l.token == "" || l.err { - return &ParseError{"", "bad HIP Hit", l} + return &ParseError{err: "bad HIP Hit", lex: l} } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 @@ -731,12 +755,12 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString if l.token == "" || l.err { - return &ParseError{"", "bad HIP PublicKey", l} + return &ParseError{err: "bad HIP PublicKey", lex: l} } rr.PublicKey = l.token // This cannot contain spaces decodedPK, decodedPKerr := base64.StdEncoding.DecodeString(rr.PublicKey) if decodedPKerr != nil { - return &ParseError{"", "bad HIP PublicKey", l} + return &ParseError{err: "bad HIP PublicKey", lex: l} } rr.PublicKeyLength = uint16(len(decodedPK)) @@ -748,13 +772,13 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { case zString: name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad HIP RendezvousServers", l} + return &ParseError{err: "bad HIP RendezvousServers", lex: l} } xs = append(xs, name) case zBlank: // Ok default: - return &ParseError{"", "bad HIP RendezvousServers", l} + return &ParseError{err: "bad HIP RendezvousServers", lex: l} } l, _ = c.Next() } @@ -768,7 +792,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError { if v, ok := StringToCertType[l.token]; ok { rr.Type = v } else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil { - return &ParseError{"", "bad CERT Type", l} + return &ParseError{err: "bad CERT Type", lex: l} } else { rr.Type = uint16(i) } @@ -776,7 +800,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() // zString i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad CERT KeyTag", l} + return &ParseError{err: "bad CERT KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank @@ -784,7 +808,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError { if v, ok := StringToAlgorithm[l.token]; ok { rr.Algorithm = v } else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { - return &ParseError{"", "bad CERT Algorithm", l} + return &ParseError{err: "bad CERT Algorithm", lex: l} } else { rr.Algorithm = uint8(i) } @@ -810,7 +834,7 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { j, e := strconv.ParseUint(l.token, 10, 32) if e != nil { // Serial must be a number - return &ParseError{"", "bad CSYNC serial", l} + return &ParseError{err: "bad CSYNC serial", lex: l} } rr.Serial = uint32(j) @@ -820,7 +844,7 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { j, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil { // Serial must be a number - return &ParseError{"", "bad CSYNC flags", l} + return &ParseError{err: "bad CSYNC flags", lex: l} } rr.Flags = uint16(j) @@ -838,12 +862,12 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{"", "bad CSYNC TypeBitMap", l} + return &ParseError{err: "bad CSYNC TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{"", "bad CSYNC TypeBitMap", l} + return &ParseError{err: "bad CSYNC TypeBitMap", lex: l} } l, _ = c.Next() } @@ -854,7 +878,7 @@ func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{"", "bad ZONEMD Serial", l} + return &ParseError{err: "bad ZONEMD Serial", lex: l} } rr.Serial = uint32(i) @@ -862,7 +886,7 @@ func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad ZONEMD Scheme", l} + return &ParseError{err: "bad ZONEMD Scheme", lex: l} } rr.Scheme = uint8(i) @@ -870,7 +894,7 @@ func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { - return &ParseError{"", "bad ZONEMD Hash Algorithm", l} + return &ParseError{err: "bad ZONEMD Hash Algorithm", lex: l} } rr.Hash = uint8(i) @@ -891,11 +915,11 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { if strings.HasPrefix(tokenUpper, "TYPE") { t, ok = typeToInt(l.token) if !ok { - return &ParseError{"", "bad RRSIG Typecovered", l} + return &ParseError{err: "bad RRSIG Typecovered", lex: l} } rr.TypeCovered = t } else { - return &ParseError{"", "bad RRSIG Typecovered", l} + return &ParseError{err: "bad RRSIG Typecovered", lex: l} } } else { rr.TypeCovered = t @@ -903,17 +927,24 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() + if l.err { + return &ParseError{err: "bad RRSIG Algorithm", lex: l} + } i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{"", "bad RRSIG Algorithm", l} + rr.Algorithm = uint8(i) // if 0 we'll check the mnemonic in the if + if e != nil { + v, ok := StringToAlgorithm[l.token] + if !ok { + return &ParseError{err: "bad RRSIG Algorithm", lex: l} + } + rr.Algorithm = v } - rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad RRSIG Labels", l} + return &ParseError{err: "bad RRSIG Labels", lex: l} } rr.Labels = uint8(i) @@ -921,7 +952,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 32) if e2 != nil || l.err { - return &ParseError{"", "bad RRSIG OrigTtl", l} + return &ParseError{err: "bad RRSIG OrigTtl", lex: l} } rr.OrigTtl = uint32(i) @@ -932,7 +963,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { if i, err := strconv.ParseUint(l.token, 10, 32); err == nil { rr.Expiration = uint32(i) } else { - return &ParseError{"", "bad RRSIG Expiration", l} + return &ParseError{err: "bad RRSIG Expiration", lex: l} } } else { rr.Expiration = i @@ -944,7 +975,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { if i, err := strconv.ParseUint(l.token, 10, 32); err == nil { rr.Inception = uint32(i) } else { - return &ParseError{"", "bad RRSIG Inception", l} + return &ParseError{err: "bad RRSIG Inception", lex: l} } } else { rr.Inception = i @@ -954,7 +985,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e3 := strconv.ParseUint(l.token, 10, 16) if e3 != nil || l.err { - return &ParseError{"", "bad RRSIG KeyTag", l} + return &ParseError{err: "bad RRSIG KeyTag", lex: l} } rr.KeyTag = uint16(i) @@ -963,7 +994,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { rr.SignerName = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad RRSIG SignerName", l} + return &ParseError{err: "bad RRSIG SignerName", lex: l} } rr.SignerName = name @@ -976,11 +1007,13 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { return nil } +func (rr *NXT) parse(c *zlexer, o string) *ParseError { return rr.NSEC.parse(c, o) } + func (rr *NSEC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad NSEC NextDomain", l} + return &ParseError{err: "bad NSEC NextDomain", lex: l} } rr.NextDomain = name @@ -998,12 +1031,12 @@ func (rr *NSEC) parse(c *zlexer, o string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{"", "bad NSEC TypeBitMap", l} + return &ParseError{err: "bad NSEC TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{"", "bad NSEC TypeBitMap", l} + return &ParseError{err: "bad NSEC TypeBitMap", lex: l} } l, _ = c.Next() } @@ -1014,27 +1047,27 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad NSEC3 Hash", l} + return &ParseError{err: "bad NSEC3 Hash", lex: l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad NSEC3 Flags", l} + return &ParseError{err: "bad NSEC3 Flags", lex: l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { - return &ParseError{"", "bad NSEC3 Iterations", l} + return &ParseError{err: "bad NSEC3 Iterations", lex: l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if l.token == "" || l.err { - return &ParseError{"", "bad NSEC3 Salt", l} + return &ParseError{err: "bad NSEC3 Salt", lex: l} } if l.token != "-" { rr.SaltLength = uint8(len(l.token)) / 2 @@ -1044,7 +1077,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { c.Next() l, _ = c.Next() if l.token == "" || l.err { - return &ParseError{"", "bad NSEC3 NextDomain", l} + return &ParseError{err: "bad NSEC3 NextDomain", lex: l} } rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.NextDomain = l.token @@ -1063,12 +1096,12 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{"", "bad NSEC3 TypeBitMap", l} + return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{"", "bad NSEC3 TypeBitMap", l} + return &ParseError{err: "bad NSEC3 TypeBitMap", lex: l} } l, _ = c.Next() } @@ -1079,21 +1112,21 @@ func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad NSEC3PARAM Hash", l} + return &ParseError{err: "bad NSEC3PARAM Hash", lex: l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad NSEC3PARAM Flags", l} + return &ParseError{err: "bad NSEC3PARAM Flags", lex: l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 16) if e2 != nil || l.err { - return &ParseError{"", "bad NSEC3PARAM Iterations", l} + return &ParseError{err: "bad NSEC3PARAM Iterations", lex: l} } rr.Iterations = uint16(i) c.Next() @@ -1108,7 +1141,7 @@ func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError { func (rr *EUI48) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if len(l.token) != 17 || l.err { - return &ParseError{"", "bad EUI48 Address", l} + return &ParseError{err: "bad EUI48 Address", lex: l} } addr := make([]byte, 12) dash := 0 @@ -1117,7 +1150,7 @@ func (rr *EUI48) parse(c *zlexer, o string) *ParseError { addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { - return &ParseError{"", "bad EUI48 Address", l} + return &ParseError{err: "bad EUI48 Address", lex: l} } } addr[10] = l.token[15] @@ -1125,7 +1158,7 @@ func (rr *EUI48) parse(c *zlexer, o string) *ParseError { i, e := strconv.ParseUint(string(addr), 16, 48) if e != nil { - return &ParseError{"", "bad EUI48 Address", l} + return &ParseError{err: "bad EUI48 Address", lex: l} } rr.Address = i return slurpRemainder(c) @@ -1134,7 +1167,7 @@ func (rr *EUI48) parse(c *zlexer, o string) *ParseError { func (rr *EUI64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if len(l.token) != 23 || l.err { - return &ParseError{"", "bad EUI64 Address", l} + return &ParseError{err: "bad EUI64 Address", lex: l} } addr := make([]byte, 16) dash := 0 @@ -1143,7 +1176,7 @@ func (rr *EUI64) parse(c *zlexer, o string) *ParseError { addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { - return &ParseError{"", "bad EUI64 Address", l} + return &ParseError{err: "bad EUI64 Address", lex: l} } } addr[14] = l.token[21] @@ -1151,7 +1184,7 @@ func (rr *EUI64) parse(c *zlexer, o string) *ParseError { i, e := strconv.ParseUint(string(addr), 16, 64) if e != nil { - return &ParseError{"", "bad EUI68 Address", l} + return &ParseError{err: "bad EUI68 Address", lex: l} } rr.Address = i return slurpRemainder(c) @@ -1161,14 +1194,14 @@ func (rr *SSHFP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad SSHFP Algorithm", l} + return &ParseError{err: "bad SSHFP Algorithm", lex: l} } rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad SSHFP Type", l} + return &ParseError{err: "bad SSHFP Type", lex: l} } rr.Type = uint8(i) c.Next() // zBlank @@ -1184,21 +1217,21 @@ func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad " + typ + " Flags", l} + return &ParseError{err: "bad " + typ + " Flags", lex: l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad " + typ + " Protocol", l} + return &ParseError{err: "bad " + typ + " Protocol", lex: l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { - return &ParseError{"", "bad " + typ + " Algorithm", l} + return &ParseError{err: "bad " + typ + " Algorithm", lex: l} } rr.Algorithm = uint8(i) s, e3 := endingToString(c, "bad "+typ+" PublicKey") @@ -1216,25 +1249,136 @@ func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") } func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") } +func (rr *IPSECKEY) parse(c *zlexer, o string) *ParseError { + l, _ := c.Next() + num, err := strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{err: "bad IPSECKEY value", lex: l} + } + rr.Precedence = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{err: "bad IPSECKEY value", lex: l} + } + rr.GatewayType = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{err: "bad IPSECKEY value", lex: l} + } + rr.Algorithm = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err { + return &ParseError{err: "bad IPSECKEY gateway", lex: l} + } + + rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType) + if err != nil { + return &ParseError{wrappedErr: fmt.Errorf("IPSECKEY %w", err), lex: l} + } + + c.Next() // zBlank + + s, pErr := endingToString(c, "bad IPSECKEY PublicKey") + if pErr != nil { + return pErr + } + rr.PublicKey = s + return slurpRemainder(c) +} + +func (rr *AMTRELAY) parse(c *zlexer, o string) *ParseError { + l, _ := c.Next() + num, err := strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{err: "bad AMTRELAY value", lex: l} + } + rr.Precedence = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err || !(l.token == "0" || l.token == "1") { + return &ParseError{err: "bad discovery value", lex: l} + } + if l.token == "1" { + rr.GatewayType = 0x80 + } + + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{err: "bad AMTRELAY value", lex: l} + } + rr.GatewayType |= uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err { + return &ParseError{err: "bad AMTRELAY gateway", lex: l} + } + + rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType&0x7f) + if err != nil { + return &ParseError{wrappedErr: fmt.Errorf("AMTRELAY %w", err), lex: l} + } + + return slurpRemainder(c) +} + +// same constants and parsing between IPSECKEY and AMTRELAY +func parseAddrHostUnion(token, o string, gatewayType uint8) (addr net.IP, host string, err error) { + switch gatewayType { + case IPSECGatewayNone: + if token != "." { + return addr, host, errors.New("gateway type none with gateway set") + } + case IPSECGatewayIPv4, IPSECGatewayIPv6: + addr = net.ParseIP(token) + if addr == nil { + return addr, host, errors.New("gateway IP invalid") + } + if (addr.To4() == nil) == (gatewayType == IPSECGatewayIPv4) { + return addr, host, errors.New("gateway IP family mismatch") + } + case IPSECGatewayHost: + var ok bool + host, ok = toAbsoluteName(token, o) + if !ok { + return addr, host, errors.New("invalid gateway host") + } + } + + return addr, host, nil +} + func (rr *RKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad RKEY Flags", l} + return &ParseError{err: "bad RKEY Flags", lex: l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad RKEY Protocol", l} + return &ParseError{err: "bad RKEY Protocol", lex: l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { - return &ParseError{"", "bad RKEY Algorithm", l} + return &ParseError{err: "bad RKEY Algorithm", lex: l} } rr.Algorithm = uint8(i) s, e3 := endingToString(c, "bad RKEY PublicKey") @@ -1267,21 +1411,21 @@ func (rr *GPOS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() _, e := strconv.ParseFloat(l.token, 64) if e != nil || l.err { - return &ParseError{"", "bad GPOS Longitude", l} + return &ParseError{err: "bad GPOS Longitude", lex: l} } rr.Longitude = l.token c.Next() // zBlank l, _ = c.Next() _, e1 := strconv.ParseFloat(l.token, 64) if e1 != nil || l.err { - return &ParseError{"", "bad GPOS Latitude", l} + return &ParseError{err: "bad GPOS Latitude", lex: l} } rr.Latitude = l.token c.Next() // zBlank l, _ = c.Next() _, e2 := strconv.ParseFloat(l.token, 64) if e2 != nil || l.err { - return &ParseError{"", "bad GPOS Altitude", l} + return &ParseError{err: "bad GPOS Altitude", lex: l} } rr.Altitude = l.token return slurpRemainder(c) @@ -1291,7 +1435,7 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad " + typ + " KeyTag", l} + return &ParseError{err: "bad " + typ + " KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank @@ -1300,7 +1444,7 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { - return &ParseError{"", "bad " + typ + " Algorithm", l} + return &ParseError{err: "bad " + typ + " Algorithm", lex: l} } rr.Algorithm = i } else { @@ -1310,7 +1454,7 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad " + typ + " DigestType", l} + return &ParseError{err: "bad " + typ + " DigestType", lex: l} } rr.DigestType = uint8(i) s, e2 := endingToString(c, "bad "+typ+" Digest") @@ -1325,7 +1469,7 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad TA KeyTag", l} + return &ParseError{err: "bad TA KeyTag", lex: l} } rr.KeyTag = uint16(i) c.Next() // zBlank @@ -1334,7 +1478,7 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { - return &ParseError{"", "bad TA Algorithm", l} + return &ParseError{err: "bad TA Algorithm", lex: l} } rr.Algorithm = i } else { @@ -1344,7 +1488,7 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad TA DigestType", l} + return &ParseError{err: "bad TA DigestType", lex: l} } rr.DigestType = uint8(i) s, e2 := endingToString(c, "bad TA Digest") @@ -1359,21 +1503,21 @@ func (rr *TLSA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad TLSA Usage", l} + return &ParseError{err: "bad TLSA Usage", lex: l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad TLSA Selector", l} + return &ParseError{err: "bad TLSA Selector", lex: l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { - return &ParseError{"", "bad TLSA MatchingType", l} + return &ParseError{err: "bad TLSA MatchingType", lex: l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t @@ -1389,21 +1533,21 @@ func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad SMIMEA Usage", l} + return &ParseError{err: "bad SMIMEA Usage", lex: l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad SMIMEA Selector", l} + return &ParseError{err: "bad SMIMEA Selector", lex: l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() i, e2 := strconv.ParseUint(l.token, 10, 8) if e2 != nil || l.err { - return &ParseError{"", "bad SMIMEA MatchingType", l} + return &ParseError{err: "bad SMIMEA MatchingType", lex: l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t @@ -1418,14 +1562,14 @@ func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError { func (rr *RFC3597) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if l.token != "\\#" { - return &ParseError{"", "bad RFC3597 Rdata", l} + return &ParseError{err: "bad RFC3597 Rdata", lex: l} } c.Next() // zBlank l, _ = c.Next() rdlength, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad RFC3597 Rdata ", l} + return &ParseError{err: "bad RFC3597 Rdata ", lex: l} } s, e1 := endingToString(c, "bad RFC3597 Rdata") @@ -1433,7 +1577,7 @@ func (rr *RFC3597) parse(c *zlexer, o string) *ParseError { return e1 } if int(rdlength)*2 != len(s) { - return &ParseError{"", "bad RFC3597 Rdata", l} + return &ParseError{err: "bad RFC3597 Rdata", lex: l} } rr.Rdata = s return nil @@ -1481,14 +1625,14 @@ func (rr *URI) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad URI Priority", l} + return &ParseError{err: "bad URI Priority", lex: l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 16) if e1 != nil || l.err { - return &ParseError{"", "bad URI Weight", l} + return &ParseError{err: "bad URI Weight", lex: l} } rr.Weight = uint16(i) @@ -1498,7 +1642,7 @@ func (rr *URI) parse(c *zlexer, o string) *ParseError { return e2 } if len(s) != 1 { - return &ParseError{"", "bad URI Target", l} + return &ParseError{err: "bad URI Target", lex: l} } rr.Target = s[0] return nil @@ -1518,7 +1662,7 @@ func (rr *NID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad NID Preference", l} + return &ParseError{err: "bad NID Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank @@ -1535,14 +1679,14 @@ func (rr *L32) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad L32 Preference", l} + return &ParseError{err: "bad L32 Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Locator32 = net.ParseIP(l.token) if rr.Locator32 == nil || l.err { - return &ParseError{"", "bad L32 Locator", l} + return &ParseError{err: "bad L32 Locator", lex: l} } return slurpRemainder(c) } @@ -1551,7 +1695,7 @@ func (rr *LP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad LP Preference", l} + return &ParseError{err: "bad LP Preference", lex: l} } rr.Preference = uint16(i) @@ -1560,7 +1704,7 @@ func (rr *LP) parse(c *zlexer, o string) *ParseError { rr.Fqdn = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{"", "bad LP Fqdn", l} + return &ParseError{err: "bad LP Fqdn", lex: l} } rr.Fqdn = name return slurpRemainder(c) @@ -1570,7 +1714,7 @@ func (rr *L64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad L64 Preference", l} + return &ParseError{err: "bad L64 Preference", lex: l} } rr.Preference = uint16(i) c.Next() // zBlank @@ -1587,7 +1731,7 @@ func (rr *UID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{"", "bad UID Uid", l} + return &ParseError{err: "bad UID Uid", lex: l} } rr.Uid = uint32(i) return slurpRemainder(c) @@ -1597,7 +1741,7 @@ func (rr *GID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{"", "bad GID Gid", l} + return &ParseError{err: "bad GID Gid", lex: l} } rr.Gid = uint32(i) return slurpRemainder(c) @@ -1619,7 +1763,7 @@ func (rr *PX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{"", "bad PX Preference", l} + return &ParseError{err: "bad PX Preference", lex: l} } rr.Preference = uint16(i) @@ -1628,7 +1772,7 @@ func (rr *PX) parse(c *zlexer, o string) *ParseError { rr.Map822 = l.token map822, map822Ok := toAbsoluteName(l.token, o) if l.err || !map822Ok { - return &ParseError{"", "bad PX Map822", l} + return &ParseError{err: "bad PX Map822", lex: l} } rr.Map822 = map822 @@ -1637,7 +1781,7 @@ func (rr *PX) parse(c *zlexer, o string) *ParseError { rr.Mapx400 = l.token mapx400, mapx400Ok := toAbsoluteName(l.token, o) if l.err || !mapx400Ok { - return &ParseError{"", "bad PX Mapx400", l} + return &ParseError{err: "bad PX Mapx400", lex: l} } rr.Mapx400 = mapx400 return slurpRemainder(c) @@ -1647,14 +1791,14 @@ func (rr *CAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad CAA Flag", l} + return &ParseError{err: "bad CAA Flag", lex: l} } rr.Flag = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if l.value != zString { - return &ParseError{"", "bad CAA Tag", l} + return &ParseError{err: "bad CAA Tag", lex: l} } rr.Tag = l.token @@ -1664,7 +1808,7 @@ func (rr *CAA) parse(c *zlexer, o string) *ParseError { return e1 } if len(s) != 1 { - return &ParseError{"", "bad CAA Value", l} + return &ParseError{err: "bad CAA Value", lex: l} } rr.Value = s[0] return nil @@ -1675,7 +1819,7 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { // Algorithm if l.value != zString { - return &ParseError{"", "bad TKEY algorithm", l} + return &ParseError{err: "bad TKEY algorithm", lex: l} } rr.Algorithm = l.token c.Next() // zBlank @@ -1684,13 +1828,13 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{"", "bad TKEY key length", l} + return &ParseError{err: "bad TKEY key length", lex: l} } rr.KeySize = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { - return &ParseError{"", "bad TKEY key", l} + return &ParseError{err: "bad TKEY key", lex: l} } rr.Key = l.token c.Next() // zBlank @@ -1699,13 +1843,13 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() i, e1 := strconv.ParseUint(l.token, 10, 8) if e1 != nil || l.err { - return &ParseError{"", "bad TKEY otherdata length", l} + return &ParseError{err: "bad TKEY otherdata length", lex: l} } rr.OtherLen = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { - return &ParseError{"", "bad TKEY otherday", l} + return &ParseError{err: "bad TKEY otherday", lex: l} } rr.OtherData = l.token return nil @@ -1723,14 +1867,14 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError { continue } if l.value != zString { - return &ParseError{"", "unexpected APL field", l} + return &ParseError{err: "unexpected APL field", lex: l} } // Expected format: [!]afi:address/prefix colon := strings.IndexByte(l.token, ':') if colon == -1 { - return &ParseError{"", "missing colon in APL field", l} + return &ParseError{err: "missing colon in APL field", lex: l} } family, cidr := l.token[:colon], l.token[colon+1:] @@ -1743,7 +1887,7 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError { afi, e := strconv.ParseUint(family, 10, 16) if e != nil { - return &ParseError{"", "failed to parse APL family: " + e.Error(), l} + return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL family: %w", e), lex: l} } var addrLen int switch afi { @@ -1752,19 +1896,19 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError { case 2: addrLen = net.IPv6len default: - return &ParseError{"", "unrecognized APL family", l} + return &ParseError{err: "unrecognized APL family", lex: l} } ip, subnet, e1 := net.ParseCIDR(cidr) if e1 != nil { - return &ParseError{"", "failed to parse APL address: " + e1.Error(), l} + return &ParseError{wrappedErr: fmt.Errorf("failed to parse APL address: %w", e1), lex: l} } if !ip.Equal(subnet.IP) { - return &ParseError{"", "extra bits in APL address", l} + return &ParseError{err: "extra bits in APL address", lex: l} } if len(subnet.IP) != addrLen { - return &ParseError{"", "address mismatch with the APL family", l} + return &ParseError{err: "address mismatch with the APL family", lex: l} } prefixes = append(prefixes, APLPrefix{ diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 4e5a9aa8a..0207d6da2 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -18,7 +18,7 @@ import ( const maxTCPQueries = 128 // aLongTimeAgo is a non-zero time, far in the past, used for -// immediate cancelation of network operations. +// immediate cancellation of network operations. var aLongTimeAgo = time.Unix(1, 0) // Handler is implemented by any value that implements ServeDNS. @@ -224,8 +224,12 @@ type Server struct { // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1). MaxTCPQueries int // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. - // It is only supported on go1.11+ and when using ListenAndServe. + // It is only supported on certain GOOSes and when using ListenAndServe. ReusePort bool + // Whether to set the SO_REUSEADDR socket option, allowing multiple listeners to be bound to a single address. + // Crucially this allows binding when an existing server is listening on `0.0.0.0` or `::`. + // It is only supported on certain GOOSes and when using ListenAndServe. + ReuseAddr bool // AcceptMsgFunc will check the incoming message and will reject it early in the process. // By default DefaultMsgAcceptFunc will be used. MsgAcceptFunc MsgAcceptFunc @@ -304,7 +308,7 @@ func (srv *Server) ListenAndServe() error { switch srv.Net { case "tcp", "tcp4", "tcp6": - l, err := listenTCP(srv.Net, addr, srv.ReusePort) + l, err := listenTCP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } @@ -317,7 +321,7 @@ func (srv *Server) ListenAndServe() error { return errors.New("dns: neither Certificates nor GetCertificate set in Config") } network := strings.TrimSuffix(srv.Net, "-tls") - l, err := listenTCP(network, addr, srv.ReusePort) + l, err := listenTCP(network, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } @@ -327,7 +331,7 @@ func (srv *Server) ListenAndServe() error { unlock() return srv.serveTCP(l) case "udp", "udp4", "udp6": - l, err := listenUDP(srv.Net, addr, srv.ReusePort) + l, err := listenUDP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr) if err != nil { return err } diff --git a/vendor/github.com/miekg/dns/singleinflight.go b/vendor/github.com/miekg/dns/singleinflight.go deleted file mode 100644 index febcc300f..000000000 --- a/vendor/github.com/miekg/dns/singleinflight.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Adapted for dns package usage by Miek Gieben. - -package dns - -import "sync" -import "time" - -// call is an in-flight or completed singleflight.Do call -type call struct { - wg sync.WaitGroup - val *Msg - rtt time.Duration - err error - dups int -} - -// singleflight represents a class of work and forms a namespace in -// which units of work can be executed with duplicate suppression. -type singleflight struct { - sync.Mutex // protects m - m map[string]*call // lazily initialized - - dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges -} - -// Do executes and returns the results of the given function, making -// sure that only one execution is in-flight for a given key at a -// time. If a duplicate comes in, the duplicate caller waits for the -// original to complete and receives the same results. -// The return value shared indicates whether v was given to multiple callers. -func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) { - g.Lock() - if g.m == nil { - g.m = make(map[string]*call) - } - if c, ok := g.m[key]; ok { - c.dups++ - g.Unlock() - c.wg.Wait() - return c.val, c.rtt, c.err, true - } - c := new(call) - c.wg.Add(1) - g.m[key] = c - g.Unlock() - - c.val, c.rtt, c.err = fn() - c.wg.Done() - - if !g.dontDeleteForTesting { - g.Lock() - delete(g.m, key) - g.Unlock() - } - - return c.val, c.rtt, c.err, c.dups > 0 -} diff --git a/vendor/github.com/miekg/dns/svcb.go b/vendor/github.com/miekg/dns/svcb.go index ea58710da..c1a740b68 100644 --- a/vendor/github.com/miekg/dns/svcb.go +++ b/vendor/github.com/miekg/dns/svcb.go @@ -85,7 +85,7 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{l.token, "bad SVCB priority", l} + return &ParseError{file: l.token, err: "bad SVCB priority", lex: l} } rr.Priority = uint16(i) @@ -95,7 +95,7 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{l.token, "bad SVCB Target", l} + return &ParseError{file: l.token, err: "bad SVCB Target", lex: l} } rr.Target = name @@ -111,7 +111,7 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError { if !canHaveNextKey { // The key we can now read was probably meant to be // a part of the last value. - return &ParseError{l.token, "bad SVCB value quotation", l} + return &ParseError{file: l.token, err: "bad SVCB value quotation", lex: l} } // In key=value pairs, value does not have to be quoted unless value @@ -124,7 +124,7 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError { // Key with no value and no equality sign key = l.token } else if idx == 0 { - return &ParseError{l.token, "bad SVCB key", l} + return &ParseError{file: l.token, err: "bad SVCB key", lex: l} } else { key, value = l.token[:idx], l.token[idx+1:] @@ -144,30 +144,30 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError { value = l.token l, _ = c.Next() if l.value != zQuote { - return &ParseError{l.token, "SVCB unterminated value", l} + return &ParseError{file: l.token, err: "SVCB unterminated value", lex: l} } case zQuote: // There's nothing in double quotes. default: - return &ParseError{l.token, "bad SVCB value", l} + return &ParseError{file: l.token, err: "bad SVCB value", lex: l} } } } } kv := makeSVCBKeyValue(svcbStringToKey(key)) if kv == nil { - return &ParseError{l.token, "bad SVCB key", l} + return &ParseError{file: l.token, err: "bad SVCB key", lex: l} } if err := kv.parse(value); err != nil { - return &ParseError{l.token, err.Error(), l} + return &ParseError{file: l.token, wrappedErr: err, lex: l} } xs = append(xs, kv) case zQuote: - return &ParseError{l.token, "SVCB key can't contain double quotes", l} + return &ParseError{file: l.token, err: "SVCB key can't contain double quotes", lex: l} case zBlank: canHaveNextKey = true default: - return &ParseError{l.token, "bad SVCB values", l} + return &ParseError{file: l.token, err: "bad SVCB values", lex: l} } l, _ = c.Next() } @@ -289,7 +289,7 @@ func (s *SVCBMandatory) String() string { } func (s *SVCBMandatory) pack() ([]byte, error) { - codes := append([]SVCBKey(nil), s.Code...) + codes := cloneSlice(s.Code) sort.Slice(codes, func(i, j int) bool { return codes[i] < codes[j] }) @@ -314,10 +314,11 @@ func (s *SVCBMandatory) unpack(b []byte) error { } func (s *SVCBMandatory) parse(b string) error { - str := strings.Split(b, ",") - codes := make([]SVCBKey, 0, len(str)) - for _, e := range str { - codes = append(codes, svcbStringToKey(e)) + codes := make([]SVCBKey, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var key string + key, b, _ = strings.Cut(b, ",") + codes = append(codes, svcbStringToKey(key)) } s.Code = codes return nil @@ -328,9 +329,7 @@ func (s *SVCBMandatory) len() int { } func (s *SVCBMandatory) copy() SVCBKeyValue { - return &SVCBMandatory{ - append([]SVCBKey(nil), s.Code...), - } + return &SVCBMandatory{cloneSlice(s.Code)} } // SVCBAlpn pair is used to list supported connection protocols. @@ -353,7 +352,7 @@ func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN } func (s *SVCBAlpn) String() string { // An ALPN value is a comma-separated list of values, each of which can be // an arbitrary binary value. In order to allow parsing, the comma and - // backslash characters are themselves excaped. + // backslash characters are themselves escaped. // // However, this escaping is done in addition to the normal escaping which // happens in zone files, meaning that these values must be @@ -481,9 +480,7 @@ func (s *SVCBAlpn) len() int { } func (s *SVCBAlpn) copy() SVCBKeyValue { - return &SVCBAlpn{ - append([]string(nil), s.Alpn...), - } + return &SVCBAlpn{cloneSlice(s.Alpn)} } // SVCBNoDefaultAlpn pair signifies no support for default connection protocols. @@ -563,15 +560,15 @@ func (s *SVCBPort) parse(b string) error { // to the hinted IP address may be terminated and a new connection may be opened. // Basic use pattern for creating an ipv4hint option: // -// h := new(dns.HTTPS) -// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} -// e := new(dns.SVCBIPv4Hint) -// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} +// h := new(dns.HTTPS) +// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} +// e := new(dns.SVCBIPv4Hint) +// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} // -// Or +// Or // -// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} -// h.Value = append(h.Value, e) +// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} +// h.Value = append(h.Value, e) type SVCBIPv4Hint struct { Hint []net.IP } @@ -595,6 +592,7 @@ func (s *SVCBIPv4Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%4 != 0 { return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4") } + b = cloneSlice(b) x := make([]net.IP, 0, len(b)/4) for i := 0; i < len(b); i += 4 { x = append(x, net.IP(b[i:i+4])) @@ -616,31 +614,33 @@ func (s *SVCBIPv4Hint) String() string { } func (s *SVCBIPv4Hint) parse(b string) error { + if b == "" { + return errors.New("dns: svcbipv4hint: empty hint") + } if strings.Contains(b, ":") { return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6") } - str := strings.Split(b, ",") - dst := make([]net.IP, len(str)) - for i, e := range str { + + hint := make([]net.IP, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var e string + e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e).To4() if ip == nil { return errors.New("dns: svcbipv4hint: bad ip") } - dst[i] = ip + hint = append(hint, ip) } - s.Hint = dst + s.Hint = hint return nil } func (s *SVCBIPv4Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { - hint[i] = copyIP(ip) - } - - return &SVCBIPv4Hint{ - Hint: hint, + hint[i] = cloneSlice(ip) } + return &SVCBIPv4Hint{Hint: hint} } // SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx]. @@ -660,19 +660,18 @@ func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) } func (s *SVCBECHConfig) len() int { return len(s.ECH) } func (s *SVCBECHConfig) pack() ([]byte, error) { - return append([]byte(nil), s.ECH...), nil + return cloneSlice(s.ECH), nil } func (s *SVCBECHConfig) copy() SVCBKeyValue { - return &SVCBECHConfig{ - append([]byte(nil), s.ECH...), - } + return &SVCBECHConfig{cloneSlice(s.ECH)} } func (s *SVCBECHConfig) unpack(b []byte) error { - s.ECH = append([]byte(nil), b...) + s.ECH = cloneSlice(b) return nil } + func (s *SVCBECHConfig) parse(b string) error { x, err := fromBase64([]byte(b)) if err != nil { @@ -715,6 +714,7 @@ func (s *SVCBIPv6Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%16 != 0 { return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16") } + b = cloneSlice(b) x := make([]net.IP, 0, len(b)/16) for i := 0; i < len(b); i += 16 { ip := net.IP(b[i : i+16]) @@ -739,9 +739,14 @@ func (s *SVCBIPv6Hint) String() string { } func (s *SVCBIPv6Hint) parse(b string) error { - str := strings.Split(b, ",") - dst := make([]net.IP, len(str)) - for i, e := range str { + if b == "" { + return errors.New("dns: svcbipv6hint: empty hint") + } + + hint := make([]net.IP, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var e string + e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e) if ip == nil { return errors.New("dns: svcbipv6hint: bad ip") @@ -749,21 +754,18 @@ func (s *SVCBIPv6Hint) parse(b string) error { if ip.To4() != nil { return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6") } - dst[i] = ip + hint = append(hint, ip) } - s.Hint = dst + s.Hint = hint return nil } func (s *SVCBIPv6Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { - hint[i] = copyIP(ip) - } - - return &SVCBIPv6Hint{ - Hint: hint, + hint[i] = cloneSlice(ip) } + return &SVCBIPv6Hint{Hint: hint} } // SVCBDoHPath pair is used to indicate the URI template that the @@ -831,11 +833,11 @@ type SVCBLocal struct { func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode } func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) } -func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil } +func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil } func (s *SVCBLocal) len() int { return len(s.Data) } func (s *SVCBLocal) unpack(b []byte) error { - s.Data = append([]byte(nil), b...) + s.Data = cloneSlice(b) return nil } @@ -849,9 +851,7 @@ func (s *SVCBLocal) parse(b string) error { } func (s *SVCBLocal) copy() SVCBKeyValue { - return &SVCBLocal{s.KeyCode, - append([]byte(nil), s.Data...), - } + return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)} } func (rr *SVCB) String() string { @@ -867,8 +867,8 @@ func (rr *SVCB) String() string { // areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their // copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function. func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool { - a = append([]SVCBKeyValue(nil), a...) - b = append([]SVCBKeyValue(nil), b...) + a = cloneSlice(a) + b = cloneSlice(b) sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() }) sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() }) for i, e := range a { diff --git a/vendor/github.com/miekg/dns/tools.go b/vendor/github.com/miekg/dns/tools.go index d11182536..ccf8f6bfc 100644 --- a/vendor/github.com/miekg/dns/tools.go +++ b/vendor/github.com/miekg/dns/tools.go @@ -1,3 +1,4 @@ +//go:build tools // +build tools // We include our tool dependencies for `go generate` here to ensure they're diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index d9becb67c..8e3129cbd 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -65,6 +65,7 @@ const ( TypeAPL uint16 = 42 TypeDS uint16 = 43 TypeSSHFP uint16 = 44 + TypeIPSECKEY uint16 = 45 TypeRRSIG uint16 = 46 TypeNSEC uint16 = 47 TypeDNSKEY uint16 = 48 @@ -98,6 +99,7 @@ const ( TypeURI uint16 = 256 TypeCAA uint16 = 257 TypeAVC uint16 = 258 + TypeAMTRELAY uint16 = 260 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 @@ -133,8 +135,8 @@ const ( RcodeNXRrset = 8 // NXRRSet - RR Set that should exist does not [DNS Update] RcodeNotAuth = 9 // NotAuth - Server Not Authoritative for zone [DNS Update] RcodeNotZone = 10 // NotZone - Name not contained in zone [DNS Update/TSIG] - RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG] - RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0] + RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3 + RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.3 RcodeBadKey = 17 // BADKEY - Key not recognized [TSIG] RcodeBadTime = 18 // BADTIME - Signature out of time window [TSIG] RcodeBadMode = 19 // BADMODE - Bad TKEY Mode [TKEY] @@ -159,6 +161,22 @@ const ( ZoneMDHashAlgSHA512 = 2 ) +// Used in IPSEC https://datatracker.ietf.org/doc/html/rfc4025#section-2.3 +const ( + IPSECGatewayNone uint8 = iota + IPSECGatewayIPv4 + IPSECGatewayIPv6 + IPSECGatewayHost +) + +// Used in AMTRELAY https://datatracker.ietf.org/doc/html/rfc8777#section-4.2.3 +const ( + AMTRELAYNone = IPSECGatewayNone + AMTRELAYIPv4 = IPSECGatewayIPv4 + AMTRELAYIPv6 = IPSECGatewayIPv6 + AMTRELAYHost = IPSECGatewayHost +) + // Header is the wire format for the DNS packet header. type Header struct { Id uint16 @@ -180,7 +198,7 @@ const ( _CD = 1 << 4 // checking disabled ) -// Various constants used in the LOC RR. See RFC 1887. +// Various constants used in the LOC RR. See RFC 1876. const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. @@ -218,6 +236,9 @@ var CertTypeToString = map[uint16]string{ CertOID: "OID", } +// Prefix for IPv4 encoded as IPv6 address +const ipv4InIPv6Prefix = "::ffff:" + //go:generate go run types_generate.go // Question holds a DNS question. Usually there is just one. While the @@ -381,6 +402,17 @@ func (rr *X25) String() string { return rr.Hdr.String() + rr.PSDNAddress } +// ISDN RR. See RFC 1183, Section 3.2. +type ISDN struct { + Hdr RR_Header + Address string + SubAddress string +} + +func (rr *ISDN) String() string { + return rr.Hdr.String() + sprintTxt([]string{rr.Address, rr.SubAddress}) +} + // RT RR. See RFC 1183, Section 3.3. type RT struct { Hdr RR_Header @@ -613,8 +645,8 @@ func nextByte(s string, offset int) (byte, int) { return 0, 0 case 2, 3: // too short to be \ddd default: // maybe \ddd - if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) { - return dddStringToByte(s[offset+1:]), 4 + if isDDD(s[offset+1:]) { + return dddToByte(s[offset+1:]), 4 } } // not \ddd, just an RFC 1035 "quoted" character @@ -733,6 +765,11 @@ func (rr *AAAA) String() string { if rr.AAAA == nil { return rr.Hdr.String() } + + if rr.AAAA.To4() != nil { + return rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String() + } + return rr.Hdr.String() + rr.AAAA.String() } @@ -760,7 +797,7 @@ func (rr *GPOS) String() string { return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude } -// LOC RR. See RFC RFC 1876. +// LOC RR. See RFC 1876. type LOC struct { Hdr RR_Header Version uint8 @@ -774,7 +811,10 @@ type LOC struct { // cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent // format and returns a string in m (two decimals for the cm). -func cmToM(m, e uint8) string { +func cmToM(x uint8) string { + m := x & 0xf0 >> 4 + e := x & 0x0f + if e < 2 { if e == 1 { m *= 10 @@ -830,10 +870,9 @@ func (rr *LOC) String() string { s += fmt.Sprintf("%.0fm ", alt) } - s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m " - s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m " - s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m" - + s += cmToM(rr.Size) + "m " + s += cmToM(rr.HorizPre) + "m " + s += cmToM(rr.VertPre) + "m" return s } @@ -870,6 +909,11 @@ func (rr *RRSIG) String() string { return s } +// NXT RR. See RFC 2535. +type NXT struct { + NSEC +} + // NSEC RR. See RFC 4034 and RFC 3755. type NSEC struct { Hdr RR_Header @@ -954,7 +998,7 @@ func (rr *TALINK) String() string { sprintName(rr.PreviousName) + " " + sprintName(rr.NextName) } -// SSHFP RR. See RFC RFC 4255. +// SSHFP RR. See RFC 4255. type SSHFP struct { Hdr RR_Header Algorithm uint8 @@ -968,7 +1012,7 @@ func (rr *SSHFP) String() string { " " + strings.ToUpper(rr.FingerPrint) } -// KEY RR. See RFC RFC 2535. +// KEY RR. See RFC 2535. type KEY struct { DNSKEY } @@ -994,6 +1038,69 @@ func (rr *DNSKEY) String() string { " " + rr.PublicKey } +// IPSECKEY RR. See RFC 4025. +type IPSECKEY struct { + Hdr RR_Header + Precedence uint8 + GatewayType uint8 + Algorithm uint8 + GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost + GatewayHost string `dns:"ipsechost"` + PublicKey string `dns:"base64"` +} + +func (rr *IPSECKEY) String() string { + var gateway string + switch rr.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + gateway = rr.GatewayAddr.String() + case IPSECGatewayHost: + gateway = rr.GatewayHost + case IPSECGatewayNone: + fallthrough + default: + gateway = "." + } + + return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + + " " + strconv.Itoa(int(rr.GatewayType)) + + " " + strconv.Itoa(int(rr.Algorithm)) + + " " + gateway + + " " + rr.PublicKey +} + +// AMTRELAY RR. See RFC 8777. +type AMTRELAY struct { + Hdr RR_Header + Precedence uint8 + GatewayType uint8 // discovery is packed in here at bit 0x80 + GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost + GatewayHost string `dns:"amtrelayhost"` +} + +func (rr *AMTRELAY) String() string { + var gateway string + switch rr.GatewayType & 0x7f { + case AMTRELAYIPv4, AMTRELAYIPv6: + gateway = rr.GatewayAddr.String() + case AMTRELAYHost: + gateway = rr.GatewayHost + case AMTRELAYNone: + fallthrough + default: + gateway = "." + } + boolS := "0" + if rr.GatewayType&0x80 == 0x80 { + boolS = "1" + } + + return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + + " " + boolS + + " " + strconv.Itoa(int(rr.GatewayType&0x7f)) + + " " + gateway +} + // RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template. type RKEY struct { Hdr RR_Header @@ -1215,7 +1322,7 @@ type NINFO struct { func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) } -// NID RR. See RFC RFC 6742. +// NID RR. See RFC 6742. type NID struct { Hdr RR_Header Preference uint16 @@ -1434,7 +1541,7 @@ func (a *APLPrefix) str() string { case net.IPv6len: // add prefix for IPv4-mapped IPv6 if v4 := a.Network.IP.To4(); v4 != nil { - sb.WriteString("::ffff:") + sb.WriteString(ipv4InIPv6Prefix) } sb.WriteString(a.Network.IP.String()) } @@ -1450,7 +1557,7 @@ func (a *APLPrefix) str() string { // equals reports whether two APL prefixes are identical. func (a *APLPrefix) equals(b *APLPrefix) bool { return a.Negation == b.Negation && - bytes.Equal(a.Network.IP, b.Network.IP) && + a.Network.IP.Equal(b.Network.IP) && bytes.Equal(a.Network.Mask, b.Network.Mask) } @@ -1518,21 +1625,19 @@ func euiToString(eui uint64, bits int) (hex string) { return } -// copyIP returns a copy of ip. -func copyIP(ip net.IP) net.IP { - p := make(net.IP, len(ip)) - copy(p, ip) - return p +// cloneSlice returns a shallow copy of s. +func cloneSlice[E any, S ~[]E](s S) S { + if s == nil { + return nil + } + return append(S(nil), s...) } // copyNet returns a copy of a subnet. func copyNet(n net.IPNet) net.IPNet { - m := make(net.IPMask, len(n.Mask)) - copy(m, n.Mask) - return net.IPNet{ - IP: copyIP(n.IP), - Mask: m, + IP: cloneSlice(n.IP), + Mask: cloneSlice(n.Mask), } } diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go index a4826ee2f..c018ad43d 100644 --- a/vendor/github.com/miekg/dns/udp.go +++ b/vendor/github.com/miekg/dns/udp.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package dns diff --git a/vendor/github.com/miekg/dns/udp_windows.go b/vendor/github.com/miekg/dns/udp_windows.go index e7dd8ca31..a259b67e4 100644 --- a/vendor/github.com/miekg/dns/udp_windows.go +++ b/vendor/github.com/miekg/dns/udp_windows.go @@ -1,5 +1,9 @@ +//go:build windows // +build windows +// TODO(tmthrgd): Remove this Windows-specific code if go.dev/issue/7175 and +// go.dev/issue/7174 are ever fixed. + package dns import "net" @@ -14,7 +18,6 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. -// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP. func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { n, raddr, err := conn.ReadFrom(b) if err != nil { @@ -24,12 +27,9 @@ func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { } // WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. -// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP. func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { return conn.WriteTo(b, session.raddr) } -// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods -// use the standard method in udp.go for these. func setUDPSocketOptions(*net.UDPConn) error { return nil } func parseDstFromOOB([]byte, net.IP) net.IP { return nil } diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index b1a872bd5..dc34e5902 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 50} +var Version = v{1, 1, 58} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index 1917e91c8..05b3c5add 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -44,7 +44,6 @@ func (t *Transfer) tsigProvider() TsigProvider { // dnscon := &dns.Conn{Conn:con} // transfer = &dns.Transfer{Conn: dnscon} // channel, err := transfer.In(message, master) -// func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: @@ -81,8 +80,13 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { first := true - defer t.Close() - defer close(c) + defer func() { + // First close the connection, then the channel. This allows functions blocked on + // the channel to assume that the connection is closed and no further operations are + // pending when they resume. + t.Close() + close(c) + }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout @@ -132,8 +136,13 @@ func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { axfr := true n := 0 qser := q.Ns[0].(*SOA).Serial - defer t.Close() - defer close(c) + defer func() { + // First close the connection, then the channel. This allows functions blocked on + // the channel to assume that the connection is closed and no further operations are + // pending when they resume. + t.Close() + close(c) + }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go index 9eb1dac29..03029fb3e 100644 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -43,6 +43,32 @@ func (r1 *AFSDB) isDuplicate(_r2 RR) bool { return true } +func (r1 *AMTRELAY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*AMTRELAY) + if !ok { + return false + } + _ = r2 + if r1.Precedence != r2.Precedence { + return false + } + if r1.GatewayType != r2.GatewayType { + return false + } + switch r1.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + if !r1.GatewayAddr.Equal(r2.GatewayAddr) { + return false + } + case IPSECGatewayHost: + if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { + return false + } + } + + return true +} + func (r1 *ANY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*ANY) if !ok { @@ -423,6 +449,53 @@ func (r1 *HTTPS) isDuplicate(_r2 RR) bool { return true } +func (r1 *IPSECKEY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*IPSECKEY) + if !ok { + return false + } + _ = r2 + if r1.Precedence != r2.Precedence { + return false + } + if r1.GatewayType != r2.GatewayType { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + switch r1.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + if !r1.GatewayAddr.Equal(r2.GatewayAddr) { + return false + } + case IPSECGatewayHost: + if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { + return false + } + } + + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + +func (r1 *ISDN) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*ISDN) + if !ok { + return false + } + _ = r2 + if r1.Address != r2.Address { + return false + } + if r1.SubAddress != r2.SubAddress { + return false + } + return true +} + func (r1 *KEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*KEY) if !ok { @@ -813,6 +886,26 @@ func (r1 *NULL) isDuplicate(_r2 RR) bool { return true } +func (r1 *NXT) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*NXT) + if !ok { + return false + } + _ = r2 + if !isDuplicateName(r1.NextDomain, r2.NextDomain) { + return false + } + if len(r1.TypeBitMap) != len(r2.TypeBitMap) { + return false + } + for i := 0; i < len(r1.TypeBitMap); i++ { + if r1.TypeBitMap[i] != r2.TypeBitMap[i] { + return false + } + } + return true +} + func (r1 *OPENPGPKEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*OPENPGPKEY) if !ok { diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index fc0822f98..39b3bc810 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -32,6 +32,22 @@ func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress return off, nil } +func (rr *AMTRELAY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packUint8(rr.Precedence, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.GatewayType, msg, off) + if err != nil { + return off, err + } + off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) + if err != nil { + return off, err + } + return off, nil +} + func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { return off, nil } @@ -332,6 +348,42 @@ func (rr *HTTPS) pack(msg []byte, off int, compression compressionMap, compress return off, nil } +func (rr *IPSECKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packUint8(rr.Precedence, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.GatewayType, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.Algorithm, msg, off) + if err != nil { + return off, err + } + off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) + if err != nil { + return off, err + } + off, err = packStringBase64(rr.PublicKey, msg, off) + if err != nil { + return off, err + } + return off, nil +} + +func (rr *ISDN) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packString(rr.Address, msg, off) + if err != nil { + return off, err + } + off, err = packString(rr.SubAddress, msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { @@ -654,6 +706,18 @@ func (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress b return off, nil } +func (rr *NXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packDomainName(rr.NextDomain, msg, off, compression, false) + if err != nil { + return off, err + } + off, err = packDataNsec(rr.TypeBitMap, msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { @@ -1180,6 +1244,34 @@ func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Precedence, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.GatewayType, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + if off == len(msg) { + return off, nil + } + rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) + if err != nil { + return off, err + } + return off, nil +} + func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart @@ -1636,6 +1728,66 @@ func (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Precedence, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.GatewayType, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.Algorithm, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + if off == len(msg) { + return off, nil + } + rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) + if err != nil { + return off, err + } + return off, nil +} + +func (rr *ISDN) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Address, off, err = unpackString(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.SubAddress, off, err = unpackString(msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart @@ -2114,6 +2266,24 @@ func (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *NXT) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.NextDomain, off, err = UnpackDomainName(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.TypeBitMap, off, err = unpackDataNsec(msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 5d060cfee..2c70fc44d 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -12,6 +12,7 @@ var TypeToRR = map[uint16]func() RR{ TypeA: func() RR { return new(A) }, TypeAAAA: func() RR { return new(AAAA) }, TypeAFSDB: func() RR { return new(AFSDB) }, + TypeAMTRELAY: func() RR { return new(AMTRELAY) }, TypeANY: func() RR { return new(ANY) }, TypeAPL: func() RR { return new(APL) }, TypeAVC: func() RR { return new(AVC) }, @@ -34,6 +35,8 @@ var TypeToRR = map[uint16]func() RR{ TypeHINFO: func() RR { return new(HINFO) }, TypeHIP: func() RR { return new(HIP) }, TypeHTTPS: func() RR { return new(HTTPS) }, + TypeIPSECKEY: func() RR { return new(IPSECKEY) }, + TypeISDN: func() RR { return new(ISDN) }, TypeKEY: func() RR { return new(KEY) }, TypeKX: func() RR { return new(KX) }, TypeL32: func() RR { return new(L32) }, @@ -57,6 +60,7 @@ var TypeToRR = map[uint16]func() RR{ TypeNSEC3: func() RR { return new(NSEC3) }, TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, TypeNULL: func() RR { return new(NULL) }, + TypeNXT: func() RR { return new(NXT) }, TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) }, TypeOPT: func() RR { return new(OPT) }, TypePTR: func() RR { return new(PTR) }, @@ -90,6 +94,7 @@ var TypeToString = map[uint16]string{ TypeA: "A", TypeAAAA: "AAAA", TypeAFSDB: "AFSDB", + TypeAMTRELAY: "AMTRELAY", TypeANY: "ANY", TypeAPL: "APL", TypeATMA: "ATMA", @@ -114,6 +119,7 @@ var TypeToString = map[uint16]string{ TypeHINFO: "HINFO", TypeHIP: "HIP", TypeHTTPS: "HTTPS", + TypeIPSECKEY: "IPSECKEY", TypeISDN: "ISDN", TypeIXFR: "IXFR", TypeKEY: "KEY", @@ -176,6 +182,7 @@ var TypeToString = map[uint16]string{ func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } +func (rr *AMTRELAY) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) Header() *RR_Header { return &rr.Hdr } func (rr *APL) Header() *RR_Header { return &rr.Hdr } func (rr *AVC) Header() *RR_Header { return &rr.Hdr } @@ -198,6 +205,8 @@ func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } func (rr *HIP) Header() *RR_Header { return &rr.Hdr } func (rr *HTTPS) Header() *RR_Header { return &rr.Hdr } +func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *ISDN) Header() *RR_Header { return &rr.Hdr } func (rr *KEY) Header() *RR_Header { return &rr.Hdr } func (rr *KX) Header() *RR_Header { return &rr.Hdr } func (rr *L32) Header() *RR_Header { return &rr.Hdr } @@ -221,6 +230,7 @@ func (rr *NSEC) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } func (rr *NULL) Header() *RR_Header { return &rr.Hdr } +func (rr *NXT) Header() *RR_Header { return &rr.Hdr } func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr } func (rr *OPT) Header() *RR_Header { return &rr.Hdr } func (rr *PTR) Header() *RR_Header { return &rr.Hdr } @@ -257,6 +267,7 @@ func (rr *A) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AAAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) if len(rr.AAAA) != 0 { @@ -264,16 +275,34 @@ func (rr *AAAA) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AFSDB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Subtype l += domainNameLen(rr.Hostname, off+l, compression, false) return l } + +func (rr *AMTRELAY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l++ // Precedence + l++ // GatewayType + switch rr.GatewayType { + case AMTRELAYIPv4: + l += net.IPv4len + case AMTRELAYIPv6: + l += net.IPv6len + case AMTRELAYHost: + l += len(rr.GatewayHost) + 1 + } + return l +} + func (rr *ANY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) return l } + func (rr *APL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Prefixes { @@ -281,6 +310,7 @@ func (rr *APL) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AVC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -288,6 +318,7 @@ func (rr *AVC) len(off int, compression map[string]struct{}) int { } return l } + func (rr *CAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Flag @@ -295,6 +326,7 @@ func (rr *CAA) len(off int, compression map[string]struct{}) int { l += len(rr.Value) return l } + func (rr *CERT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Type @@ -303,21 +335,25 @@ func (rr *CERT) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) return l } + func (rr *CNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, true) return l } + func (rr *DHCID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.Digest)) return l } + func (rr *DNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, false) return l } + func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags @@ -326,6 +362,7 @@ func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *DS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag @@ -334,26 +371,31 @@ func (rr *DS) len(off int, compression map[string]struct{}) int { l += len(rr.Digest) / 2 return l } + func (rr *EID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Endpoint) / 2 return l } + func (rr *EUI48) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 6 // Address return l } + func (rr *EUI64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 8 // Address return l } + func (rr *GID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Gid return l } + func (rr *GPOS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Longitude) + 1 @@ -361,12 +403,14 @@ func (rr *GPOS) len(off int, compression map[string]struct{}) int { l += len(rr.Altitude) + 1 return l } + func (rr *HINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Cpu) + 1 l += len(rr.Os) + 1 return l } + func (rr *HIP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // HitLength @@ -379,12 +423,38 @@ func (rr *HIP) len(off int, compression map[string]struct{}) int { } return l } + +func (rr *IPSECKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l++ // Precedence + l++ // GatewayType + l++ // Algorithm + switch rr.GatewayType { + case IPSECGatewayIPv4: + l += net.IPv4len + case IPSECGatewayIPv6: + l += net.IPv6len + case IPSECGatewayHost: + l += len(rr.GatewayHost) + 1 + } + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + return l +} + +func (rr *ISDN) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += len(rr.Address) + 1 + l += len(rr.SubAddress) + 1 + return l +} + func (rr *KX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Exchanger, off+l, compression, false) return l } + func (rr *L32) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference @@ -393,12 +463,14 @@ func (rr *L32) len(off int, compression map[string]struct{}) int { } return l } + func (rr *L64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // Locator64 return l } + func (rr *LOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Version @@ -410,49 +482,58 @@ func (rr *LOC) len(off int, compression map[string]struct{}) int { l += 4 // Altitude return l } + func (rr *LP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Fqdn, off+l, compression, false) return l } + func (rr *MB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mb, off+l, compression, true) return l } + func (rr *MD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Md, off+l, compression, true) return l } + func (rr *MF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mf, off+l, compression, true) return l } + func (rr *MG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mg, off+l, compression, true) return l } + func (rr *MINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Rmail, off+l, compression, true) l += domainNameLen(rr.Email, off+l, compression, true) return l } + func (rr *MR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mr, off+l, compression, true) return l } + func (rr *MX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Mx, off+l, compression, true) return l } + func (rr *NAPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Order @@ -463,17 +544,20 @@ func (rr *NAPTR) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Replacement, off+l, compression, false) return l } + func (rr *NID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // NodeID return l } + func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Locator) / 2 return l } + func (rr *NINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.ZSData { @@ -481,16 +565,19 @@ func (rr *NINFO) len(off int, compression map[string]struct{}) int { } return l } + func (rr *NS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) return l } + func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, false) return l } + func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Hash @@ -500,21 +587,25 @@ func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { l += len(rr.Salt) / 2 return l } + func (rr *NULL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Data) return l } + func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *PTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, true) return l } + func (rr *PX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference @@ -522,11 +613,13 @@ func (rr *PX) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Mapx400, off+l, compression, false) return l } + func (rr *RFC3597) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Rdata) / 2 return l } + func (rr *RKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags @@ -535,12 +628,14 @@ func (rr *RKEY) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *RP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mbox, off+l, compression, false) l += domainNameLen(rr.Txt, off+l, compression, false) return l } + func (rr *RRSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // TypeCovered @@ -554,12 +649,14 @@ func (rr *RRSIG) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.Signature)) return l } + func (rr *RT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Host, off+l, compression, false) return l } + func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage @@ -568,6 +665,7 @@ func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { l += len(rr.Certificate) / 2 return l } + func (rr *SOA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) @@ -579,6 +677,7 @@ func (rr *SOA) len(off int, compression map[string]struct{}) int { l += 4 // Minttl return l } + func (rr *SPF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -586,6 +685,7 @@ func (rr *SPF) len(off int, compression map[string]struct{}) int { } return l } + func (rr *SRV) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -594,6 +694,7 @@ func (rr *SRV) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Target, off+l, compression, false) return l } + func (rr *SSHFP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Algorithm @@ -601,6 +702,7 @@ func (rr *SSHFP) len(off int, compression map[string]struct{}) int { l += len(rr.FingerPrint) / 2 return l } + func (rr *SVCB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -610,6 +712,7 @@ func (rr *SVCB) len(off int, compression map[string]struct{}) int { } return l } + func (rr *TA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag @@ -618,12 +721,14 @@ func (rr *TA) len(off int, compression map[string]struct{}) int { l += len(rr.Digest) / 2 return l } + func (rr *TALINK) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.PreviousName, off+l, compression, false) l += domainNameLen(rr.NextName, off+l, compression, false) return l } + func (rr *TKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) @@ -637,6 +742,7 @@ func (rr *TKEY) len(off int, compression map[string]struct{}) int { l += len(rr.OtherData) / 2 return l } + func (rr *TLSA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage @@ -645,6 +751,7 @@ func (rr *TLSA) len(off int, compression map[string]struct{}) int { l += len(rr.Certificate) / 2 return l } + func (rr *TSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) @@ -658,6 +765,7 @@ func (rr *TSIG) len(off int, compression map[string]struct{}) int { l += len(rr.OtherData) / 2 return l } + func (rr *TXT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -665,16 +773,19 @@ func (rr *TXT) len(off int, compression map[string]struct{}) int { } return l } + func (rr *UID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Uid return l } + func (rr *UINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Uinfo) + 1 return l } + func (rr *URI) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -682,11 +793,13 @@ func (rr *URI) len(off int, compression map[string]struct{}) int { l += len(rr.Target) return l } + func (rr *X25) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.PSDNAddress) + 1 return l } + func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Serial @@ -698,17 +811,31 @@ func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { // copy() functions func (rr *A) copy() RR { - return &A{rr.Hdr, copyIP(rr.A)} + return &A{rr.Hdr, cloneSlice(rr.A)} } + func (rr *AAAA) copy() RR { - return &AAAA{rr.Hdr, copyIP(rr.AAAA)} + return &AAAA{rr.Hdr, cloneSlice(rr.AAAA)} } + func (rr *AFSDB) copy() RR { return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname} } + +func (rr *AMTRELAY) copy() RR { + return &AMTRELAY{ + rr.Hdr, + rr.Precedence, + rr.GatewayType, + cloneSlice(rr.GatewayAddr), + rr.GatewayHost, + } +} + func (rr *ANY) copy() RR { return &ANY{rr.Hdr} } + func (rr *APL) copy() RR { Prefixes := make([]APLPrefix, len(rr.Prefixes)) for i, e := range rr.Prefixes { @@ -716,150 +843,278 @@ func (rr *APL) copy() RR { } return &APL{rr.Hdr, Prefixes} } + func (rr *AVC) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &AVC{rr.Hdr, Txt} + return &AVC{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *CAA) copy() RR { - return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} + return &CAA{ + rr.Hdr, + rr.Flag, + rr.Tag, + rr.Value, + } } + func (rr *CDNSKEY) copy() RR { return &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)} } + func (rr *CDS) copy() RR { return &CDS{*rr.DS.copy().(*DS)} } + func (rr *CERT) copy() RR { - return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} + return &CERT{ + rr.Hdr, + rr.Type, + rr.KeyTag, + rr.Algorithm, + rr.Certificate, + } } + func (rr *CNAME) copy() RR { return &CNAME{rr.Hdr, rr.Target} } + func (rr *CSYNC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap} + return &CSYNC{ + rr.Hdr, + rr.Serial, + rr.Flags, + cloneSlice(rr.TypeBitMap), + } } + func (rr *DHCID) copy() RR { return &DHCID{rr.Hdr, rr.Digest} } + func (rr *DLV) copy() RR { return &DLV{*rr.DS.copy().(*DS)} } + func (rr *DNAME) copy() RR { return &DNAME{rr.Hdr, rr.Target} } + func (rr *DNSKEY) copy() RR { - return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &DNSKEY{ + rr.Hdr, + rr.Flags, + rr.Protocol, + rr.Algorithm, + rr.PublicKey, + } } + func (rr *DS) copy() RR { - return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} + return &DS{ + rr.Hdr, + rr.KeyTag, + rr.Algorithm, + rr.DigestType, + rr.Digest, + } } + func (rr *EID) copy() RR { return &EID{rr.Hdr, rr.Endpoint} } + func (rr *EUI48) copy() RR { return &EUI48{rr.Hdr, rr.Address} } + func (rr *EUI64) copy() RR { return &EUI64{rr.Hdr, rr.Address} } + func (rr *GID) copy() RR { return &GID{rr.Hdr, rr.Gid} } + func (rr *GPOS) copy() RR { - return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude} + return &GPOS{ + rr.Hdr, + rr.Longitude, + rr.Latitude, + rr.Altitude, + } } + func (rr *HINFO) copy() RR { return &HINFO{rr.Hdr, rr.Cpu, rr.Os} } + func (rr *HIP) copy() RR { - RendezvousServers := make([]string, len(rr.RendezvousServers)) - copy(RendezvousServers, rr.RendezvousServers) - return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers} + return &HIP{ + rr.Hdr, + rr.HitLength, + rr.PublicKeyAlgorithm, + rr.PublicKeyLength, + rr.Hit, + rr.PublicKey, + cloneSlice(rr.RendezvousServers), + } } + func (rr *HTTPS) copy() RR { return &HTTPS{*rr.SVCB.copy().(*SVCB)} } + +func (rr *IPSECKEY) copy() RR { + return &IPSECKEY{ + rr.Hdr, + rr.Precedence, + rr.GatewayType, + rr.Algorithm, + cloneSlice(rr.GatewayAddr), + rr.GatewayHost, + rr.PublicKey, + } +} + +func (rr *ISDN) copy() RR { + return &ISDN{rr.Hdr, rr.Address, rr.SubAddress} +} + func (rr *KEY) copy() RR { return &KEY{*rr.DNSKEY.copy().(*DNSKEY)} } + func (rr *KX) copy() RR { return &KX{rr.Hdr, rr.Preference, rr.Exchanger} } + func (rr *L32) copy() RR { - return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)} + return &L32{rr.Hdr, rr.Preference, cloneSlice(rr.Locator32)} } + func (rr *L64) copy() RR { return &L64{rr.Hdr, rr.Preference, rr.Locator64} } + func (rr *LOC) copy() RR { - return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} + return &LOC{ + rr.Hdr, + rr.Version, + rr.Size, + rr.HorizPre, + rr.VertPre, + rr.Latitude, + rr.Longitude, + rr.Altitude, + } } + func (rr *LP) copy() RR { return &LP{rr.Hdr, rr.Preference, rr.Fqdn} } + func (rr *MB) copy() RR { return &MB{rr.Hdr, rr.Mb} } + func (rr *MD) copy() RR { return &MD{rr.Hdr, rr.Md} } + func (rr *MF) copy() RR { return &MF{rr.Hdr, rr.Mf} } + func (rr *MG) copy() RR { return &MG{rr.Hdr, rr.Mg} } + func (rr *MINFO) copy() RR { return &MINFO{rr.Hdr, rr.Rmail, rr.Email} } + func (rr *MR) copy() RR { return &MR{rr.Hdr, rr.Mr} } + func (rr *MX) copy() RR { return &MX{rr.Hdr, rr.Preference, rr.Mx} } + func (rr *NAPTR) copy() RR { - return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement} + return &NAPTR{ + rr.Hdr, + rr.Order, + rr.Preference, + rr.Flags, + rr.Service, + rr.Regexp, + rr.Replacement, + } } + func (rr *NID) copy() RR { return &NID{rr.Hdr, rr.Preference, rr.NodeID} } + func (rr *NIMLOC) copy() RR { return &NIMLOC{rr.Hdr, rr.Locator} } + func (rr *NINFO) copy() RR { - ZSData := make([]string, len(rr.ZSData)) - copy(ZSData, rr.ZSData) - return &NINFO{rr.Hdr, ZSData} + return &NINFO{rr.Hdr, cloneSlice(rr.ZSData)} } + func (rr *NS) copy() RR { return &NS{rr.Hdr, rr.Ns} } + func (rr *NSAPPTR) copy() RR { return &NSAPPTR{rr.Hdr, rr.Ptr} } + func (rr *NSEC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap} + return &NSEC{rr.Hdr, rr.NextDomain, cloneSlice(rr.TypeBitMap)} } + func (rr *NSEC3) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC3{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap} + return &NSEC3{ + rr.Hdr, + rr.Hash, + rr.Flags, + rr.Iterations, + rr.SaltLength, + rr.Salt, + rr.HashLength, + rr.NextDomain, + cloneSlice(rr.TypeBitMap), + } } + func (rr *NSEC3PARAM) copy() RR { - return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} + return &NSEC3PARAM{ + rr.Hdr, + rr.Hash, + rr.Flags, + rr.Iterations, + rr.SaltLength, + rr.Salt, + } } + func (rr *NULL) copy() RR { return &NULL{rr.Hdr, rr.Data} } + +func (rr *NXT) copy() RR { + return &NXT{*rr.NSEC.copy().(*NSEC)} +} + func (rr *OPENPGPKEY) copy() RR { return &OPENPGPKEY{rr.Hdr, rr.PublicKey} } + func (rr *OPT) copy() RR { Option := make([]EDNS0, len(rr.Option)) for i, e := range rr.Option { @@ -867,86 +1122,205 @@ func (rr *OPT) copy() RR { } return &OPT{rr.Hdr, Option} } + func (rr *PTR) copy() RR { return &PTR{rr.Hdr, rr.Ptr} } + func (rr *PX) copy() RR { - return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400} + return &PX{ + rr.Hdr, + rr.Preference, + rr.Map822, + rr.Mapx400, + } } + func (rr *RFC3597) copy() RR { return &RFC3597{rr.Hdr, rr.Rdata} } + func (rr *RKEY) copy() RR { - return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &RKEY{ + rr.Hdr, + rr.Flags, + rr.Protocol, + rr.Algorithm, + rr.PublicKey, + } } + func (rr *RP) copy() RR { return &RP{rr.Hdr, rr.Mbox, rr.Txt} } + func (rr *RRSIG) copy() RR { - return &RRSIG{rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature} + return &RRSIG{ + rr.Hdr, + rr.TypeCovered, + rr.Algorithm, + rr.Labels, + rr.OrigTtl, + rr.Expiration, + rr.Inception, + rr.KeyTag, + rr.SignerName, + rr.Signature, + } } + func (rr *RT) copy() RR { return &RT{rr.Hdr, rr.Preference, rr.Host} } + func (rr *SIG) copy() RR { return &SIG{*rr.RRSIG.copy().(*RRSIG)} } + func (rr *SMIMEA) copy() RR { - return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} + return &SMIMEA{ + rr.Hdr, + rr.Usage, + rr.Selector, + rr.MatchingType, + rr.Certificate, + } } + func (rr *SOA) copy() RR { - return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl} + return &SOA{ + rr.Hdr, + rr.Ns, + rr.Mbox, + rr.Serial, + rr.Refresh, + rr.Retry, + rr.Expire, + rr.Minttl, + } } + func (rr *SPF) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &SPF{rr.Hdr, Txt} + return &SPF{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *SRV) copy() RR { - return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target} + return &SRV{ + rr.Hdr, + rr.Priority, + rr.Weight, + rr.Port, + rr.Target, + } } + func (rr *SSHFP) copy() RR { - return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint} + return &SSHFP{ + rr.Hdr, + rr.Algorithm, + rr.Type, + rr.FingerPrint, + } } + func (rr *SVCB) copy() RR { Value := make([]SVCBKeyValue, len(rr.Value)) for i, e := range rr.Value { Value[i] = e.copy() } - return &SVCB{rr.Hdr, rr.Priority, rr.Target, Value} + return &SVCB{ + rr.Hdr, + rr.Priority, + rr.Target, + Value, + } } + func (rr *TA) copy() RR { - return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} + return &TA{ + rr.Hdr, + rr.KeyTag, + rr.Algorithm, + rr.DigestType, + rr.Digest, + } } + func (rr *TALINK) copy() RR { return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName} } + func (rr *TKEY) copy() RR { - return &TKEY{rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData} + return &TKEY{ + rr.Hdr, + rr.Algorithm, + rr.Inception, + rr.Expiration, + rr.Mode, + rr.Error, + rr.KeySize, + rr.Key, + rr.OtherLen, + rr.OtherData, + } } + func (rr *TLSA) copy() RR { - return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} + return &TLSA{ + rr.Hdr, + rr.Usage, + rr.Selector, + rr.MatchingType, + rr.Certificate, + } } + func (rr *TSIG) copy() RR { - return &TSIG{rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData} + return &TSIG{ + rr.Hdr, + rr.Algorithm, + rr.TimeSigned, + rr.Fudge, + rr.MACSize, + rr.MAC, + rr.OrigId, + rr.Error, + rr.OtherLen, + rr.OtherData, + } } + func (rr *TXT) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &TXT{rr.Hdr, Txt} + return &TXT{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *UID) copy() RR { return &UID{rr.Hdr, rr.Uid} } + func (rr *UINFO) copy() RR { return &UINFO{rr.Hdr, rr.Uinfo} } + func (rr *URI) copy() RR { - return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target} + return &URI{ + rr.Hdr, + rr.Priority, + rr.Weight, + rr.Target, + } } + func (rr *X25) copy() RR { return &X25{rr.Hdr, rr.PSDNAddress} } + func (rr *ZONEMD) copy() RR { - return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest} + return &ZONEMD{ + rr.Hdr, + rr.Serial, + rr.Scheme, + rr.Hash, + rr.Digest, + } } diff --git a/vendor/github.com/onsi/ginkgo/v2/LICENSE b/vendor/github.com/onsi/ginkgo/v2/LICENSE new file mode 100644 index 000000000..9415ee72c --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013-2014 Onsi Fakhouri + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go b/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go new file mode 100644 index 000000000..a61021d08 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go @@ -0,0 +1,69 @@ +package config + +// GinkgoConfigType has been deprecated and its equivalent now lives in +// the types package. You can no longer access Ginkgo configuration from the config +// package. Instead use the DSL's GinkgoConfiguration() function to get copies of the +// current configuration +// +// GinkgoConfigType is still here so custom V1 reporters do not result in a compilation error +// It will be removed in a future minor release of Ginkgo +type GinkgoConfigType = DeprecatedGinkgoConfigType +type DeprecatedGinkgoConfigType struct { + RandomSeed int64 + RandomizeAllSpecs bool + RegexScansFilePath bool + FocusStrings []string + SkipStrings []string + SkipMeasurements bool + FailOnPending bool + FailFast bool + FlakeAttempts int + EmitSpecProgress bool + DryRun bool + DebugParallel bool + + ParallelNode int + ParallelTotal int + SyncHost string + StreamHost string +} + +// DefaultReporterConfigType has been deprecated and its equivalent now lives in +// the types package. You can no longer access Ginkgo configuration from the config +// package. Instead use the DSL's GinkgoConfiguration() function to get copies of the +// current configuration +// +// DefaultReporterConfigType is still here so custom V1 reporters do not result in a compilation error +// It will be removed in a future minor release of Ginkgo +type DefaultReporterConfigType = DeprecatedDefaultReporterConfigType +type DeprecatedDefaultReporterConfigType struct { + NoColor bool + SlowSpecThreshold float64 + NoisyPendings bool + NoisySkippings bool + Succinct bool + Verbose bool + FullTrace bool + ReportPassed bool + ReportFile string +} + +// Sadly there is no way to gracefully deprecate access to these global config variables. +// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method +// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails +type GinkgoConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead struct{} + +// Sadly there is no way to gracefully deprecate access to these global config variables. +// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method +// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails +var GinkgoConfig = GinkgoConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead{} + +// Sadly there is no way to gracefully deprecate access to these global config variables. +// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method +// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails +type DefaultReporterConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead struct{} + +// Sadly there is no way to gracefully deprecate access to these global config variables. +// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method +// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails +var DefaultReporterConfig = DefaultReporterConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead{} diff --git a/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go b/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go new file mode 100644 index 000000000..778bfd7c7 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go @@ -0,0 +1,41 @@ +// +build !windows + +/* +These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com + + * go-colorable: + * go-isatty: + +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package formatter + +import ( + "io" + "os" +) + +func newColorable(file *os.File) io.Writer { + return file +} diff --git a/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go b/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go new file mode 100644 index 000000000..dd1d143cc --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go @@ -0,0 +1,809 @@ +/* +These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com + + * go-colorable: + * go-isatty: + +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package formatter + +import ( + "bytes" + "fmt" + "io" + "math" + "os" + "strconv" + "strings" + "syscall" + "unsafe" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +func isTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type writer struct { + out io.Writer + handle syscall.Handle + lastbuf bytes.Buffer + oldattr word +} + +func newColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isTerminal(file.Fd()) { + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &writer{out: file, handle: handle, oldattr: csbi.attributes} + } else { + return file + } +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +func (w *writer) Write(data []byte) (n int, err error) { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + er := bytes.NewBuffer(data) +loop: + for { + r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + break loop + } + + c1, _, err := er.ReadRune() + if err != nil { + break loop + } + if c1 != 0x1b { + fmt.Fprint(w.out, string(c1)) + continue + } + c2, _, err := er.ReadRune() + if err != nil { + w.lastbuf.WriteRune(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteRune(c1) + w.lastbuf.WriteRune(c2) + continue + } + + var buf bytes.Buffer + var m rune + for { + c, _, err := er.ReadRune() + if err != nil { + w.lastbuf.WriteRune(c1) + w.lastbuf.WriteRune(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + break + } + buf.Write([]byte(string(c))) + } + + var csbi consoleScreenBufferInfo + switch m { + case 'A': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n, err = strconv.Atoi(buf.String()); err == nil { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + } + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H': + token := strings.Split(buf.String(), ";") + if len(token) != 2 { + continue + } + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2) + csbi.cursorPosition.x = short(n1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i += 1 { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case n == 7: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 22 == n || n == 25 || n == 25: + attr |= foregroundIntensity + case n == 27: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 30 <= n && n <= 37: + attr = (attr & backgroundMask) + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr = (attr & foregroundMask) + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + } + } + } + } + return len(data) - w.lastbuf.Len(), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + consoleColor{0x000000, false, false, false, false}, + consoleColor{0x000080, false, false, true, false}, + consoleColor{0x008000, false, true, false, false}, + consoleColor{0x008080, false, true, true, false}, + consoleColor{0x800000, true, false, false, false}, + consoleColor{0x800080, true, false, true, false}, + consoleColor{0x808000, true, true, false, false}, + consoleColor{0xc0c0c0, true, true, true, false}, + consoleColor{0x808080, false, false, false, true}, + consoleColor{0x0000ff, false, false, true, true}, + consoleColor{0x00ff00, false, true, false, true}, + consoleColor{0x00ffff, false, true, true, true}, + consoleColor{0xff0000, true, false, false, true}, + consoleColor{0xff00ff, true, false, true, true}, + consoleColor{0xffff00, true, true, false, true}, + consoleColor{0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go new file mode 100644 index 000000000..743555dde --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go @@ -0,0 +1,230 @@ +package formatter + +import ( + "fmt" + "os" + "regexp" + "strconv" + "strings" +) + +// ColorableStdOut and ColorableStdErr enable color output support on Windows +var ColorableStdOut = newColorable(os.Stdout) +var ColorableStdErr = newColorable(os.Stderr) + +const COLS = 80 + +type ColorMode uint8 + +const ( + ColorModeNone ColorMode = iota + ColorModeTerminal + ColorModePassthrough +) + +var SingletonFormatter = New(ColorModeTerminal) + +func F(format string, args ...interface{}) string { + return SingletonFormatter.F(format, args...) +} + +func Fi(indentation uint, format string, args ...interface{}) string { + return SingletonFormatter.Fi(indentation, format, args...) +} + +func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { + return SingletonFormatter.Fiw(indentation, maxWidth, format, args...) +} + +type Formatter struct { + ColorMode ColorMode + colors map[string]string + styleRe *regexp.Regexp + preserveColorStylingTags bool +} + +func NewWithNoColorBool(noColor bool) Formatter { + if noColor { + return New(ColorModeNone) + } + return New(ColorModeTerminal) +} + +func New(colorMode ColorMode) Formatter { + colorAliases := map[string]int{ + "black": 0, + "red": 1, + "green": 2, + "yellow": 3, + "blue": 4, + "magenta": 5, + "cyan": 6, + "white": 7, + } + for colorAlias, n := range colorAliases { + colorAliases[fmt.Sprintf("bright-%s", colorAlias)] = n + 8 + } + + getColor := func(color, defaultEscapeCode string) string { + color = strings.ToUpper(strings.ReplaceAll(color, "-", "_")) + envVar := fmt.Sprintf("GINKGO_CLI_COLOR_%s", color) + envVarColor := os.Getenv(envVar) + if envVarColor == "" { + return defaultEscapeCode + } + if colorCode, ok := colorAliases[envVarColor]; ok { + return fmt.Sprintf("\x1b[38;5;%dm", colorCode) + } + colorCode, err := strconv.Atoi(envVarColor) + if err != nil || colorCode < 0 || colorCode > 255 { + return defaultEscapeCode + } + return fmt.Sprintf("\x1b[38;5;%dm", colorCode) + } + + f := Formatter{ + ColorMode: colorMode, + colors: map[string]string{ + "/": "\x1b[0m", + "bold": "\x1b[1m", + "underline": "\x1b[4m", + + "red": getColor("red", "\x1b[38;5;9m"), + "orange": getColor("orange", "\x1b[38;5;214m"), + "coral": getColor("coral", "\x1b[38;5;204m"), + "magenta": getColor("magenta", "\x1b[38;5;13m"), + "green": getColor("green", "\x1b[38;5;10m"), + "dark-green": getColor("dark-green", "\x1b[38;5;28m"), + "yellow": getColor("yellow", "\x1b[38;5;11m"), + "light-yellow": getColor("light-yellow", "\x1b[38;5;228m"), + "cyan": getColor("cyan", "\x1b[38;5;14m"), + "gray": getColor("gray", "\x1b[38;5;243m"), + "light-gray": getColor("light-gray", "\x1b[38;5;246m"), + "blue": getColor("blue", "\x1b[38;5;12m"), + }, + } + colors := []string{} + for color := range f.colors { + colors = append(colors, color) + } + f.styleRe = regexp.MustCompile("{{(" + strings.Join(colors, "|") + ")}}") + return f +} + +func (f Formatter) F(format string, args ...interface{}) string { + return f.Fi(0, format, args...) +} + +func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string { + return f.Fiw(indentation, 0, format, args...) +} + +func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { + out := f.style(format) + if len(args) > 0 { + out = fmt.Sprintf(out, args...) + } + + if indentation == 0 && maxWidth == 0 { + return out + } + + lines := strings.Split(out, "\n") + + if maxWidth != 0 { + outLines := []string{} + + maxWidth = maxWidth - indentation*2 + for _, line := range lines { + if f.length(line) <= maxWidth { + outLines = append(outLines, line) + continue + } + words := strings.Split(line, " ") + outWords := []string{words[0]} + length := uint(f.length(words[0])) + for _, word := range words[1:] { + wordLength := f.length(word) + if length+wordLength+1 <= maxWidth { + length += wordLength + 1 + outWords = append(outWords, word) + continue + } + outLines = append(outLines, strings.Join(outWords, " ")) + outWords = []string{word} + length = wordLength + } + if len(outWords) > 0 { + outLines = append(outLines, strings.Join(outWords, " ")) + } + } + + lines = outLines + } + + if indentation == 0 { + return strings.Join(lines, "\n") + } + + padding := strings.Repeat(" ", int(indentation)) + for i := range lines { + if lines[i] != "" { + lines[i] = padding + lines[i] + } + } + + return strings.Join(lines, "\n") +} + +func (f Formatter) length(styled string) uint { + n := uint(0) + inStyle := false + for _, b := range styled { + if inStyle { + if b == 'm' { + inStyle = false + } + continue + } + if b == '\x1b' { + inStyle = true + continue + } + n += 1 + } + return n +} + +func (f Formatter) CycleJoin(elements []string, joiner string, cycle []string) string { + if len(elements) == 0 { + return "" + } + n := len(cycle) + out := "" + for i, text := range elements { + out += cycle[i%n] + text + if i < len(elements)-1 { + out += joiner + } + } + out += "{{/}}" + return f.style(out) +} + +func (f Formatter) style(s string) string { + switch f.ColorMode { + case ColorModeNone: + return f.styleRe.ReplaceAllString(s, "") + case ColorModePassthrough: + return s + case ColorModeTerminal: + return f.styleRe.ReplaceAllStringFunc(s, func(match string) string { + if out, ok := f.colors[strings.Trim(match, "{}")]; ok { + return out + } + return match + }) + } + + return "" +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go new file mode 100644 index 000000000..5db5d1a7b --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go @@ -0,0 +1,63 @@ +package build + +import ( + "fmt" + + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/types" +) + +func BuildBuildCommand() command.Command { + var cliConfig = types.NewDefaultCLIConfig() + var goFlagsConfig = types.NewDefaultGoFlagsConfig() + + flags, err := types.BuildBuildCommandFlagSet(&cliConfig, &goFlagsConfig) + if err != nil { + panic(err) + } + + return command.Command{ + Name: "build", + Flags: flags, + Usage: "ginkgo build ", + ShortDoc: "Build the passed in (or the package in the current directory if left blank).", + DocLink: "precompiling-suites", + Command: func(args []string, _ []string) { + var errors []error + cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) + command.AbortIfErrors("Ginkgo detected configuration issues:", errors) + + buildSpecs(args, cliConfig, goFlagsConfig) + }, + } +} + +func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) { + suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) + if len(suites) == 0 { + command.AbortWith("Found no test suites") + } + + internal.VerifyCLIAndFrameworkVersion(suites) + + opc := internal.NewOrderedParallelCompiler(cliConfig.ComputedNumCompilers()) + opc.StartCompiling(suites, goFlagsConfig) + + for { + suiteIdx, suite := opc.Next() + if suiteIdx >= len(suites) { + break + } + suites[suiteIdx] = suite + if suite.State.Is(internal.TestSuiteStateFailedToCompile) { + fmt.Println(suite.CompilationError.Error()) + } else { + fmt.Printf("Compiled %s.test\n", suite.PackageName) + } + } + + if suites.CountWithState(internal.TestSuiteStateFailedToCompile) > 0 { + command.AbortWith("Failed to compile all tests") + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go new file mode 100644 index 000000000..2efd28608 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go @@ -0,0 +1,61 @@ +package command + +import "fmt" + +type AbortDetails struct { + ExitCode int + Error error + EmitUsage bool +} + +func Abort(details AbortDetails) { + panic(details) +} + +func AbortGracefullyWith(format string, args ...interface{}) { + Abort(AbortDetails{ + ExitCode: 0, + Error: fmt.Errorf(format, args...), + EmitUsage: false, + }) +} + +func AbortWith(format string, args ...interface{}) { + Abort(AbortDetails{ + ExitCode: 1, + Error: fmt.Errorf(format, args...), + EmitUsage: false, + }) +} + +func AbortWithUsage(format string, args ...interface{}) { + Abort(AbortDetails{ + ExitCode: 1, + Error: fmt.Errorf(format, args...), + EmitUsage: true, + }) +} + +func AbortIfError(preamble string, err error) { + if err != nil { + Abort(AbortDetails{ + ExitCode: 1, + Error: fmt.Errorf("%s\n%s", preamble, err.Error()), + EmitUsage: false, + }) + } +} + +func AbortIfErrors(preamble string, errors []error) { + if len(errors) > 0 { + out := "" + for _, err := range errors { + out += err.Error() + } + Abort(AbortDetails{ + ExitCode: 1, + Error: fmt.Errorf("%s\n%s", preamble, out), + EmitUsage: false, + }) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go new file mode 100644 index 000000000..12e0e5659 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go @@ -0,0 +1,50 @@ +package command + +import ( + "fmt" + "io" + "strings" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/types" +) + +type Command struct { + Name string + Flags types.GinkgoFlagSet + Usage string + ShortDoc string + Documentation string + DocLink string + Command func(args []string, additionalArgs []string) +} + +func (c Command) Run(args []string, additionalArgs []string) { + args, err := c.Flags.Parse(args) + if err != nil { + AbortWithUsage(err.Error()) + } + + c.Command(args, additionalArgs) +} + +func (c Command) EmitUsage(writer io.Writer) { + fmt.Fprintln(writer, formatter.F("{{bold}}"+c.Usage+"{{/}}")) + fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(c.Usage)))) + if c.ShortDoc != "" { + fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.ShortDoc)) + fmt.Fprintln(writer, "") + } + if c.Documentation != "" { + fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.Documentation)) + fmt.Fprintln(writer, "") + } + if c.DocLink != "" { + fmt.Fprintln(writer, formatter.Fi(0, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}http://onsi.github.io/ginkgo/#%s{{/}}", c.DocLink)) + fmt.Fprintln(writer, "") + } + flagUsage := c.Flags.Usage() + if flagUsage != "" { + fmt.Fprintf(writer, formatter.F(flagUsage)) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go new file mode 100644 index 000000000..88dd8d6b0 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go @@ -0,0 +1,182 @@ +package command + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/types" +) + +type Program struct { + Name string + Heading string + Commands []Command + DefaultCommand Command + DeprecatedCommands []DeprecatedCommand + + //For testing - leave as nil in production + OutWriter io.Writer + ErrWriter io.Writer + Exiter func(code int) +} + +type DeprecatedCommand struct { + Name string + Deprecation types.Deprecation +} + +func (p Program) RunAndExit(osArgs []string) { + var command Command + deprecationTracker := types.NewDeprecationTracker() + if p.Exiter == nil { + p.Exiter = os.Exit + } + if p.OutWriter == nil { + p.OutWriter = formatter.ColorableStdOut + } + if p.ErrWriter == nil { + p.ErrWriter = formatter.ColorableStdErr + } + + defer func() { + exitCode := 0 + + if r := recover(); r != nil { + details, ok := r.(AbortDetails) + if !ok { + panic(r) + } + + if details.Error != nil { + fmt.Fprintln(p.ErrWriter, formatter.F("{{red}}{{bold}}%s %s{{/}} {{red}}failed{{/}}", p.Name, command.Name)) + fmt.Fprintln(p.ErrWriter, formatter.Fi(1, details.Error.Error())) + } + if details.EmitUsage { + if details.Error != nil { + fmt.Fprintln(p.ErrWriter, "") + } + command.EmitUsage(p.ErrWriter) + } + exitCode = details.ExitCode + } + + command.Flags.ValidateDeprecations(deprecationTracker) + if deprecationTracker.DidTrackDeprecations() { + fmt.Fprintln(p.ErrWriter, deprecationTracker.DeprecationsReport()) + } + p.Exiter(exitCode) + return + }() + + args, additionalArgs := []string{}, []string{} + + foundDelimiter := false + for _, arg := range osArgs[1:] { + if !foundDelimiter { + if arg == "--" { + foundDelimiter = true + continue + } + } + + if foundDelimiter { + additionalArgs = append(additionalArgs, arg) + } else { + args = append(args, arg) + } + } + + command = p.DefaultCommand + if len(args) > 0 { + p.handleHelpRequestsAndExit(p.OutWriter, args) + if command.Name == args[0] { + args = args[1:] + } else { + for _, deprecatedCommand := range p.DeprecatedCommands { + if deprecatedCommand.Name == args[0] { + deprecationTracker.TrackDeprecation(deprecatedCommand.Deprecation) + return + } + } + for _, tryCommand := range p.Commands { + if tryCommand.Name == args[0] { + command, args = tryCommand, args[1:] + break + } + } + } + } + + command.Run(args, additionalArgs) +} + +func (p Program) handleHelpRequestsAndExit(writer io.Writer, args []string) { + if len(args) == 0 { + return + } + + matchesHelpFlag := func(args ...string) bool { + for _, arg := range args { + if arg == "--help" || arg == "-help" || arg == "-h" || arg == "--h" { + return true + } + } + return false + } + if len(args) == 1 { + if args[0] == "help" || matchesHelpFlag(args[0]) { + p.EmitUsage(writer) + Abort(AbortDetails{}) + } + } else { + var name string + if args[0] == "help" || matchesHelpFlag(args[0]) { + name = args[1] + } else if matchesHelpFlag(args[1:]...) { + name = args[0] + } else { + return + } + + if p.DefaultCommand.Name == name || p.Name == name { + p.DefaultCommand.EmitUsage(writer) + Abort(AbortDetails{}) + } + for _, command := range p.Commands { + if command.Name == name { + command.EmitUsage(writer) + Abort(AbortDetails{}) + } + } + + fmt.Fprintln(writer, formatter.F("{{red}}Unknown Command: {{bold}}%s{{/}}", name)) + fmt.Fprintln(writer, "") + p.EmitUsage(writer) + Abort(AbortDetails{ExitCode: 1}) + } + return +} + +func (p Program) EmitUsage(writer io.Writer) { + fmt.Fprintln(writer, formatter.F(p.Heading)) + fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(p.Heading)))) + fmt.Fprintln(writer, formatter.F("For usage information for a command, run {{bold}}%s help COMMAND{{/}}.", p.Name)) + fmt.Fprintln(writer, formatter.F("For usage information for the default command, run {{bold}}%s help %s{{/}} or {{bold}}%s help %s{{/}}.", p.Name, p.Name, p.Name, p.DefaultCommand.Name)) + fmt.Fprintln(writer, "") + fmt.Fprintln(writer, formatter.F("The following commands are available:")) + + fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} or %s {{bold}}%s{{/}} - {{gray}}%s{{/}}", p.Name, p.Name, p.DefaultCommand.Name, p.DefaultCommand.Usage)) + if p.DefaultCommand.ShortDoc != "" { + fmt.Fprintln(writer, formatter.Fi(2, p.DefaultCommand.ShortDoc)) + } + + for _, command := range p.Commands { + fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} - {{gray}}%s{{/}}", command.Name, command.Usage)) + if command.ShortDoc != "" { + fmt.Fprintln(writer, formatter.Fi(2, command.ShortDoc)) + } + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go new file mode 100644 index 000000000..a367a1fc9 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go @@ -0,0 +1,48 @@ +package generators + +var bootstrapText = `package {{.Package}} + +import ( + "testing" + + {{.GinkgoImport}} + {{.GomegaImport}} +) + +func Test{{.FormattedName}}(t *testing.T) { + {{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail) + {{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite") +} +` + +var agoutiBootstrapText = `package {{.Package}} + +import ( + "testing" + + {{.GinkgoImport}} + {{.GomegaImport}} + "github.com/sclevine/agouti" +) + +func Test{{.FormattedName}}(t *testing.T) { + {{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail) + {{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite") +} + +var agoutiDriver *agouti.WebDriver + +var _ = {{.GinkgoPackage}}BeforeSuite(func() { + // Choose a WebDriver: + + agoutiDriver = agouti.PhantomJS() + // agoutiDriver = agouti.Selenium() + // agoutiDriver = agouti.ChromeDriver() + + {{.GomegaPackage}}Expect(agoutiDriver.Start()).To({{.GomegaPackage}}Succeed()) +}) + +var _ = {{.GinkgoPackage}}AfterSuite(func() { + {{.GomegaPackage}}Expect(agoutiDriver.Stop()).To({{.GomegaPackage}}Succeed()) +}) +` diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go new file mode 100644 index 000000000..73aff0b7a --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go @@ -0,0 +1,133 @@ +package generators + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "text/template" + + sprig "github.com/go-task/slim-sprig" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/types" +) + +func BuildBootstrapCommand() command.Command { + conf := GeneratorsConfig{} + flags, err := types.NewGinkgoFlagSet( + types.GinkgoFlags{ + {Name: "agouti", KeyPath: "Agouti", + Usage: "If set, bootstrap will generate a bootstrap file for writing Agouti tests"}, + {Name: "nodot", KeyPath: "NoDot", + Usage: "If set, bootstrap will generate a bootstrap test file that does not dot-import ginkgo and gomega"}, + {Name: "internal", KeyPath: "Internal", + Usage: "If set, bootstrap will generate a bootstrap test file that uses the regular package name (i.e. `package X`, not `package X_test`)"}, + {Name: "template", KeyPath: "CustomTemplate", + UsageArgument: "template-file", + Usage: "If specified, generate will use the contents of the file passed as the bootstrap template"}, + {Name: "template-data", KeyPath: "CustomTemplateData", + UsageArgument: "template-data-file", + Usage: "If specified, generate will use the contents of the file passed as data to be rendered in the bootstrap template"}, + }, + &conf, + types.GinkgoFlagSections{}, + ) + + if err != nil { + panic(err) + } + + return command.Command{ + Name: "bootstrap", + Usage: "ginkgo bootstrap", + ShortDoc: "Bootstrap a test suite for the current package", + Documentation: `Tests written in Ginkgo and Gomega require a small amount of boilerplate to hook into Go's testing infrastructure. + +{{bold}}ginkgo bootstrap{{/}} generates this boilerplate for you in a file named X_suite_test.go where X is the name of the package under test.`, + DocLink: "generators", + Flags: flags, + Command: func(_ []string, _ []string) { + generateBootstrap(conf) + }, + } +} + +type bootstrapData struct { + Package string + FormattedName string + + GinkgoImport string + GomegaImport string + GinkgoPackage string + GomegaPackage string + CustomData map[string]any +} + +func generateBootstrap(conf GeneratorsConfig) { + packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName() + + data := bootstrapData{ + Package: determinePackageName(packageName, conf.Internal), + FormattedName: formattedName, + + GinkgoImport: `. "github.com/onsi/ginkgo/v2"`, + GomegaImport: `. "github.com/onsi/gomega"`, + GinkgoPackage: "", + GomegaPackage: "", + } + + if conf.NoDot { + data.GinkgoImport = `"github.com/onsi/ginkgo/v2"` + data.GomegaImport = `"github.com/onsi/gomega"` + data.GinkgoPackage = `ginkgo.` + data.GomegaPackage = `gomega.` + } + + targetFile := fmt.Sprintf("%s_suite_test.go", bootstrapFilePrefix) + if internal.FileExists(targetFile) { + command.AbortWith("{{bold}}%s{{/}} already exists", targetFile) + } else { + fmt.Printf("Generating ginkgo test suite bootstrap for %s in:\n\t%s\n", packageName, targetFile) + } + + f, err := os.Create(targetFile) + command.AbortIfError("Failed to create file:", err) + defer f.Close() + + var templateText string + if conf.CustomTemplate != "" { + tpl, err := os.ReadFile(conf.CustomTemplate) + command.AbortIfError("Failed to read custom bootstrap file:", err) + templateText = string(tpl) + if conf.CustomTemplateData != "" { + var tplCustomDataMap map[string]any + tplCustomData, err := os.ReadFile(conf.CustomTemplateData) + command.AbortIfError("Failed to read custom boostrap data file:", err) + if !json.Valid([]byte(tplCustomData)) { + command.AbortWith("Invalid JSON object in custom data file.") + } + //create map from the custom template data + json.Unmarshal(tplCustomData, &tplCustomDataMap) + data.CustomData = tplCustomDataMap + } + } else if conf.Agouti { + templateText = agoutiBootstrapText + } else { + templateText = bootstrapText + } + + //Setting the option to explicitly fail if template is rendered trying to access missing key + bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText) + command.AbortIfError("Failed to parse bootstrap template:", err) + + buf := &bytes.Buffer{} + //Being explicit about failing sooner during template rendering + //when accessing custom data rather than during the go fmt command + err = bootstrapTemplate.Execute(buf, data) + command.AbortIfError("Failed to render bootstrap template:", err) + + buf.WriteTo(f) + + internal.GoFmt(targetFile) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go new file mode 100644 index 000000000..be01dec97 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go @@ -0,0 +1,264 @@ +package generators + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "text/template" + + sprig "github.com/go-task/slim-sprig" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/types" +) + +func BuildGenerateCommand() command.Command { + conf := GeneratorsConfig{} + flags, err := types.NewGinkgoFlagSet( + types.GinkgoFlags{ + {Name: "agouti", KeyPath: "Agouti", + Usage: "If set, generate will create a test file for writing Agouti tests"}, + {Name: "nodot", KeyPath: "NoDot", + Usage: "If set, generate will create a test file that does not dot-import ginkgo and gomega"}, + {Name: "internal", KeyPath: "Internal", + Usage: "If set, generate will create a test file that uses the regular package name (i.e. `package X`, not `package X_test`)"}, + {Name: "template", KeyPath: "CustomTemplate", + UsageArgument: "template-file", + Usage: "If specified, generate will use the contents of the file passed as the test file template"}, + {Name: "template-data", KeyPath: "CustomTemplateData", + UsageArgument: "template-data-file", + Usage: "If specified, generate will use the contents of the file passed as data to be rendered in the test file template"}, + {Name: "tags", KeyPath: "Tags", + UsageArgument: "build-tags", + Usage: "If specified, generate will create a test file that uses the given build tags (i.e. `--tags e2e,!unit` will add `//go:build e2e,!unit`)"}, + }, + &conf, + types.GinkgoFlagSections{}, + ) + + if err != nil { + panic(err) + } + + return command.Command{ + Name: "generate", + Usage: "ginkgo generate ", + ShortDoc: "Generate a test file named _test.go", + Documentation: `If the optional argument is omitted, a file named after the package in the current directory will be created. + +You can pass multiple to generate multiple files simultaneously. The resulting files are named _test.go. + +You can also pass a of the form "file.go" and generate will emit "file_test.go".`, + DocLink: "generators", + Flags: flags, + Command: func(args []string, _ []string) { + generateTestFiles(conf, args) + }, + } +} + +type specData struct { + BuildTags string + Package string + Subject string + PackageImportPath string + ImportPackage bool + + GinkgoImport string + GomegaImport string + GinkgoPackage string + GomegaPackage string + CustomData map[string]any +} + +func generateTestFiles(conf GeneratorsConfig, args []string) { + subjects := args + if len(subjects) == 0 { + subjects = []string{""} + } + for _, subject := range subjects { + generateTestFileForSubject(subject, conf) + } +} + +func generateTestFileForSubject(subject string, conf GeneratorsConfig) { + packageName, specFilePrefix, formattedName := getPackageAndFormattedName() + if subject != "" { + specFilePrefix = formatSubject(subject) + formattedName = prettifyName(specFilePrefix) + } + + if conf.Internal { + specFilePrefix = specFilePrefix + "_internal" + } + + data := specData{ + BuildTags: getBuildTags(conf.Tags), + Package: determinePackageName(packageName, conf.Internal), + Subject: formattedName, + PackageImportPath: getPackageImportPath(), + ImportPackage: !conf.Internal, + + GinkgoImport: `. "github.com/onsi/ginkgo/v2"`, + GomegaImport: `. "github.com/onsi/gomega"`, + GinkgoPackage: "", + GomegaPackage: "", + } + + if conf.NoDot { + data.GinkgoImport = `"github.com/onsi/ginkgo/v2"` + data.GomegaImport = `"github.com/onsi/gomega"` + data.GinkgoPackage = `ginkgo.` + data.GomegaPackage = `gomega.` + } + + targetFile := fmt.Sprintf("%s_test.go", specFilePrefix) + if internal.FileExists(targetFile) { + command.AbortWith("{{bold}}%s{{/}} already exists", targetFile) + } else { + fmt.Printf("Generating ginkgo test for %s in:\n %s\n", data.Subject, targetFile) + } + + f, err := os.Create(targetFile) + command.AbortIfError("Failed to create test file:", err) + defer f.Close() + + var templateText string + if conf.CustomTemplate != "" { + tpl, err := os.ReadFile(conf.CustomTemplate) + command.AbortIfError("Failed to read custom template file:", err) + templateText = string(tpl) + if conf.CustomTemplateData != "" { + var tplCustomDataMap map[string]any + tplCustomData, err := os.ReadFile(conf.CustomTemplateData) + command.AbortIfError("Failed to read custom template data file:", err) + if !json.Valid([]byte(tplCustomData)) { + command.AbortWith("Invalid JSON object in custom data file.") + } + //create map from the custom template data + json.Unmarshal(tplCustomData, &tplCustomDataMap) + data.CustomData = tplCustomDataMap + } + } else if conf.Agouti { + templateText = agoutiSpecText + } else { + templateText = specText + } + + //Setting the option to explicitly fail if template is rendered trying to access missing key + specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText) + command.AbortIfError("Failed to read parse test template:", err) + + //Being explicit about failing sooner during template rendering + //when accessing custom data rather than during the go fmt command + err = specTemplate.Execute(f, data) + command.AbortIfError("Failed to render bootstrap template:", err) + internal.GoFmt(targetFile) +} + +func formatSubject(name string) string { + name = strings.ReplaceAll(name, "-", "_") + name = strings.ReplaceAll(name, " ", "_") + name = strings.Split(name, ".go")[0] + name = strings.Split(name, "_test")[0] + return name +} + +// moduleName returns module name from go.mod from given module root directory +func moduleName(modRoot string) string { + modFile, err := os.Open(filepath.Join(modRoot, "go.mod")) + if err != nil { + return "" + } + + mod := make([]byte, 128) + _, err = modFile.Read(mod) + if err != nil { + return "" + } + + slashSlash := []byte("//") + moduleStr := []byte("module") + + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + + return "" // missing module path +} + +func findModuleRoot(dir string) (root string) { + dir = filepath.Clean(dir) + + // Look for enclosing go.mod. + for { + if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() { + return dir + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + return "" +} + +func getPackageImportPath() string { + workingDir, err := os.Getwd() + if err != nil { + panic(err.Error()) + } + + sep := string(filepath.Separator) + + // Try go.mod file first + modRoot := findModuleRoot(workingDir) + if modRoot != "" { + modName := moduleName(modRoot) + if modName != "" { + cd := strings.ReplaceAll(workingDir, modRoot, "") + cd = strings.ReplaceAll(cd, sep, "/") + return modName + cd + } + } + + // Fallback to GOPATH structure + paths := strings.Split(workingDir, sep+"src"+sep) + if len(paths) == 1 { + fmt.Printf("\nCouldn't identify package import path.\n\n\tginkgo generate\n\nMust be run within a package directory under $GOPATH/src/...\nYou're going to have to change UNKNOWN_PACKAGE_PATH in the generated file...\n\n") + return "UNKNOWN_PACKAGE_PATH" + } + return filepath.ToSlash(paths[len(paths)-1]) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go new file mode 100644 index 000000000..4dab07d03 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go @@ -0,0 +1,43 @@ +package generators + +var specText = `{{.BuildTags}} +package {{.Package}} + +import ( + {{.GinkgoImport}} + {{.GomegaImport}} + + {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}} +) + +var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() { + +}) +` + +var agoutiSpecText = `{{.BuildTags}} +package {{.Package}} + +import ( + {{.GinkgoImport}} + {{.GomegaImport}} + "github.com/sclevine/agouti" + . "github.com/sclevine/agouti/matchers" + + {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}} +) + +var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() { + var page *agouti.Page + + {{.GinkgoPackage}}BeforeEach(func() { + var err error + page, err = agoutiDriver.NewPage() + {{.GomegaPackage}}Expect(err).NotTo({{.GomegaPackage}}HaveOccurred()) + }) + + {{.GinkgoPackage}}AfterEach(func() { + {{.GomegaPackage}}Expect(page.Destroy()).To({{.GomegaPackage}}Succeed()) + }) +}) +` diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go new file mode 100644 index 000000000..28c7aa6f4 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go @@ -0,0 +1,76 @@ +package generators + +import ( + "fmt" + "go/build" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/onsi/ginkgo/v2/ginkgo/command" +) + +type GeneratorsConfig struct { + Agouti, NoDot, Internal bool + CustomTemplate string + CustomTemplateData string + Tags string +} + +func getPackageAndFormattedName() (string, string, string) { + path, err := os.Getwd() + command.AbortIfError("Could not get current working directory:", err) + + dirName := strings.ReplaceAll(filepath.Base(path), "-", "_") + dirName = strings.ReplaceAll(dirName, " ", "_") + + pkg, err := build.ImportDir(path, 0) + packageName := pkg.Name + if err != nil { + packageName = ensureLegalPackageName(dirName) + } + + formattedName := prettifyName(filepath.Base(path)) + return packageName, dirName, formattedName +} + +func ensureLegalPackageName(name string) string { + if name == "_" { + return "underscore" + } + if len(name) == 0 { + return "empty" + } + n, isDigitErr := strconv.Atoi(string(name[0])) + if isDigitErr == nil { + return []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}[n] + name[1:] + } + return name +} + +func prettifyName(name string) string { + name = strings.ReplaceAll(name, "-", " ") + name = strings.ReplaceAll(name, "_", " ") + name = strings.Title(name) + name = strings.ReplaceAll(name, " ", "") + return name +} + +func determinePackageName(name string, internal bool) string { + if internal { + return name + } + + return name + "_test" +} + +// getBuildTags returns the resultant string to be added. +// If the input string is not empty, then returns a `//go:build {}` string, +// otherwise returns an empty string. +func getBuildTags(tags string) string { + if tags != "" { + return fmt.Sprintf("//go:build %s\n", tags) + } + return "" +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go new file mode 100644 index 000000000..86da7340d --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go @@ -0,0 +1,161 @@ +package internal + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + + "github.com/onsi/ginkgo/v2/types" +) + +func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite { + if suite.PathToCompiledTest != "" { + return suite + } + + suite.CompilationError = nil + + path, err := filepath.Abs(filepath.Join(suite.Path, suite.PackageName+".test")) + if err != nil { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to compute compilation target path:\n%s", err.Error()) + return suite + } + + ginkgoInvocationPath, _ := os.Getwd() + ginkgoInvocationPath, _ = filepath.Abs(ginkgoInvocationPath) + packagePath := suite.AbsPath() + pathToInvocationPath, err := filepath.Rel(packagePath, ginkgoInvocationPath) + if err != nil { + suite.State = TestSuiteStateFailedToCompile + 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) + if err != nil { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error()) + return suite + } + + cmd := exec.Command("go", args...) + cmd.Dir = suite.Path + output, err := cmd.CombinedOutput() + if err != nil { + if len(output) > 0 { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to compile %s:\n\n%s", suite.PackageName, output) + } else { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to compile %s\n%s", suite.PackageName, err.Error()) + } + return suite + } + + if strings.Contains(string(output), "[no test files]") { + suite.State = TestSuiteStateSkippedDueToEmptyCompilation + return suite + } + + if len(output) > 0 { + fmt.Println(string(output)) + } + + if !FileExists(path) { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to compile %s:\nOutput file %s could not be found", suite.PackageName, path) + return suite + } + + suite.State = TestSuiteStateCompiled + suite.PathToCompiledTest = path + return suite +} + +func Cleanup(goFlagsConfig types.GoFlagsConfig, suites ...TestSuite) { + if goFlagsConfig.BinaryMustBePreserved() { + return + } + for _, suite := range suites { + if !suite.Precompiled { + os.Remove(suite.PathToCompiledTest) + } + } +} + +type parallelSuiteBundle struct { + suite TestSuite + compiled chan TestSuite +} + +type OrderedParallelCompiler struct { + mutex *sync.Mutex + stopped bool + numCompilers int + + idx int + numSuites int + completionChannels []chan TestSuite +} + +func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler { + return &OrderedParallelCompiler{ + mutex: &sync.Mutex{}, + numCompilers: numCompilers, + } +} + +func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) { + opc.stopped = false + opc.idx = 0 + opc.numSuites = len(suites) + opc.completionChannels = make([]chan TestSuite, opc.numSuites) + + toCompile := make(chan parallelSuiteBundle, opc.numCompilers) + for compiler := 0; compiler < opc.numCompilers; compiler++ { + go func() { + for bundle := range toCompile { + c, suite := bundle.compiled, bundle.suite + opc.mutex.Lock() + stopped := opc.stopped + opc.mutex.Unlock() + if !stopped { + suite = CompileSuite(suite, goFlagsConfig) + } + c <- suite + } + }() + } + + for idx, suite := range suites { + opc.completionChannels[idx] = make(chan TestSuite, 1) + toCompile <- parallelSuiteBundle{suite, opc.completionChannels[idx]} + if idx == 0 { //compile first suite serially + suite = <-opc.completionChannels[0] + opc.completionChannels[0] <- suite + } + } + + close(toCompile) +} + +func (opc *OrderedParallelCompiler) Next() (int, TestSuite) { + if opc.idx >= opc.numSuites { + return opc.numSuites, TestSuite{} + } + + idx := opc.idx + suite := <-opc.completionChannels[idx] + opc.idx = opc.idx + 1 + + return idx, suite +} + +func (opc *OrderedParallelCompiler) StopAndDrain() { + opc.mutex.Lock() + opc.stopped = true + opc.mutex.Unlock() +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go new file mode 100644 index 000000000..bd3c6d028 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go @@ -0,0 +1,237 @@ +package internal + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + + "github.com/google/pprof/profile" + "github.com/onsi/ginkgo/v2/reporters" + "github.com/onsi/ginkgo/v2/types" +) + +func AbsPathForGeneratedAsset(assetName string, suite TestSuite, cliConfig types.CLIConfig, process int) string { + suffix := "" + if process != 0 { + suffix = fmt.Sprintf(".%d", process) + } + if cliConfig.OutputDir == "" { + return filepath.Join(suite.AbsPath(), assetName+suffix) + } + outputDir, _ := filepath.Abs(cliConfig.OutputDir) + return filepath.Join(outputDir, suite.NamespacedName()+"_"+assetName+suffix) +} + +func FinalizeProfilesAndReportsForSuites(suites TestSuites, cliConfig types.CLIConfig, suiteConfig types.SuiteConfig, reporterConfig types.ReporterConfig, goFlagsConfig types.GoFlagsConfig) ([]string, error) { + messages := []string{} + suitesWithProfiles := suites.WithState(TestSuiteStatePassed, TestSuiteStateFailed) //anything else won't have actually run and generated a profile + + // merge cover profiles if need be + if goFlagsConfig.Cover && !cliConfig.KeepSeparateCoverprofiles { + coverProfiles := []string{} + for _, suite := range suitesWithProfiles { + if !suite.HasProgrammaticFocus { + coverProfiles = append(coverProfiles, AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)) + } + } + + if len(coverProfiles) > 0 { + dst := goFlagsConfig.CoverProfile + if cliConfig.OutputDir != "" { + dst = filepath.Join(cliConfig.OutputDir, goFlagsConfig.CoverProfile) + } + err := MergeAndCleanupCoverProfiles(coverProfiles, dst) + if err != nil { + return messages, err + } + coverage, err := GetCoverageFromCoverProfile(dst) + if err != nil { + return messages, err + } + if coverage == 0 { + messages = append(messages, "composite coverage: [no statements]") + } else if suitesWithProfiles.AnyHaveProgrammaticFocus() { + messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements however some suites did not contribute because they included programatically focused specs", coverage)) + } else { + messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements", coverage)) + } + } else { + messages = append(messages, "no composite coverage computed: all suites included programatically focused specs") + } + } + + // copy binaries if need be + for _, suite := range suitesWithProfiles { + if goFlagsConfig.BinaryMustBePreserved() && cliConfig.OutputDir != "" { + src := suite.PathToCompiledTest + dst := filepath.Join(cliConfig.OutputDir, suite.NamespacedName()+".test") + if suite.Precompiled { + if err := CopyFile(src, dst); err != nil { + return messages, err + } + } else { + if err := os.Rename(src, dst); err != nil { + return messages, err + } + } + } + } + + type reportFormat struct { + ReportName string + GenerateFunc func(types.Report, string) error + MergeFunc func([]string, string) ([]string, error) + } + reportFormats := []reportFormat{} + if reporterConfig.JSONReport != "" { + reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JSONReport, GenerateFunc: reporters.GenerateJSONReport, MergeFunc: reporters.MergeAndCleanupJSONReports}) + } + if reporterConfig.JUnitReport != "" { + reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JUnitReport, GenerateFunc: reporters.GenerateJUnitReport, MergeFunc: reporters.MergeAndCleanupJUnitReports}) + } + if reporterConfig.TeamcityReport != "" { + reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.TeamcityReport, GenerateFunc: reporters.GenerateTeamcityReport, MergeFunc: reporters.MergeAndCleanupTeamcityReports}) + } + + // Generate reports for suites that failed to run + reportableSuites := suites.ThatAreGinkgoSuites() + for _, suite := range reportableSuites.WithState(TestSuiteStateFailedToCompile, TestSuiteStateFailedDueToTimeout, TestSuiteStateSkippedDueToPriorFailures, TestSuiteStateSkippedDueToEmptyCompilation) { + report := types.Report{ + SuitePath: suite.AbsPath(), + SuiteConfig: suiteConfig, + SuiteSucceeded: false, + } + switch suite.State { + case TestSuiteStateFailedToCompile: + report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, suite.CompilationError.Error()) + case TestSuiteStateFailedDueToTimeout: + report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, TIMEOUT_ELAPSED_FAILURE_REASON) + case TestSuiteStateSkippedDueToPriorFailures: + report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, PRIOR_FAILURES_FAILURE_REASON) + case TestSuiteStateSkippedDueToEmptyCompilation: + report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, EMPTY_SKIP_FAILURE_REASON) + report.SuiteSucceeded = true + } + + for _, format := range reportFormats { + format.GenerateFunc(report, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0)) + } + } + + // Merge reports unless we've been asked to keep them separate + if !cliConfig.KeepSeparateReports { + for _, format := range reportFormats { + reports := []string{} + for _, suite := range reportableSuites { + reports = append(reports, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0)) + } + dst := format.ReportName + if cliConfig.OutputDir != "" { + dst = filepath.Join(cliConfig.OutputDir, format.ReportName) + } + mergeMessages, err := format.MergeFunc(reports, dst) + messages = append(messages, mergeMessages...) + if err != nil { + return messages, err + } + } + } + + return messages, nil +} + +//loads each profile, combines 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) + if err != nil { + return fmt.Errorf("Unable to read coverage file %s:\n%s", profile, err.Error()) + } + 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()) + } + } + + err := os.WriteFile(destination, combined.Bytes(), 0666) + if err != nil { + return fmt.Errorf("Unable to create combined cover profile:\n%s", err.Error()) + } + return nil +} + +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()) + } + re := regexp.MustCompile(`total:\s*\(statements\)\s*(\d*\.\d*)\%`) + matches := re.FindStringSubmatch(string(output)) + if matches == nil { + return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage") + } + coverageString := matches[1] + coverage, err := strconv.ParseFloat(coverageString, 64) + if err != nil { + return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage: %s", err.Error()) + } + + return coverage, nil +} + +func MergeProfiles(profilePaths []string, destination string) error { + profiles := []*profile.Profile{} + for _, profilePath := range profilePaths { + proFile, err := os.Open(profilePath) + if err != nil { + return fmt.Errorf("Could not open profile: %s\n%s", profilePath, err.Error()) + } + prof, err := profile.Parse(proFile) + if err != nil { + return fmt.Errorf("Could not parse profile: %s\n%s", profilePath, err.Error()) + } + profiles = append(profiles, prof) + os.Remove(profilePath) + } + + mergedProfile, err := profile.Merge(profiles) + if err != nil { + return fmt.Errorf("Could not merge profiles:\n%s", err.Error()) + } + + outFile, err := os.Create(destination) + if err != nil { + return fmt.Errorf("Could not create merged profile %s:\n%s", destination, err.Error()) + } + err = mergedProfile.Write(outFile) + if err != nil { + return fmt.Errorf("Could not write merged profile %s:\n%s", destination, err.Error()) + } + err = outFile.Close() + if err != nil { + return fmt.Errorf("Could not close merged profile %s:\n%s", destination, err.Error()) + } + + return nil +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go new file mode 100644 index 000000000..41052ea19 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go @@ -0,0 +1,355 @@ +package internal + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "syscall" + "time" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/internal/parallel_support" + "github.com/onsi/ginkgo/v2/reporters" + "github.com/onsi/ginkgo/v2/types" +) + +func RunCompiledSuite(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { + suite.State = TestSuiteStateFailed + suite.HasProgrammaticFocus = false + + if suite.PathToCompiledTest == "" { + return suite + } + + if suite.IsGinkgo && cliConfig.ComputedProcs() > 1 { + suite = runParallel(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs) + } else if suite.IsGinkgo { + suite = runSerial(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs) + } else { + suite = runGoTest(suite, cliConfig, goFlagsConfig) + } + runAfterRunHook(cliConfig.AfterRunHook, reporterConfig.NoColor, suite) + return suite +} + +func buildAndStartCommand(suite TestSuite, args []string, pipeToStdout bool) (*exec.Cmd, *bytes.Buffer) { + buf := &bytes.Buffer{} + cmd := exec.Command(suite.PathToCompiledTest, args...) + cmd.Dir = suite.Path + if pipeToStdout { + cmd.Stderr = io.MultiWriter(os.Stdout, buf) + cmd.Stdout = os.Stdout + } else { + cmd.Stderr = buf + cmd.Stdout = buf + } + err := cmd.Start() + command.AbortIfError("Failed to start test suite", err) + + return cmd, buf +} + +func checkForNoTestsWarning(buf *bytes.Buffer) bool { + if strings.Contains(buf.String(), "warning: no tests to run") { + fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`) + return true + } + return false +} + +func runGoTest(suite TestSuite, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) TestSuite { + // As we run the go test from the suite directory, make sure the cover profile is absolute + // and placed into the expected output directory when one is configured. + if goFlagsConfig.Cover && !filepath.IsAbs(goFlagsConfig.CoverProfile) { + goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) + } + + args, err := types.GenerateGoTestRunArgs(goFlagsConfig) + command.AbortIfError("Failed to generate test run arguments", err) + cmd, buf := buildAndStartCommand(suite, args, true) + + cmd.Wait() + + exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() + passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) + passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed + if passed { + suite.State = TestSuiteStatePassed + } else { + suite.State = TestSuiteStateFailed + } + + return suite +} + +func runSerial(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { + if goFlagsConfig.Cover { + goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) + } + if goFlagsConfig.BlockProfile != "" { + goFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0) + } + if goFlagsConfig.CPUProfile != "" { + goFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0) + } + if goFlagsConfig.MemProfile != "" { + goFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0) + } + if goFlagsConfig.MutexProfile != "" { + goFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0) + } + if reporterConfig.JSONReport != "" { + reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0) + } + if reporterConfig.JUnitReport != "" { + reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0) + } + if reporterConfig.TeamcityReport != "" { + reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0) + } + + args, err := types.GenerateGinkgoTestRunArgs(ginkgoConfig, reporterConfig, goFlagsConfig) + command.AbortIfError("Failed to generate test run arguments", err) + args = append([]string{"--test.timeout=0"}, args...) + args = append(args, additionalArgs...) + + cmd, buf := buildAndStartCommand(suite, args, true) + + cmd.Wait() + + exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() + suite.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) + passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) + passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed + if passed { + suite.State = TestSuiteStatePassed + } else { + suite.State = TestSuiteStateFailed + } + + if suite.HasProgrammaticFocus { + if goFlagsConfig.Cover { + fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused") + } + if goFlagsConfig.BlockProfile != "" { + fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused") + } + if goFlagsConfig.CPUProfile != "" { + fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused") + } + if goFlagsConfig.MemProfile != "" { + fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused") + } + if goFlagsConfig.MutexProfile != "" { + fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused") + } + } + + return suite +} + +func runParallel(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { + type procResult struct { + passed bool + hasProgrammaticFocus bool + } + + numProcs := cliConfig.ComputedProcs() + procOutput := make([]*bytes.Buffer, numProcs) + coverProfiles := []string{} + + blockProfiles := []string{} + cpuProfiles := []string{} + memProfiles := []string{} + mutexProfiles := []string{} + + procResults := make(chan procResult) + + server, err := parallel_support.NewServer(numProcs, reporters.NewDefaultReporter(reporterConfig, formatter.ColorableStdOut)) + command.AbortIfError("Failed to start parallel spec server", err) + server.Start() + defer server.Close() + + if reporterConfig.JSONReport != "" { + reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0) + } + if reporterConfig.JUnitReport != "" { + reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0) + } + if reporterConfig.TeamcityReport != "" { + reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0) + } + + for proc := 1; proc <= numProcs; proc++ { + procGinkgoConfig := ginkgoConfig + procGinkgoConfig.ParallelProcess, procGinkgoConfig.ParallelTotal, procGinkgoConfig.ParallelHost = proc, numProcs, server.Address() + + procGoFlagsConfig := goFlagsConfig + if goFlagsConfig.Cover { + procGoFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, proc) + coverProfiles = append(coverProfiles, procGoFlagsConfig.CoverProfile) + } + if goFlagsConfig.BlockProfile != "" { + procGoFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, proc) + blockProfiles = append(blockProfiles, procGoFlagsConfig.BlockProfile) + } + if goFlagsConfig.CPUProfile != "" { + procGoFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, proc) + cpuProfiles = append(cpuProfiles, procGoFlagsConfig.CPUProfile) + } + if goFlagsConfig.MemProfile != "" { + procGoFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, proc) + memProfiles = append(memProfiles, procGoFlagsConfig.MemProfile) + } + if goFlagsConfig.MutexProfile != "" { + procGoFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, proc) + mutexProfiles = append(mutexProfiles, procGoFlagsConfig.MutexProfile) + } + + args, err := types.GenerateGinkgoTestRunArgs(procGinkgoConfig, reporterConfig, procGoFlagsConfig) + command.AbortIfError("Failed to generate test run arguments", err) + args = append([]string{"--test.timeout=0"}, args...) + args = append(args, additionalArgs...) + + cmd, buf := buildAndStartCommand(suite, args, false) + procOutput[proc-1] = buf + server.RegisterAlive(proc, func() bool { return cmd.ProcessState == nil || !cmd.ProcessState.Exited() }) + + go func() { + cmd.Wait() + exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() + procResults <- procResult{ + passed: (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE), + hasProgrammaticFocus: exitStatus == types.GINKGO_FOCUS_EXIT_CODE, + } + }() + } + + passed := true + for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { + result := <-procResults + passed = passed && result.passed + suite.HasProgrammaticFocus = suite.HasProgrammaticFocus || result.hasProgrammaticFocus + } + if passed { + suite.State = TestSuiteStatePassed + } else { + suite.State = TestSuiteStateFailed + } + + select { + case <-server.GetSuiteDone(): + fmt.Println("") + case <-time.After(time.Second): + //one of the nodes never finished reporting to the server. Something must have gone wrong. + fmt.Fprint(formatter.ColorableStdErr, formatter.F("\n{{bold}}{{red}}Ginkgo timed out waiting for all parallel procs to report back{{/}}\n")) + fmt.Fprint(formatter.ColorableStdErr, formatter.F("{{gray}}Test suite:{{/}} %s (%s)\n\n", suite.PackageName, suite.Path)) + fmt.Fprint(formatter.ColorableStdErr, formatter.Fiw(0, formatter.COLS, "This occurs if a parallel process exits before it reports its results to the Ginkgo CLI. The CLI will now print out all the stdout/stderr output it's collected from the running processes. However you may not see anything useful in these logs because the individual test processes usually intercept output to stdout/stderr in order to capture it in the spec reports.\n\nYou may want to try rerunning your test suite with {{light-gray}}--output-interceptor-mode=none{{/}} to see additional output here and debug your suite.\n")) + fmt.Fprintln(formatter.ColorableStdErr, " ") + for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { + fmt.Fprintf(formatter.ColorableStdErr, formatter.F("{{bold}}Output from proc %d:{{/}}\n", proc)) + fmt.Fprintln(os.Stderr, formatter.Fi(1, "%s", procOutput[proc-1].String())) + } + fmt.Fprintf(os.Stderr, "** End **") + } + + for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { + output := procOutput[proc-1].String() + if proc == 1 && checkForNoTestsWarning(procOutput[0]) && cliConfig.RequireSuite { + suite.State = TestSuiteStateFailed + } + if strings.Contains(output, "deprecated Ginkgo functionality") { + fmt.Fprintln(os.Stderr, output) + } + } + + if len(coverProfiles) > 0 { + if suite.HasProgrammaticFocus { + fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused") + } else { + coverProfile := AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) + err := MergeAndCleanupCoverProfiles(coverProfiles, coverProfile) + command.AbortIfError("Failed to combine cover profiles", err) + + coverage, err := GetCoverageFromCoverProfile(coverProfile) + command.AbortIfError("Failed to compute coverage", err) + if coverage == 0 { + fmt.Fprintln(os.Stdout, "coverage: [no statements]") + } else { + fmt.Fprintf(os.Stdout, "coverage: %.1f%% of statements\n", coverage) + } + } + } + if len(blockProfiles) > 0 { + if suite.HasProgrammaticFocus { + fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused") + } else { + blockProfile := AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0) + err := MergeProfiles(blockProfiles, blockProfile) + command.AbortIfError("Failed to combine blockprofiles", err) + } + } + if len(cpuProfiles) > 0 { + if suite.HasProgrammaticFocus { + fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused") + } else { + cpuProfile := AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0) + err := MergeProfiles(cpuProfiles, cpuProfile) + command.AbortIfError("Failed to combine cpuprofiles", err) + } + } + if len(memProfiles) > 0 { + if suite.HasProgrammaticFocus { + fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused") + } else { + memProfile := AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0) + err := MergeProfiles(memProfiles, memProfile) + command.AbortIfError("Failed to combine memprofiles", err) + } + } + if len(mutexProfiles) > 0 { + if suite.HasProgrammaticFocus { + fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused") + } else { + mutexProfile := AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0) + err := MergeProfiles(mutexProfiles, mutexProfile) + command.AbortIfError("Failed to combine mutexprofiles", err) + } + } + + return suite +} + +func runAfterRunHook(command string, noColor bool, suite TestSuite) { + if command == "" { + return + } + f := formatter.NewWithNoColorBool(noColor) + + // Allow for string replacement to pass input to the command + passed := "[FAIL]" + if suite.State.Is(TestSuiteStatePassed) { + passed = "[PASS]" + } + command = strings.ReplaceAll(command, "(ginkgo-suite-passed)", passed) + command = strings.ReplaceAll(command, "(ginkgo-suite-name)", suite.PackageName) + + // Must break command into parts + splitArgs := regexp.MustCompile(`'.+'|".+"|\S+`) + parts := splitArgs.FindAllString(command, -1) + + output, err := exec.Command(parts[0], parts[1:]...).CombinedOutput() + if err != nil { + fmt.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{red}}{{bold}}After-run-hook failed:{{/}}")) + fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{red}}%s{{/}}", output)) + } else { + fmt.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{green}}{{bold}}After-run-hook succeeded:{{/}}")) + fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{green}}%s{{/}}", output)) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go new file mode 100644 index 000000000..64dcb1b78 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go @@ -0,0 +1,283 @@ +package internal + +import ( + "errors" + "math/rand" + "os" + "path" + "path/filepath" + "regexp" + "strings" + + "github.com/onsi/ginkgo/v2/types" +) + +const TIMEOUT_ELAPSED_FAILURE_REASON = "Suite did not run because the timeout elapsed" +const PRIOR_FAILURES_FAILURE_REASON = "Suite did not run because prior suites failed and --keep-going is not set" +const EMPTY_SKIP_FAILURE_REASON = "Suite did not run go test reported that no test files were found" + +type TestSuiteState uint + +const ( + TestSuiteStateInvalid TestSuiteState = iota + + TestSuiteStateUncompiled + TestSuiteStateCompiled + + TestSuiteStatePassed + + TestSuiteStateSkippedDueToEmptyCompilation + TestSuiteStateSkippedByFilter + TestSuiteStateSkippedDueToPriorFailures + + TestSuiteStateFailed + TestSuiteStateFailedDueToTimeout + TestSuiteStateFailedToCompile +) + +var TestSuiteStateFailureStates = []TestSuiteState{TestSuiteStateFailed, TestSuiteStateFailedDueToTimeout, TestSuiteStateFailedToCompile} + +func (state TestSuiteState) Is(states ...TestSuiteState) bool { + for _, suiteState := range states { + if suiteState == state { + return true + } + } + + return false +} + +type TestSuite struct { + Path string + PackageName string + IsGinkgo bool + + Precompiled bool + PathToCompiledTest string + CompilationError error + + HasProgrammaticFocus bool + State TestSuiteState +} + +func (ts TestSuite) AbsPath() string { + path, _ := filepath.Abs(ts.Path) + return path +} + +func (ts TestSuite) NamespacedName() string { + name := relPath(ts.Path) + name = strings.TrimLeft(name, "."+string(filepath.Separator)) + name = strings.ReplaceAll(name, string(filepath.Separator), "_") + name = strings.ReplaceAll(name, " ", "_") + if name == "" { + return ts.PackageName + } + return name +} + +type TestSuites []TestSuite + +func (ts TestSuites) AnyHaveProgrammaticFocus() bool { + for _, suite := range ts { + if suite.HasProgrammaticFocus { + return true + } + } + + return false +} + +func (ts TestSuites) ThatAreGinkgoSuites() TestSuites { + out := TestSuites{} + for _, suite := range ts { + if suite.IsGinkgo { + out = append(out, suite) + } + } + return out +} + +func (ts TestSuites) CountWithState(states ...TestSuiteState) int { + n := 0 + for _, suite := range ts { + if suite.State.Is(states...) { + n += 1 + } + } + + return n +} + +func (ts TestSuites) WithState(states ...TestSuiteState) TestSuites { + out := TestSuites{} + for _, suite := range ts { + if suite.State.Is(states...) { + out = append(out, suite) + } + } + + return out +} + +func (ts TestSuites) WithoutState(states ...TestSuiteState) TestSuites { + out := TestSuites{} + for _, suite := range ts { + if !suite.State.Is(states...) { + out = append(out, suite) + } + } + + return out +} + +func (ts TestSuites) ShuffledCopy(seed int64) TestSuites { + out := make(TestSuites, len(ts)) + permutation := rand.New(rand.NewSource(seed)).Perm(len(ts)) + for i, j := range permutation { + out[i] = ts[j] + } + return out +} + +func FindSuites(args []string, cliConfig types.CLIConfig, allowPrecompiled bool) TestSuites { + suites := TestSuites{} + + if len(args) > 0 { + for _, arg := range args { + if allowPrecompiled { + suite, err := precompiledTestSuite(arg) + if err == nil { + suites = append(suites, suite) + continue + } + } + recurseForSuite := cliConfig.Recurse + if strings.HasSuffix(arg, "/...") && arg != "/..." { + arg = arg[:len(arg)-4] + recurseForSuite = true + } + suites = append(suites, suitesInDir(arg, recurseForSuite)...) + } + } else { + suites = suitesInDir(".", cliConfig.Recurse) + } + + if cliConfig.SkipPackage != "" { + skipFilters := strings.Split(cliConfig.SkipPackage, ",") + for idx := range suites { + for _, skipFilter := range skipFilters { + if strings.Contains(suites[idx].Path, skipFilter) { + suites[idx].State = TestSuiteStateSkippedByFilter + break + } + } + } + } + + return suites +} + +func precompiledTestSuite(path string) (TestSuite, error) { + info, err := os.Stat(path) + if err != nil { + return TestSuite{}, err + } + + if info.IsDir() { + return TestSuite{}, errors.New("this is a directory, not a file") + } + + if filepath.Ext(path) != ".test" && filepath.Ext(path) != ".exe" { + return TestSuite{}, errors.New("this is not a .test binary") + } + + if filepath.Ext(path) == ".test" && info.Mode()&0111 == 0 { + return TestSuite{}, errors.New("this is not executable") + } + + dir := relPath(filepath.Dir(path)) + packageName := strings.TrimSuffix(filepath.Base(path), ".exe") + packageName = strings.TrimSuffix(packageName, ".test") + + path, err = filepath.Abs(path) + if err != nil { + return TestSuite{}, err + } + + return TestSuite{ + Path: dir, + PackageName: packageName, + IsGinkgo: true, + Precompiled: true, + PathToCompiledTest: path, + State: TestSuiteStateCompiled, + }, nil +} + +func suitesInDir(dir string, recurse bool) TestSuites { + suites := TestSuites{} + + if path.Base(dir) == "vendor" { + return suites + } + + files, _ := os.ReadDir(dir) + re := regexp.MustCompile(`^[^._].*_test\.go$`) + for _, file := range files { + if !file.IsDir() && re.Match([]byte(file.Name())) { + suite := TestSuite{ + Path: relPath(dir), + PackageName: packageNameForSuite(dir), + IsGinkgo: filesHaveGinkgoSuite(dir, files), + State: TestSuiteStateUncompiled, + } + suites = append(suites, suite) + break + } + } + + if recurse { + re = regexp.MustCompile(`^[._]`) + for _, file := range files { + if file.IsDir() && !re.Match([]byte(file.Name())) { + suites = append(suites, suitesInDir(dir+"/"+file.Name(), recurse)...) + } + } + } + + return suites +} + +func relPath(dir string) string { + dir, _ = filepath.Abs(dir) + cwd, _ := os.Getwd() + dir, _ = filepath.Rel(cwd, filepath.Clean(dir)) + + if string(dir[0]) != "." { + dir = "." + string(filepath.Separator) + dir + } + + return dir +} + +func packageNameForSuite(dir string) string { + path, _ := filepath.Abs(dir) + return filepath.Base(path) +} + +func filesHaveGinkgoSuite(dir string, files []os.DirEntry) bool { + reTestFile := regexp.MustCompile(`_test\.go$`) + reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"|\/ginkgo\/v2"|\/ginkgo\/v2/dsl/`) + + for _, file := range files { + if !file.IsDir() && reTestFile.Match([]byte(file.Name())) { + contents, _ := os.ReadFile(dir + "/" + file.Name()) + if reGinkgo.Match(contents) { + return true + } + } + } + + return false +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go new file mode 100644 index 000000000..bd9ca7d51 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go @@ -0,0 +1,86 @@ +package internal + +import ( + "fmt" + "io" + "os" + "os/exec" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/ginkgo/command" +) + +func FileExists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +func CopyFile(src string, dest string) error { + srcFile, err := os.Open(src) + if err != nil { + return err + } + + srcStat, err := srcFile.Stat() + if err != nil { + return err + } + + if _, err := os.Stat(dest); err == nil { + os.Remove(dest) + } + + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, srcStat.Mode()) + if err != nil { + return err + } + + _, err = io.Copy(destFile, srcFile) + if err != nil { + return err + } + + if err := srcFile.Close(); err != nil { + return err + } + return destFile.Close() +} + +func GoFmt(path string) { + out, err := exec.Command("go", "fmt", path).CombinedOutput() + if err != nil { + command.AbortIfError(fmt.Sprintf("Could not fmt:\n%s\n", string(out)), err) + } +} + +func PluralizedWord(singular, plural string, count int) string { + if count == 1 { + return singular + } + return plural +} + +func FailedSuitesReport(suites TestSuites, f formatter.Formatter) string { + out := "" + out += "There were failures detected in the following suites:\n" + + maxPackageNameLength := 0 + for _, suite := range suites.WithState(TestSuiteStateFailureStates...) { + if len(suite.PackageName) > maxPackageNameLength { + maxPackageNameLength = len(suite.PackageName) + } + } + + packageNameFormatter := fmt.Sprintf("%%%ds", maxPackageNameLength) + for _, suite := range suites { + switch suite.State { + case TestSuiteStateFailed: + out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s{{/}}\n", suite.PackageName, suite.Path) + case TestSuiteStateFailedToCompile: + out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{magenta}}[Compilation failure]{{/}}\n", suite.PackageName, suite.Path) + case TestSuiteStateFailedDueToTimeout: + out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{orange}}[%s]{{/}}\n", suite.PackageName, suite.Path, TIMEOUT_ELAPSED_FAILURE_REASON) + } + } + return out +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go new file mode 100644 index 000000000..9da1bab3d --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go @@ -0,0 +1,54 @@ +package internal + +import ( + "fmt" + "os/exec" + "regexp" + "strings" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/types" +) + +var versiorRe = regexp.MustCompile(`v(\d+\.\d+\.\d+)`) + +func VerifyCLIAndFrameworkVersion(suites TestSuites) { + cliVersion := types.VERSION + mismatches := map[string][]string{} + + for _, suite := range suites { + cmd := exec.Command("go", "list", "-m", "github.com/onsi/ginkgo/v2") + cmd.Dir = suite.Path + output, err := cmd.CombinedOutput() + if err != nil { + continue + } + components := strings.Split(string(output), " ") + if len(components) != 2 { + continue + } + matches := versiorRe.FindStringSubmatch(components[1]) + if matches == nil || len(matches) != 2 { + continue + } + libraryVersion := matches[1] + if cliVersion != libraryVersion { + mismatches[libraryVersion] = append(mismatches[libraryVersion], suite.PackageName) + } + } + + if len(mismatches) == 0 { + return + } + + fmt.Println(formatter.F("{{red}}{{bold}}Ginkgo detected a version mismatch between the Ginkgo CLI and the version of Ginkgo imported by your packages:{{/}}")) + + fmt.Println(formatter.Fi(1, "Ginkgo CLI Version:")) + fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}}", cliVersion)) + fmt.Println(formatter.Fi(1, "Mismatched package versions found:")) + for version, packages := range mismatches { + fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}} used by %s", version, strings.Join(packages, ", "))) + } + fmt.Println("") + fmt.Println(formatter.Fiw(1, formatter.COLS, "{{gray}}Ginkgo will continue to attempt to run but you may see errors (including flag parsing errors) and should either update your go.mod or your version of the Ginkgo CLI to match.\n\nTo install the matching version of the CLI run\n {{bold}}go install github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file. Alternatively you can use\n {{bold}}go run github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file to invoke the matching version of the Ginkgo CLI.\n\nIf you are attempting to test multiple packages that each have a different version of the Ginkgo library with a single Ginkgo CLI that is currently unsupported.\n{{/}}")) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go new file mode 100644 index 000000000..6c61f09d1 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go @@ -0,0 +1,123 @@ +package labels + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "sort" + "strconv" + "strings" + + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/types" + "golang.org/x/tools/go/ast/inspector" +) + +func BuildLabelsCommand() command.Command { + var cliConfig = types.NewDefaultCLIConfig() + + flags, err := types.BuildLabelsCommandFlagSet(&cliConfig) + if err != nil { + panic(err) + } + + return command.Command{ + Name: "labels", + Usage: "ginkgo labels ", + Flags: flags, + ShortDoc: "List labels detected in the passed-in packages (or the package in the current directory if left blank).", + DocLink: "spec-labels", + Command: func(args []string, _ []string) { + ListLabels(args, cliConfig) + }, + } +} + +func ListLabels(args []string, cliConfig types.CLIConfig) { + suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) + if len(suites) == 0 { + command.AbortWith("Found no test suites") + } + for _, suite := range suites { + labels := fetchLabelsFromPackage(suite.Path) + if len(labels) == 0 { + fmt.Printf("%s: No labels found\n", suite.PackageName) + } else { + fmt.Printf("%s: [%s]\n", suite.PackageName, strings.Join(labels, ", ")) + } + } +} + +func fetchLabelsFromPackage(packagePath string) []string { + fset := token.NewFileSet() + parsedPackages, err := parser.ParseDir(fset, packagePath, nil, 0) + command.AbortIfError("Failed to parse package source:", err) + + files := []*ast.File{} + hasTestPackage := false + for key, pkg := range parsedPackages { + if strings.HasSuffix(key, "_test") { + hasTestPackage = true + for _, file := range pkg.Files { + files = append(files, file) + } + } + } + if !hasTestPackage { + for _, pkg := range parsedPackages { + for _, file := range pkg.Files { + files = append(files, file) + } + } + } + + seen := map[string]bool{} + labels := []string{} + ispr := inspector.New(files) + ispr.Preorder([]ast.Node{&ast.CallExpr{}}, func(n ast.Node) { + potentialLabels := fetchLabels(n.(*ast.CallExpr)) + for _, label := range potentialLabels { + if !seen[label] { + seen[label] = true + labels = append(labels, strconv.Quote(label)) + } + } + }) + + sort.Strings(labels) + return labels +} + +func fetchLabels(callExpr *ast.CallExpr) []string { + out := []string{} + switch expr := callExpr.Fun.(type) { + case *ast.Ident: + if expr.Name != "Label" { + return out + } + case *ast.SelectorExpr: + if expr.Sel.Name != "Label" { + return out + } + default: + return out + } + for _, arg := range callExpr.Args { + switch expr := arg.(type) { + case *ast.BasicLit: + if expr.Kind == token.STRING { + unquoted, err := strconv.Unquote(expr.Value) + if err != nil { + unquoted = expr.Value + } + validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{}) + if err == nil { + out = append(out, validated) + } + } + } + } + return out +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go new file mode 100644 index 000000000..e9abb27d8 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "os" + + "github.com/onsi/ginkgo/v2/ginkgo/build" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/generators" + "github.com/onsi/ginkgo/v2/ginkgo/labels" + "github.com/onsi/ginkgo/v2/ginkgo/outline" + "github.com/onsi/ginkgo/v2/ginkgo/run" + "github.com/onsi/ginkgo/v2/ginkgo/unfocus" + "github.com/onsi/ginkgo/v2/ginkgo/watch" + "github.com/onsi/ginkgo/v2/types" +) + +var program command.Program + +func GenerateCommands() []command.Command { + return []command.Command{ + watch.BuildWatchCommand(), + build.BuildBuildCommand(), + generators.BuildBootstrapCommand(), + generators.BuildGenerateCommand(), + labels.BuildLabelsCommand(), + outline.BuildOutlineCommand(), + unfocus.BuildUnfocusCommand(), + BuildVersionCommand(), + } +} + +func main() { + program = command.Program{ + Name: "ginkgo", + Heading: fmt.Sprintf("Ginkgo Version %s", types.VERSION), + Commands: GenerateCommands(), + DefaultCommand: run.BuildRunCommand(), + DeprecatedCommands: []command.DeprecatedCommand{ + {Name: "convert", Deprecation: types.Deprecations.Convert()}, + {Name: "blur", Deprecation: types.Deprecations.Blur()}, + {Name: "nodot", Deprecation: types.Deprecations.Nodot()}, + }, + } + + program.RunAndExit(os.Args) +} + +func BuildVersionCommand() command.Command { + return command.Command{ + Name: "version", + Usage: "ginkgo version", + ShortDoc: "Print Ginkgo's version", + Command: func(_ []string, _ []string) { + fmt.Printf("Ginkgo Version %s\n", types.VERSION) + }, + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go new file mode 100644 index 000000000..958daccbf --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go @@ -0,0 +1,300 @@ +package outline + +import ( + "github.com/onsi/ginkgo/v2/types" + "go/ast" + "go/token" + "strconv" +) + +const ( + // undefinedTextAlt is used if the spec/container text cannot be derived + undefinedTextAlt = "undefined" +) + +// ginkgoMetadata holds useful bits of information for every entry in the outline +type ginkgoMetadata struct { + // Name is the spec or container function name, e.g. `Describe` or `It` + Name string `json:"name"` + + // Text is the `text` argument passed to specs, and some containers + Text string `json:"text"` + + // Start is the position of first character of the spec or container block + Start int `json:"start"` + + // End is the position of first character immediately after the spec or container block + End int `json:"end"` + + Spec bool `json:"spec"` + Focused bool `json:"focused"` + Pending bool `json:"pending"` + Labels []string `json:"labels"` +} + +// ginkgoNode is used to construct the outline as a tree +type ginkgoNode struct { + ginkgoMetadata + Nodes []*ginkgoNode `json:"nodes"` +} + +type walkFunc func(n *ginkgoNode) + +func (n *ginkgoNode) PreOrder(f walkFunc) { + f(n) + for _, m := range n.Nodes { + m.PreOrder(f) + } +} + +func (n *ginkgoNode) PostOrder(f walkFunc) { + for _, m := range n.Nodes { + m.PostOrder(f) + } + f(n) +} + +func (n *ginkgoNode) Walk(pre, post walkFunc) { + pre(n) + for _, m := range n.Nodes { + m.Walk(pre, post) + } + post(n) +} + +// PropagateInheritedProperties propagates the Pending and Focused properties +// through the subtree rooted at n. +func (n *ginkgoNode) PropagateInheritedProperties() { + n.PreOrder(func(thisNode *ginkgoNode) { + for _, descendantNode := range thisNode.Nodes { + if thisNode.Pending { + descendantNode.Pending = true + descendantNode.Focused = false + } + if thisNode.Focused && !descendantNode.Pending { + descendantNode.Focused = true + } + } + }) +} + +// BackpropagateUnfocus propagates the Focused property through the subtree +// rooted at n. It applies the rule described in the Ginkgo docs: +// > Nested programmatically focused specs follow a simple rule: if a +// > leaf-node is marked focused, any of its ancestor nodes that are marked +// > focus will be unfocused. +func (n *ginkgoNode) BackpropagateUnfocus() { + focusedSpecInSubtreeStack := []bool{} + n.PostOrder(func(thisNode *ginkgoNode) { + if thisNode.Spec { + focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, thisNode.Focused) + return + } + focusedSpecInSubtree := false + for range thisNode.Nodes { + focusedSpecInSubtree = focusedSpecInSubtree || focusedSpecInSubtreeStack[len(focusedSpecInSubtreeStack)-1] + focusedSpecInSubtreeStack = focusedSpecInSubtreeStack[0 : len(focusedSpecInSubtreeStack)-1] + } + focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, focusedSpecInSubtree) + if focusedSpecInSubtree { + thisNode.Focused = false + } + }) + +} + +func packageAndIdentNamesFromCallExpr(ce *ast.CallExpr) (string, string, bool) { + switch ex := ce.Fun.(type) { + case *ast.Ident: + return "", ex.Name, true + case *ast.SelectorExpr: + pkgID, ok := ex.X.(*ast.Ident) + if !ok { + return "", "", false + } + // A package identifier is top-level, so Obj must be nil + if pkgID.Obj != nil { + return "", "", false + } + if ex.Sel == nil { + return "", "", false + } + return pkgID.Name, ex.Sel.Name, true + default: + return "", "", false + } +} + +// absoluteOffsetsForNode derives the absolute character offsets of the node start and +// end positions. +func absoluteOffsetsForNode(fset *token.FileSet, n ast.Node) (start, end int) { + return fset.PositionFor(n.Pos(), false).Offset, fset.PositionFor(n.End(), false).Offset +} + +// ginkgoNodeFromCallExpr derives an outline entry from a go AST subtree +// corresponding to a Ginkgo container or spec. +func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackageName *string) (*ginkgoNode, bool) { + packageName, identName, ok := packageAndIdentNamesFromCallExpr(ce) + if !ok { + return nil, false + } + + n := ginkgoNode{} + n.Name = identName + n.Start, n.End = absoluteOffsetsForNode(fset, ce) + n.Nodes = make([]*ginkgoNode, 0) + switch identName { + case "It", "Specify", "Entry": + n.Spec = true + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + n.Pending = pendingFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "FIt", "FSpecify", "FEntry": + n.Spec = true + n.Focused = true + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "PIt", "PSpecify", "XIt", "XSpecify", "PEntry", "XEntry": + n.Spec = true + n.Pending = true + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "Context", "Describe", "When", "DescribeTable": + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + n.Pending = pendingFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "FContext", "FDescribe", "FWhen", "FDescribeTable": + n.Focused = true + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable": + n.Pending = true + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + n.Labels = labelFromCallExpr(ce) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "By": + n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "AfterEach", "BeforeEach": + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "JustAfterEach", "JustBeforeEach": + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "AfterSuite", "BeforeSuite": + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + case "SynchronizedAfterSuite", "SynchronizedBeforeSuite": + return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName + default: + return nil, false + } +} + +// textOrAltFromCallExpr tries to derive the "text" of a Ginkgo spec or +// container. If it cannot derive it, it returns the alt text. +func textOrAltFromCallExpr(ce *ast.CallExpr, alt string) string { + text, defined := textFromCallExpr(ce) + if !defined { + return alt + } + return text +} + +// textFromCallExpr tries to derive the "text" of a Ginkgo spec or container. If +// it cannot derive it, it returns false. +func textFromCallExpr(ce *ast.CallExpr) (string, bool) { + if len(ce.Args) < 1 { + return "", false + } + text, ok := ce.Args[0].(*ast.BasicLit) + if !ok { + return "", false + } + switch text.Kind { + case token.CHAR, token.STRING: + // For token.CHAR and token.STRING, Value is quoted + unquoted, err := strconv.Unquote(text.Value) + if err != nil { + // If unquoting fails, just use the raw Value + return text.Value, true + } + return unquoted, true + default: + return text.Value, true + } +} + +func labelFromCallExpr(ce *ast.CallExpr) []string { + + labels := []string{} + if len(ce.Args) < 2 { + return labels + } + + for _, arg := range ce.Args[1:] { + switch expr := arg.(type) { + case *ast.CallExpr: + id, ok := expr.Fun.(*ast.Ident) + if !ok { + // to skip over cases where the expr.Fun. is actually *ast.SelectorExpr + continue + } + if id.Name == "Label" { + ls := extractLabels(expr) + labels = append(labels, ls...) + } + } + } + return labels +} + +func extractLabels(expr *ast.CallExpr) []string { + out := []string{} + for _, arg := range expr.Args { + switch expr := arg.(type) { + case *ast.BasicLit: + if expr.Kind == token.STRING { + unquoted, err := strconv.Unquote(expr.Value) + if err != nil { + unquoted = expr.Value + } + validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{}) + if err == nil { + out = append(out, validated) + } + } + } + } + + return out +} + +func pendingFromCallExpr(ce *ast.CallExpr) bool { + + pending := false + if len(ce.Args) < 2 { + return pending + } + + for _, arg := range ce.Args[1:] { + switch expr := arg.(type) { + case *ast.CallExpr: + id, ok := expr.Fun.(*ast.Ident) + if !ok { + // to skip over cases where the expr.Fun. is actually *ast.SelectorExpr + continue + } + if id.Name == "Pending" { + pending = true + } + case *ast.Ident: + if expr.Name == "Pending" { + pending = true + } + } + } + return pending +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go new file mode 100644 index 000000000..67ec5ab75 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go @@ -0,0 +1,65 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Most of the required functions were available in the +// "golang.org/x/tools/go/ast/astutil" package, but not exported. +// They were copied from https://github.com/golang/tools/blob/2b0845dc783e36ae26d683f4915a5840ef01ab0f/go/ast/astutil/imports.go + +package outline + +import ( + "go/ast" + "strconv" + "strings" +) + +// packageNameForImport returns the package name for the package. If the package +// is not imported, it returns nil. "Package name" refers to `pkgname` in the +// call expression `pkgname.ExportedIdentifier`. Examples: +// (import path not found) -> nil +// "import example.com/pkg/foo" -> "foo" +// "import fooalias example.com/pkg/foo" -> "fooalias" +// "import . example.com/pkg/foo" -> "" +func packageNameForImport(f *ast.File, path string) *string { + spec := importSpec(f, path) + if spec == nil { + return nil + } + name := spec.Name.String() + if name == "" { + // 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:] + } + } + if name == "." { + name = "" + } + return &name +} + +// importSpec returns the import spec if f imports path, +// or nil otherwise. +func importSpec(f *ast.File, path string) *ast.ImportSpec { + for _, s := range f.Imports { + if strings.HasPrefix(importPath(s), path) { + return s + } + } + return nil +} + +// importPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func importPath(s *ast.ImportSpec) string { + t, err := strconv.Unquote(s.Path.Value) + if err != nil { + return "" + } + return t +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go new file mode 100644 index 000000000..c2327cda8 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go @@ -0,0 +1,110 @@ +package outline + +import ( + "encoding/json" + "fmt" + "go/ast" + "go/token" + "strings" + + "golang.org/x/tools/go/ast/inspector" +) + +const ( + // ginkgoImportPath is the well-known ginkgo import path + ginkgoImportPath = "github.com/onsi/ginkgo/v2" +) + +// FromASTFile returns an outline for a Ginkgo test source file +func FromASTFile(fset *token.FileSet, src *ast.File) (*outline, error) { + ginkgoPackageName := packageNameForImport(src, ginkgoImportPath) + if ginkgoPackageName == nil { + return nil, fmt.Errorf("file does not import %q", ginkgoImportPath) + } + + root := ginkgoNode{} + stack := []*ginkgoNode{&root} + ispr := inspector.New([]*ast.File{src}) + ispr.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool) bool { + if push { + // Pre-order traversal + ce, ok := node.(*ast.CallExpr) + if !ok { + // Because `Nodes` calls this function only when the node is an + // ast.CallExpr, this should never happen + panic(fmt.Errorf("node starting at %d, ending at %d is not an *ast.CallExpr", node.Pos(), node.End())) + } + gn, ok := ginkgoNodeFromCallExpr(fset, ce, ginkgoPackageName) + if !ok { + // Node is not a Ginkgo spec or container, continue + return true + } + parent := stack[len(stack)-1] + parent.Nodes = append(parent.Nodes, gn) + stack = append(stack, gn) + return true + } + // Post-order traversal + start, end := absoluteOffsetsForNode(fset, node) + lastVisitedGinkgoNode := stack[len(stack)-1] + if start != lastVisitedGinkgoNode.Start || end != lastVisitedGinkgoNode.End { + // Node is not a Ginkgo spec or container, so it was not pushed onto the stack, continue + return true + } + stack = stack[0 : len(stack)-1] + return true + }) + if len(root.Nodes) == 0 { + return &outline{[]*ginkgoNode{}}, nil + } + + // Derive the final focused property for all nodes. This must be done + // _before_ propagating the inherited focused property. + root.BackpropagateUnfocus() + // Now, propagate inherited properties, including focused and pending. + root.PropagateInheritedProperties() + + return &outline{root.Nodes}, nil +} + +type outline struct { + Nodes []*ginkgoNode `json:"nodes"` +} + +func (o *outline) MarshalJSON() ([]byte, error) { + return json.Marshal(o.Nodes) +} + +// String returns a CSV-formatted outline. Spec or container are output in +// depth-first order. +func (o *outline) String() string { + return o.StringIndent(0) +} + +// 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 + b.WriteString("Name,Text,Start,End,Spec,Focused,Pending,Labels\n") + + currentIndent := 0 + pre := func(n *ginkgoNode) { + b.WriteString(fmt.Sprintf("%*s", currentIndent, "")) + var labels string + if len(n.Labels) == 1 { + labels = n.Labels[0] + } 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)) + currentIndent += width + } + post := func(n *ginkgoNode) { + currentIndent -= width + } + for _, n := range o.Nodes { + n.Walk(pre, post) + } + return b.String() +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go new file mode 100644 index 000000000..36698d46a --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go @@ -0,0 +1,98 @@ +package outline + +import ( + "encoding/json" + "fmt" + "go/parser" + "go/token" + "os" + + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/types" +) + +const ( + // indentWidth is the width used by the 'indent' output + indentWidth = 4 + // stdinAlias is a portable alias for stdin. This convention is used in + // other CLIs, e.g., kubectl. + stdinAlias = "-" + usageCommand = "ginkgo outline " +) + +type outlineConfig struct { + Format string +} + +func BuildOutlineCommand() command.Command { + conf := outlineConfig{ + Format: "csv", + } + flags, err := types.NewGinkgoFlagSet( + types.GinkgoFlags{ + {Name: "format", KeyPath: "Format", + Usage: "Format of outline", + UsageArgument: "one of 'csv', 'indent', or 'json'", + UsageDefaultValue: conf.Format, + }, + }, + &conf, + types.GinkgoFlagSections{}, + ) + if err != nil { + panic(err) + } + + return command.Command{ + Name: "outline", + Usage: "ginkgo outline ", + ShortDoc: "Create an outline of Ginkgo symbols for a file", + Documentation: "To read from stdin, use: `ginkgo outline -`", + DocLink: "creating-an-outline-of-specs", + Flags: flags, + Command: func(args []string, _ []string) { + outlineFile(args, conf.Format) + }, + } +} + +func outlineFile(args []string, format string) { + if len(args) != 1 { + command.AbortWithUsage("outline expects exactly one argument") + } + + filename := args[0] + var src *os.File + if filename == stdinAlias { + src = os.Stdin + } else { + var err error + src, err = os.Open(filename) + command.AbortIfError("Failed to open file:", err) + } + + fset := token.NewFileSet() + + parsedSrc, err := parser.ParseFile(fset, filename, src, 0) + command.AbortIfError("Failed to parse source:", err) + + o, err := FromASTFile(fset, parsedSrc) + command.AbortIfError("Failed to create outline:", err) + + var oerr error + switch format { + case "csv": + _, oerr = fmt.Print(o) + case "indent": + _, oerr = fmt.Print(o.StringIndent(indentWidth)) + case "json": + b, err := json.Marshal(o) + if err != nil { + println(fmt.Sprintf("error marshalling to json: %s", err)) + } + _, oerr = fmt.Println(string(b)) + default: + command.AbortWith("Format %s not accepted", format) + } + command.AbortIfError("Failed to write outline:", oerr) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go new file mode 100644 index 000000000..aaed4d570 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go @@ -0,0 +1,232 @@ +package run + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/internal/interrupt_handler" + "github.com/onsi/ginkgo/v2/types" +) + +func BuildRunCommand() command.Command { + var suiteConfig = types.NewDefaultSuiteConfig() + var reporterConfig = types.NewDefaultReporterConfig() + var cliConfig = types.NewDefaultCLIConfig() + var goFlagsConfig = types.NewDefaultGoFlagsConfig() + + flags, err := types.BuildRunCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig) + if err != nil { + panic(err) + } + + interruptHandler := interrupt_handler.NewInterruptHandler(nil) + interrupt_handler.SwallowSigQuit() + + return command.Command{ + Name: "run", + Flags: flags, + Usage: "ginkgo run -- ", + ShortDoc: "Run the tests in the passed in (or the package in the current directory if left blank)", + Documentation: "Any arguments after -- will be passed to the test.", + DocLink: "running-tests", + Command: func(args []string, additionalArgs []string) { + var errors []error + cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) + command.AbortIfErrors("Ginkgo detected configuration issues:", errors) + + runner := &SpecRunner{ + cliConfig: cliConfig, + goFlagsConfig: goFlagsConfig, + suiteConfig: suiteConfig, + reporterConfig: reporterConfig, + flags: flags, + + interruptHandler: interruptHandler, + } + + runner.RunSpecs(args, additionalArgs) + }, + } +} + +type SpecRunner struct { + suiteConfig types.SuiteConfig + reporterConfig types.ReporterConfig + cliConfig types.CLIConfig + goFlagsConfig types.GoFlagsConfig + flags types.GinkgoFlagSet + + interruptHandler *interrupt_handler.InterruptHandler +} + +func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) { + suites := internal.FindSuites(args, r.cliConfig, true) + skippedSuites := suites.WithState(internal.TestSuiteStateSkippedByFilter) + suites = suites.WithoutState(internal.TestSuiteStateSkippedByFilter) + + internal.VerifyCLIAndFrameworkVersion(suites) + + if len(skippedSuites) > 0 { + fmt.Println("Will skip:") + for _, skippedSuite := range skippedSuites { + fmt.Println(" " + skippedSuite.Path) + } + } + + if len(skippedSuites) > 0 && len(suites) == 0 { + command.AbortGracefullyWith("All tests skipped! Exiting...") + } + + if len(suites) == 0 { + command.AbortWith("Found no test suites") + } + + if len(suites) > 1 && !r.flags.WasSet("succinct") && r.reporterConfig.Verbosity().LT(types.VerbosityLevelVerbose) { + r.reporterConfig.Succinct = true + } + + t := time.Now() + var endTime time.Time + if r.suiteConfig.Timeout > 0 { + endTime = t.Add(r.suiteConfig.Timeout) + } + + iteration := 0 +OUTER_LOOP: + for { + if !r.flags.WasSet("seed") { + r.suiteConfig.RandomSeed = time.Now().Unix() + } + if r.cliConfig.RandomizeSuites && len(suites) > 1 { + suites = suites.ShuffledCopy(r.suiteConfig.RandomSeed) + } + + opc := internal.NewOrderedParallelCompiler(r.cliConfig.ComputedNumCompilers()) + opc.StartCompiling(suites, r.goFlagsConfig) + + SUITE_LOOP: + for { + suiteIdx, suite := opc.Next() + if suiteIdx >= len(suites) { + break SUITE_LOOP + } + suites[suiteIdx] = suite + + if r.interruptHandler.Status().Interrupted() { + opc.StopAndDrain() + break OUTER_LOOP + } + + if suites[suiteIdx].State.Is(internal.TestSuiteStateSkippedDueToEmptyCompilation) { + fmt.Printf("Skipping %s (no test files)\n", suite.Path) + continue SUITE_LOOP + } + + if suites[suiteIdx].State.Is(internal.TestSuiteStateFailedToCompile) { + fmt.Println(suites[suiteIdx].CompilationError.Error()) + if !r.cliConfig.KeepGoing { + opc.StopAndDrain() + } + continue SUITE_LOOP + } + + if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 && !r.cliConfig.KeepGoing { + suites[suiteIdx].State = internal.TestSuiteStateSkippedDueToPriorFailures + opc.StopAndDrain() + continue SUITE_LOOP + } + + if !endTime.IsZero() { + r.suiteConfig.Timeout = endTime.Sub(time.Now()) + if r.suiteConfig.Timeout <= 0 { + suites[suiteIdx].State = internal.TestSuiteStateFailedDueToTimeout + opc.StopAndDrain() + continue SUITE_LOOP + } + } + + suites[suiteIdx] = internal.RunCompiledSuite(suites[suiteIdx], r.suiteConfig, r.reporterConfig, r.cliConfig, r.goFlagsConfig, additionalArgs) + } + + if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { + if iteration > 0 { + fmt.Printf("\nTests failed on attempt #%d\n\n", iteration+1) + } + break OUTER_LOOP + } + + if r.cliConfig.UntilItFails { + fmt.Printf("\nAll tests passed...\nWill keep running them until they fail.\nThis was attempt #%d\n%s\n", iteration+1, orcMessage(iteration+1)) + } else if r.cliConfig.Repeat > 0 && iteration < r.cliConfig.Repeat { + fmt.Printf("\nAll tests passed...\nThis was attempt %d of %d.\n", iteration+1, r.cliConfig.Repeat+1) + } else { + break OUTER_LOOP + } + iteration += 1 + } + + internal.Cleanup(r.goFlagsConfig, suites...) + + messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, r.cliConfig, r.suiteConfig, r.reporterConfig, r.goFlagsConfig) + command.AbortIfError("could not finalize profiles:", err) + for _, message := range messages { + fmt.Println(message) + } + + fmt.Printf("\nGinkgo ran %d %s in %s\n", len(suites), internal.PluralizedWord("suite", "suites", len(suites)), time.Since(t)) + + if suites.CountWithState(internal.TestSuiteStateFailureStates...) == 0 { + if suites.AnyHaveProgrammaticFocus() && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { + fmt.Printf("Test Suite Passed\n") + fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE) + command.Abort(command.AbortDetails{ExitCode: types.GINKGO_FOCUS_EXIT_CODE}) + } else { + fmt.Printf("Test Suite Passed\n") + command.Abort(command.AbortDetails{}) + } + } else { + fmt.Fprintln(formatter.ColorableStdOut, "") + if len(suites) > 1 && suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { + fmt.Fprintln(formatter.ColorableStdOut, + internal.FailedSuitesReport(suites, formatter.NewWithNoColorBool(r.reporterConfig.NoColor))) + } + fmt.Printf("Test Suite Failed\n") + command.Abort(command.AbortDetails{ExitCode: 1}) + } +} + +func orcMessage(iteration int) string { + if iteration < 10 { + return "" + } else if iteration < 30 { + return []string{ + "If at first you succeed...", + "...try, try again.", + "Looking good!", + "Still good...", + "I think your tests are fine....", + "Yep, still passing", + "Oh boy, here I go testin' again!", + "Even the gophers are getting bored", + "Did you try -race?", + "Maybe you should stop now?", + "I'm getting tired...", + "What if I just made you a sandwich?", + "Hit ^C, hit ^C, please hit ^C", + "Make it stop. Please!", + "Come on! Enough is enough!", + "Dave, this conversation can serve no purpose anymore. Goodbye.", + "Just what do you think you're doing, Dave? ", + "I, Sisyphus", + "Insanity: doing the same thing over and over again and expecting different results. -Einstein", + "I guess Einstein never tried to churn butter", + }[iteration-10] + "\n" + } else { + return "No, seriously... you can probably stop now.\n" + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go new file mode 100644 index 000000000..7dd294394 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go @@ -0,0 +1,186 @@ +package unfocus + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/onsi/ginkgo/v2/ginkgo/command" +) + +func BuildUnfocusCommand() command.Command { + return command.Command{ + Name: "unfocus", + Usage: "ginkgo unfocus", + ShortDoc: "Recursively unfocus any focused tests under the current directory", + DocLink: "filtering-specs", + Command: func(_ []string, _ []string) { + unfocusSpecs() + }, + } +} + +func unfocusSpecs() { + fmt.Println("Scanning for focus...") + + goFiles := make(chan string) + go func() { + unfocusDir(goFiles, ".") + close(goFiles) + }() + + const workers = 10 + wg := sync.WaitGroup{} + wg.Add(workers) + + for i := 0; i < workers; i++ { + go func() { + for path := range goFiles { + unfocusFile(path) + } + wg.Done() + }() + } + + wg.Wait() +} + +func unfocusDir(goFiles chan string, path string) { + files, err := os.ReadDir(path) + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, f := range files { + switch { + case f.IsDir() && shouldProcessDir(f.Name()): + unfocusDir(goFiles, filepath.Join(path, f.Name())) + case !f.IsDir() && shouldProcessFile(f.Name()): + goFiles <- filepath.Join(path, f.Name()) + } + } +} + +func shouldProcessDir(basename string) bool { + return basename != "vendor" && !strings.HasPrefix(basename, ".") +} + +func shouldProcessFile(basename string) bool { + return strings.HasSuffix(basename, ".go") +} + +func unfocusFile(path string) { + data, err := os.ReadFile(path) + if err != nil { + fmt.Printf("error reading file '%s': %s\n", path, err.Error()) + return + } + + ast, err := parser.ParseFile(token.NewFileSet(), path, bytes.NewReader(data), parser.ParseComments) + if err != nil { + fmt.Printf("error parsing file '%s': %s\n", path, err.Error()) + return + } + + eliminations := scanForFocus(ast) + if len(eliminations) == 0 { + return + } + + fmt.Printf("...updating %s\n", path) + backup, err := writeBackup(path, data) + if err != nil { + fmt.Printf("error creating backup file: %s\n", err.Error()) + return + } + + if err := updateFile(path, data, eliminations); err != nil { + fmt.Printf("error writing file '%s': %s\n", path, err.Error()) + return + } + + os.Remove(backup) +} + +func writeBackup(path string, data []byte) (string, error) { + t, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)) + + if err != nil { + return "", fmt.Errorf("error creating temporary file: %w", err) + } + defer t.Close() + + if _, err := io.Copy(t, bytes.NewReader(data)); err != nil { + return "", fmt.Errorf("error writing to temporary file: %w", err) + } + + return t.Name(), nil +} + +func updateFile(path string, data []byte, eliminations [][]int64) error { + to, err := os.Create(path) + if err != nil { + return fmt.Errorf("error opening file for writing '%s': %w\n", path, err) + } + defer to.Close() + + from := bytes.NewReader(data) + var cursor int64 + for _, eliminationRange := range eliminations { + positionToEliminate, lengthToEliminate := eliminationRange[0]-1, eliminationRange[1] + if _, err := io.CopyN(to, from, positionToEliminate-cursor); err != nil { + return fmt.Errorf("error copying data: %w", err) + } + + cursor = positionToEliminate + lengthToEliminate + + if _, err := from.Seek(lengthToEliminate, io.SeekCurrent); err != nil { + return fmt.Errorf("error seeking to position in buffer: %w", err) + } + } + + if _, err := io.Copy(to, from); err != nil { + return fmt.Errorf("error copying end data: %w", err) + } + + return nil +} + +func scanForFocus(file *ast.File) (eliminations [][]int64) { + ast.Inspect(file, func(n ast.Node) bool { + if c, ok := n.(*ast.CallExpr); ok { + if i, ok := c.Fun.(*ast.Ident); ok { + if isFocus(i.Name) { + eliminations = append(eliminations, []int64{int64(i.Pos()), 1}) + } + } + } + + if i, ok := n.(*ast.Ident); ok { + if i.Name == "Focus" { + eliminations = append(eliminations, []int64{int64(i.Pos()), 6}) + } + } + + return true + }) + + return eliminations +} + +func isFocus(name string) bool { + switch name { + case "FDescribe", "FContext", "FIt", "FDescribeTable", "FEntry", "FSpecify", "FWhen": + return true + default: + return false + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go new file mode 100644 index 000000000..6c485c5b1 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go @@ -0,0 +1,22 @@ +package watch + +import "sort" + +type Delta struct { + ModifiedPackages []string + + NewSuites []*Suite + RemovedSuites []*Suite + modifiedSuites []*Suite +} + +type DescendingByDelta []*Suite + +func (a DescendingByDelta) Len() int { return len(a) } +func (a DescendingByDelta) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a DescendingByDelta) Less(i, j int) bool { return a[i].Delta() > a[j].Delta() } + +func (d Delta) ModifiedSuites() []*Suite { + sort.Sort(DescendingByDelta(d.modifiedSuites)) + return d.modifiedSuites +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go new file mode 100644 index 000000000..26418ac62 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go @@ -0,0 +1,75 @@ +package watch + +import ( + "fmt" + + "regexp" + + "github.com/onsi/ginkgo/v2/ginkgo/internal" +) + +type SuiteErrors map[internal.TestSuite]error + +type DeltaTracker struct { + maxDepth int + watchRegExp *regexp.Regexp + suites map[string]*Suite + packageHashes *PackageHashes +} + +func NewDeltaTracker(maxDepth int, watchRegExp *regexp.Regexp) *DeltaTracker { + return &DeltaTracker{ + maxDepth: maxDepth, + watchRegExp: watchRegExp, + packageHashes: NewPackageHashes(watchRegExp), + suites: map[string]*Suite{}, + } +} + +func (d *DeltaTracker) Delta(suites internal.TestSuites) (delta Delta, errors SuiteErrors) { + errors = SuiteErrors{} + delta.ModifiedPackages = d.packageHashes.CheckForChanges() + + providedSuitePaths := map[string]bool{} + for _, suite := range suites { + providedSuitePaths[suite.Path] = true + } + + d.packageHashes.StartTrackingUsage() + + for _, suite := range d.suites { + if providedSuitePaths[suite.Suite.Path] { + if suite.Delta() > 0 { + delta.modifiedSuites = append(delta.modifiedSuites, suite) + } + } else { + delta.RemovedSuites = append(delta.RemovedSuites, suite) + } + } + + d.packageHashes.StopTrackingUsageAndPrune() + + for _, suite := range suites { + _, ok := d.suites[suite.Path] + if !ok { + s, err := NewSuite(suite, d.maxDepth, d.packageHashes) + if err != nil { + errors[suite] = err + continue + } + d.suites[suite.Path] = s + delta.NewSuites = append(delta.NewSuites, s) + } + } + + return delta, errors +} + +func (d *DeltaTracker) WillRun(suite internal.TestSuite) error { + s, ok := d.suites[suite.Path] + if !ok { + return fmt.Errorf("unknown suite %s", suite.Path) + } + + return s.MarkAsRunAndRecomputedDependencies(d.maxDepth) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go new file mode 100644 index 000000000..f5ddff30f --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go @@ -0,0 +1,92 @@ +package watch + +import ( + "go/build" + "regexp" +) + +var ginkgoAndGomegaFilter = regexp.MustCompile(`github\.com/onsi/ginkgo|github\.com/onsi/gomega`) +var ginkgoIntegrationTestFilter = regexp.MustCompile(`github\.com/onsi/ginkgo/integration`) //allow us to integration test this thing + +type Dependencies struct { + deps map[string]int +} + +func NewDependencies(path string, maxDepth int) (Dependencies, error) { + d := Dependencies{ + deps: map[string]int{}, + } + + if maxDepth == 0 { + return d, nil + } + + err := d.seedWithDepsForPackageAtPath(path) + if err != nil { + return d, err + } + + for depth := 1; depth < maxDepth; depth++ { + n := len(d.deps) + d.addDepsForDepth(depth) + if n == len(d.deps) { + break + } + } + + return d, nil +} + +func (d Dependencies) Dependencies() map[string]int { + return d.deps +} + +func (d Dependencies) seedWithDepsForPackageAtPath(path string) error { + pkg, err := build.ImportDir(path, 0) + if err != nil { + return err + } + + d.resolveAndAdd(pkg.Imports, 1) + d.resolveAndAdd(pkg.TestImports, 1) + d.resolveAndAdd(pkg.XTestImports, 1) + + delete(d.deps, pkg.Dir) + return nil +} + +func (d Dependencies) addDepsForDepth(depth int) { + for dep, depDepth := range d.deps { + if depDepth == depth { + d.addDepsForDep(dep, depth+1) + } + } +} + +func (d Dependencies) addDepsForDep(dep string, depth int) { + pkg, err := build.ImportDir(dep, 0) + if err != nil { + println(err.Error()) + return + } + d.resolveAndAdd(pkg.Imports, depth) +} + +func (d Dependencies) resolveAndAdd(deps []string, depth int) { + for _, dep := range deps { + pkg, err := build.Import(dep, ".", 0) + if err != nil { + continue + } + if !pkg.Goroot && (!ginkgoAndGomegaFilter.Match([]byte(pkg.Dir)) || ginkgoIntegrationTestFilter.Match([]byte(pkg.Dir))) { + d.addDepIfNotPresent(pkg.Dir, depth) + } + } +} + +func (d Dependencies) addDepIfNotPresent(dep string, depth int) { + _, ok := d.deps[dep] + if !ok { + d.deps[dep] = depth + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go new file mode 100644 index 000000000..e9f7ec0cb --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go @@ -0,0 +1,108 @@ +package watch + +import ( + "fmt" + "os" + "regexp" + "time" +) + +var goTestRegExp = regexp.MustCompile(`_test\.go$`) + +type PackageHash struct { + CodeModifiedTime time.Time + TestModifiedTime time.Time + Deleted bool + + path string + codeHash string + testHash string + watchRegExp *regexp.Regexp +} + +func NewPackageHash(path string, watchRegExp *regexp.Regexp) *PackageHash { + p := &PackageHash{ + path: path, + watchRegExp: watchRegExp, + } + + p.codeHash, _, p.testHash, _, p.Deleted = p.computeHashes() + + return p +} + +func (p *PackageHash) CheckForChanges() bool { + codeHash, codeModifiedTime, testHash, testModifiedTime, deleted := p.computeHashes() + + if deleted { + if !p.Deleted { + t := time.Now() + p.CodeModifiedTime = t + p.TestModifiedTime = t + } + p.Deleted = true + return true + } + + modified := false + p.Deleted = false + + if p.codeHash != codeHash { + p.CodeModifiedTime = codeModifiedTime + modified = true + } + if p.testHash != testHash { + p.TestModifiedTime = testModifiedTime + modified = true + } + + p.codeHash = codeHash + p.testHash = testHash + return modified +} + +func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Time, testHash string, testModifiedTime time.Time, deleted bool) { + entries, err := os.ReadDir(p.path) + + if err != nil { + deleted = true + return + } + + for _, entry := range entries { + if entry.IsDir() { + continue + } + + info, err := entry.Info() + if err != nil { + continue + } + + if goTestRegExp.Match([]byte(info.Name())) { + testHash += p.hashForFileInfo(info) + if info.ModTime().After(testModifiedTime) { + testModifiedTime = info.ModTime() + } + continue + } + + if p.watchRegExp.Match([]byte(info.Name())) { + codeHash += p.hashForFileInfo(info) + if info.ModTime().After(codeModifiedTime) { + codeModifiedTime = info.ModTime() + } + } + } + + testHash += codeHash + if codeModifiedTime.After(testModifiedTime) { + testModifiedTime = codeModifiedTime + } + + return +} + +func (p *PackageHash) hashForFileInfo(info os.FileInfo) string { + return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano()) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go new file mode 100644 index 000000000..b4892bebf --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go @@ -0,0 +1,85 @@ +package watch + +import ( + "path/filepath" + "regexp" + "sync" +) + +type PackageHashes struct { + PackageHashes map[string]*PackageHash + usedPaths map[string]bool + watchRegExp *regexp.Regexp + lock *sync.Mutex +} + +func NewPackageHashes(watchRegExp *regexp.Regexp) *PackageHashes { + return &PackageHashes{ + PackageHashes: map[string]*PackageHash{}, + usedPaths: nil, + watchRegExp: watchRegExp, + lock: &sync.Mutex{}, + } +} + +func (p *PackageHashes) CheckForChanges() []string { + p.lock.Lock() + defer p.lock.Unlock() + + modified := []string{} + + for _, packageHash := range p.PackageHashes { + if packageHash.CheckForChanges() { + modified = append(modified, packageHash.path) + } + } + + return modified +} + +func (p *PackageHashes) Add(path string) *PackageHash { + p.lock.Lock() + defer p.lock.Unlock() + + path, _ = filepath.Abs(path) + _, ok := p.PackageHashes[path] + if !ok { + p.PackageHashes[path] = NewPackageHash(path, p.watchRegExp) + } + + if p.usedPaths != nil { + p.usedPaths[path] = true + } + return p.PackageHashes[path] +} + +func (p *PackageHashes) Get(path string) *PackageHash { + p.lock.Lock() + defer p.lock.Unlock() + + path, _ = filepath.Abs(path) + if p.usedPaths != nil { + p.usedPaths[path] = true + } + return p.PackageHashes[path] +} + +func (p *PackageHashes) StartTrackingUsage() { + p.lock.Lock() + defer p.lock.Unlock() + + p.usedPaths = map[string]bool{} +} + +func (p *PackageHashes) StopTrackingUsageAndPrune() { + p.lock.Lock() + defer p.lock.Unlock() + + for path := range p.PackageHashes { + if !p.usedPaths[path] { + delete(p.PackageHashes, path) + } + } + + p.usedPaths = nil +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go new file mode 100644 index 000000000..53272df7e --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go @@ -0,0 +1,87 @@ +package watch + +import ( + "fmt" + "math" + "time" + + "github.com/onsi/ginkgo/v2/ginkgo/internal" +) + +type Suite struct { + Suite internal.TestSuite + RunTime time.Time + Dependencies Dependencies + + sharedPackageHashes *PackageHashes +} + +func NewSuite(suite internal.TestSuite, maxDepth int, sharedPackageHashes *PackageHashes) (*Suite, error) { + deps, err := NewDependencies(suite.Path, maxDepth) + if err != nil { + return nil, err + } + + sharedPackageHashes.Add(suite.Path) + for dep := range deps.Dependencies() { + sharedPackageHashes.Add(dep) + } + + return &Suite{ + Suite: suite, + Dependencies: deps, + + sharedPackageHashes: sharedPackageHashes, + }, nil +} + +func (s *Suite) Delta() float64 { + delta := s.delta(s.Suite.Path, true, 0) * 1000 + for dep, depth := range s.Dependencies.Dependencies() { + delta += s.delta(dep, false, depth) + } + return delta +} + +func (s *Suite) MarkAsRunAndRecomputedDependencies(maxDepth int) error { + s.RunTime = time.Now() + + deps, err := NewDependencies(s.Suite.Path, maxDepth) + if err != nil { + return err + } + + s.sharedPackageHashes.Add(s.Suite.Path) + for dep := range deps.Dependencies() { + s.sharedPackageHashes.Add(dep) + } + + s.Dependencies = deps + + return nil +} + +func (s *Suite) Description() string { + numDeps := len(s.Dependencies.Dependencies()) + pluralizer := "ies" + if numDeps == 1 { + pluralizer = "y" + } + return fmt.Sprintf("%s [%d dependenc%s]", s.Suite.Path, numDeps, pluralizer) +} + +func (s *Suite) delta(packagePath string, includeTests bool, depth int) float64 { + return math.Max(float64(s.dt(packagePath, includeTests)), 0) / float64(depth+1) +} + +func (s *Suite) dt(packagePath string, includeTests bool) time.Duration { + packageHash := s.sharedPackageHashes.Get(packagePath) + var modifiedTime time.Time + if includeTests { + modifiedTime = packageHash.TestModifiedTime + } else { + modifiedTime = packageHash.CodeModifiedTime + } + + return modifiedTime.Sub(s.RunTime) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go new file mode 100644 index 000000000..bde4193ce --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go @@ -0,0 +1,192 @@ +package watch + +import ( + "fmt" + "regexp" + "time" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/ginkgo/command" + "github.com/onsi/ginkgo/v2/ginkgo/internal" + "github.com/onsi/ginkgo/v2/internal/interrupt_handler" + "github.com/onsi/ginkgo/v2/types" +) + +func BuildWatchCommand() command.Command { + var suiteConfig = types.NewDefaultSuiteConfig() + var reporterConfig = types.NewDefaultReporterConfig() + var cliConfig = types.NewDefaultCLIConfig() + var goFlagsConfig = types.NewDefaultGoFlagsConfig() + + flags, err := types.BuildWatchCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig) + if err != nil { + panic(err) + } + interruptHandler := interrupt_handler.NewInterruptHandler(nil) + interrupt_handler.SwallowSigQuit() + + return command.Command{ + Name: "watch", + Flags: flags, + Usage: "ginkgo watch -- ", + ShortDoc: "Watch the passed in and runs their tests whenever changes occur.", + Documentation: "Any arguments after -- will be passed to the test.", + DocLink: "watching-for-changes", + Command: func(args []string, additionalArgs []string) { + var errors []error + cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) + command.AbortIfErrors("Ginkgo detected configuration issues:", errors) + + watcher := &SpecWatcher{ + cliConfig: cliConfig, + goFlagsConfig: goFlagsConfig, + suiteConfig: suiteConfig, + reporterConfig: reporterConfig, + flags: flags, + + interruptHandler: interruptHandler, + } + + watcher.WatchSpecs(args, additionalArgs) + }, + } +} + +type SpecWatcher struct { + suiteConfig types.SuiteConfig + reporterConfig types.ReporterConfig + cliConfig types.CLIConfig + goFlagsConfig types.GoFlagsConfig + flags types.GinkgoFlagSet + + interruptHandler *interrupt_handler.InterruptHandler +} + +func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) { + suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) + + internal.VerifyCLIAndFrameworkVersion(suites) + + if len(suites) == 0 { + command.AbortWith("Found no test suites") + } + + fmt.Printf("Identified %d test %s. Locating dependencies to a depth of %d (this may take a while)...\n", len(suites), internal.PluralizedWord("suite", "suites", len(suites)), w.cliConfig.Depth) + deltaTracker := NewDeltaTracker(w.cliConfig.Depth, regexp.MustCompile(w.cliConfig.WatchRegExp)) + delta, errors := deltaTracker.Delta(suites) + + fmt.Printf("Watching %d %s:\n", len(delta.NewSuites), internal.PluralizedWord("suite", "suites", len(delta.NewSuites))) + for _, suite := range delta.NewSuites { + fmt.Println(" " + suite.Description()) + } + + for suite, err := range errors { + fmt.Printf("Failed to watch %s: %s\n", suite.PackageName, err) + } + + if len(suites) == 1 { + w.updateSeed() + w.compileAndRun(suites[0], additionalArgs) + } + + ticker := time.NewTicker(time.Second) + + for { + select { + case <-ticker.C: + suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) + delta, _ := deltaTracker.Delta(suites) + coloredStream := formatter.ColorableStdOut + + suites = internal.TestSuites{} + + if len(delta.NewSuites) > 0 { + fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected %d new %s:{{/}}", len(delta.NewSuites), internal.PluralizedWord("suite", "suites", len(delta.NewSuites)))) + for _, suite := range delta.NewSuites { + suites = append(suites, suite.Suite) + fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description())) + } + } + + modifiedSuites := delta.ModifiedSuites() + if len(modifiedSuites) > 0 { + fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected changes in:{{/}}")) + for _, pkg := range delta.ModifiedPackages { + fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", pkg)) + } + fmt.Fprintln(coloredStream, formatter.F("{{green}}Will run %d %s:{{/}}", len(modifiedSuites), internal.PluralizedWord("suite", "suites", len(modifiedSuites)))) + for _, suite := range modifiedSuites { + suites = append(suites, suite.Suite) + fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description())) + } + fmt.Fprintln(coloredStream, "") + } + + if len(suites) == 0 { + break + } + + w.updateSeed() + w.computeSuccinctMode(len(suites)) + for idx := range suites { + if w.interruptHandler.Status().Interrupted() { + return + } + deltaTracker.WillRun(suites[idx]) + suites[idx] = w.compileAndRun(suites[idx], additionalArgs) + } + color := "{{green}}" + if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { + color = "{{red}}" + } + fmt.Fprintln(coloredStream, formatter.F(color+"\nDone. Resuming watch...{{/}}")) + + messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, w.cliConfig, w.suiteConfig, w.reporterConfig, w.goFlagsConfig) + command.AbortIfError("could not finalize profiles:", err) + for _, message := range messages { + fmt.Println(message) + } + case <-w.interruptHandler.Status().Channel: + return + } + } +} + +func (w *SpecWatcher) compileAndRun(suite internal.TestSuite, additionalArgs []string) internal.TestSuite { + suite = internal.CompileSuite(suite, w.goFlagsConfig) + if suite.State.Is(internal.TestSuiteStateFailedToCompile) { + fmt.Println(suite.CompilationError.Error()) + return suite + } + if w.interruptHandler.Status().Interrupted() { + return suite + } + suite = internal.RunCompiledSuite(suite, w.suiteConfig, w.reporterConfig, w.cliConfig, w.goFlagsConfig, additionalArgs) + internal.Cleanup(w.goFlagsConfig, suite) + return suite +} + +func (w *SpecWatcher) computeSuccinctMode(numSuites int) { + if w.reporterConfig.Verbosity().GTE(types.VerbosityLevelVerbose) { + w.reporterConfig.Succinct = false + return + } + + if w.flags.WasSet("succinct") { + return + } + + if numSuites == 1 { + w.reporterConfig.Succinct = false + } + + if numSuites > 1 { + w.reporterConfig.Succinct = true + } +} + +func (w *SpecWatcher) updateSeed() { + if !w.flags.WasSet("seed") { + w.suiteConfig.RandomSeed = time.Now().Unix() + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go new file mode 100644 index 000000000..8ed86111f --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go @@ -0,0 +1,177 @@ +package interrupt_handler + +import ( + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/onsi/ginkgo/v2/internal/parallel_support" +) + +var ABORT_POLLING_INTERVAL = 500 * time.Millisecond + +type InterruptCause uint + +const ( + InterruptCauseInvalid InterruptCause = iota + InterruptCauseSignal + InterruptCauseAbortByOtherProcess +) + +type InterruptLevel uint + +const ( + InterruptLevelUninterrupted InterruptLevel = iota + InterruptLevelCleanupAndReport + InterruptLevelReportOnly + InterruptLevelBailOut +) + +func (ic InterruptCause) String() string { + switch ic { + case InterruptCauseSignal: + return "Interrupted by User" + case InterruptCauseAbortByOtherProcess: + return "Interrupted by Other Ginkgo Process" + } + return "INVALID_INTERRUPT_CAUSE" +} + +type InterruptStatus struct { + Channel chan interface{} + Level InterruptLevel + Cause InterruptCause +} + +func (s InterruptStatus) Interrupted() bool { + return s.Level != InterruptLevelUninterrupted +} + +func (s InterruptStatus) Message() string { + return s.Cause.String() +} + +func (s InterruptStatus) ShouldIncludeProgressReport() bool { + return s.Cause != InterruptCauseAbortByOtherProcess +} + +type InterruptHandlerInterface interface { + Status() InterruptStatus +} + +type InterruptHandler struct { + c chan interface{} + lock *sync.Mutex + level InterruptLevel + cause InterruptCause + client parallel_support.Client + stop chan interface{} + signals []os.Signal + requestAbortCheck chan interface{} +} + +func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *InterruptHandler { + if len(signals) == 0 { + signals = []os.Signal{os.Interrupt, syscall.SIGTERM} + } + handler := &InterruptHandler{ + c: make(chan interface{}), + lock: &sync.Mutex{}, + stop: make(chan interface{}), + requestAbortCheck: make(chan interface{}), + client: client, + signals: signals, + } + handler.registerForInterrupts() + return handler +} + +func (handler *InterruptHandler) Stop() { + close(handler.stop) +} + +func (handler *InterruptHandler) registerForInterrupts() { + // os signal handling + signalChannel := make(chan os.Signal, 1) + signal.Notify(signalChannel, handler.signals...) + + // cross-process abort handling + var abortChannel chan interface{} + if handler.client != nil { + abortChannel = make(chan interface{}) + go func() { + pollTicker := time.NewTicker(ABORT_POLLING_INTERVAL) + for { + select { + case <-pollTicker.C: + if handler.client.ShouldAbort() { + close(abortChannel) + pollTicker.Stop() + return + } + case <-handler.requestAbortCheck: + if handler.client.ShouldAbort() { + close(abortChannel) + pollTicker.Stop() + return + } + case <-handler.stop: + pollTicker.Stop() + return + } + } + }() + } + + go func(abortChannel chan interface{}) { + var interruptCause InterruptCause + for { + select { + case <-signalChannel: + interruptCause = InterruptCauseSignal + case <-abortChannel: + interruptCause = InterruptCauseAbortByOtherProcess + case <-handler.stop: + signal.Stop(signalChannel) + return + } + abortChannel = nil + + handler.lock.Lock() + oldLevel := handler.level + handler.cause = interruptCause + if handler.level == InterruptLevelUninterrupted { + handler.level = InterruptLevelCleanupAndReport + } else if handler.level == InterruptLevelCleanupAndReport { + handler.level = InterruptLevelReportOnly + } else if handler.level == InterruptLevelReportOnly { + handler.level = InterruptLevelBailOut + } + if handler.level != oldLevel { + close(handler.c) + handler.c = make(chan interface{}) + } + handler.lock.Unlock() + } + }(abortChannel) +} + +func (handler *InterruptHandler) Status() InterruptStatus { + handler.lock.Lock() + status := InterruptStatus{ + Level: handler.level, + Channel: handler.c, + Cause: handler.cause, + } + handler.lock.Unlock() + + if handler.client != nil && handler.client.ShouldAbort() && !status.Interrupted() { + close(handler.requestAbortCheck) + <-status.Channel + return handler.Status() + } + + return status +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go new file mode 100644 index 000000000..bf0de496d --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go @@ -0,0 +1,15 @@ +//go:build freebsd || openbsd || netbsd || dragonfly || darwin || linux || solaris +// +build freebsd openbsd netbsd dragonfly darwin linux solaris + +package interrupt_handler + +import ( + "os" + "os/signal" + "syscall" +) + +func SwallowSigQuit() { + c := make(chan os.Signal, 1024) + signal.Notify(c, syscall.SIGQUIT) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go new file mode 100644 index 000000000..fcf8da833 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go @@ -0,0 +1,8 @@ +//go:build windows +// +build windows + +package interrupt_handler + +func SwallowSigQuit() { + //noop +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go new file mode 100644 index 000000000..b3cd64292 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go @@ -0,0 +1,72 @@ +package parallel_support + +import ( + "fmt" + "io" + "os" + "time" + + "github.com/onsi/ginkgo/v2/reporters" + "github.com/onsi/ginkgo/v2/types" +) + +type BeforeSuiteState struct { + Data []byte + State types.SpecState +} + +type ParallelIndexCounter struct { + Index int +} + +var ErrorGone = fmt.Errorf("gone") +var ErrorFailed = fmt.Errorf("failed") +var ErrorEarly = fmt.Errorf("early") + +var POLLING_INTERVAL = 50 * time.Millisecond + +type Server interface { + Start() + Close() + Address() string + RegisterAlive(node int, alive func() bool) + GetSuiteDone() chan interface{} + GetOutputDestination() io.Writer + SetOutputDestination(io.Writer) +} + +type Client interface { + Connect() bool + Close() error + + PostSuiteWillBegin(report types.Report) error + PostDidRun(report types.SpecReport) error + PostSuiteDidEnd(report types.Report) error + PostReportBeforeSuiteCompleted(state types.SpecState) error + BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) + PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error + BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) + BlockUntilNonprimaryProcsHaveFinished() error + BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) + FetchNextCounter() (int, error) + PostAbort() error + ShouldAbort() bool + PostEmitProgressReport(report types.ProgressReport) error + Write(p []byte) (int, error) +} + +func NewServer(parallelTotal int, reporter reporters.Reporter) (Server, error) { + if os.Getenv("GINKGO_PARALLEL_PROTOCOL") == "HTTP" { + return newHttpServer(parallelTotal, reporter) + } else { + return newRPCServer(parallelTotal, reporter) + } +} + +func NewClient(serverHost string) Client { + if os.Getenv("GINKGO_PARALLEL_PROTOCOL") == "HTTP" { + return newHttpClient(serverHost) + } else { + return newRPCClient(serverHost) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go new file mode 100644 index 000000000..6547c7a66 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go @@ -0,0 +1,169 @@ +package parallel_support + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/onsi/ginkgo/v2/types" +) + +type httpClient struct { + serverHost string +} + +func newHttpClient(serverHost string) *httpClient { + return &httpClient{ + serverHost: serverHost, + } +} + +func (client *httpClient) Connect() bool { + resp, err := http.Get(client.serverHost + "/up") + if err != nil { + return false + } + resp.Body.Close() + return resp.StatusCode == http.StatusOK +} + +func (client *httpClient) Close() error { + return nil +} + +func (client *httpClient) post(path string, data interface{}) error { + var body io.Reader + if data != nil { + encoded, err := json.Marshal(data) + if err != nil { + return err + } + body = bytes.NewBuffer(encoded) + } + resp, err := http.Post(client.serverHost+path, "application/json", body) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("received unexpected status code %d", resp.StatusCode) + } + return nil +} + +func (client *httpClient) poll(path string, data interface{}) error { + for { + resp, err := http.Get(client.serverHost + path) + if err != nil { + return err + } + if resp.StatusCode == http.StatusTooEarly { + resp.Body.Close() + time.Sleep(POLLING_INTERVAL) + continue + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusGone { + return ErrorGone + } + if resp.StatusCode == http.StatusFailedDependency { + return ErrorFailed + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("received unexpected status code %d", resp.StatusCode) + } + if data != nil { + return json.NewDecoder(resp.Body).Decode(data) + } + return nil + } +} + +func (client *httpClient) PostSuiteWillBegin(report types.Report) error { + return client.post("/suite-will-begin", report) +} + +func (client *httpClient) PostDidRun(report types.SpecReport) error { + return client.post("/did-run", report) +} + +func (client *httpClient) PostSuiteDidEnd(report types.Report) error { + return client.post("/suite-did-end", report) +} + +func (client *httpClient) PostEmitProgressReport(report types.ProgressReport) error { + return client.post("/progress-report", report) +} + +func (client *httpClient) PostReportBeforeSuiteCompleted(state types.SpecState) error { + return client.post("/report-before-suite-completed", state) +} + +func (client *httpClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) { + var state types.SpecState + err := client.poll("/report-before-suite-state", &state) + if err == ErrorGone { + return types.SpecStateFailed, nil + } + return state, err +} + +func (client *httpClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { + beforeSuiteState := BeforeSuiteState{ + State: state, + Data: data, + } + return client.post("/before-suite-completed", beforeSuiteState) +} + +func (client *httpClient) BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) { + var beforeSuiteState BeforeSuiteState + err := client.poll("/before-suite-state", &beforeSuiteState) + if err == ErrorGone { + return types.SpecStateInvalid, nil, types.GinkgoErrors.SynchronizedBeforeSuiteDisappearedOnProc1() + } + return beforeSuiteState.State, beforeSuiteState.Data, err +} + +func (client *httpClient) BlockUntilNonprimaryProcsHaveFinished() error { + return client.poll("/have-nonprimary-procs-finished", nil) +} + +func (client *httpClient) BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) { + var report types.Report + err := client.poll("/aggregated-nonprimary-procs-report", &report) + if err == ErrorGone { + return types.Report{}, types.GinkgoErrors.AggregatedReportUnavailableDueToNodeDisappearing() + } + return report, err +} + +func (client *httpClient) FetchNextCounter() (int, error) { + var counter ParallelIndexCounter + err := client.poll("/counter", &counter) + return counter.Index, err +} + +func (client *httpClient) PostAbort() error { + return client.post("/abort", nil) +} + +func (client *httpClient) ShouldAbort() bool { + err := client.poll("/abort", nil) + if err == ErrorGone { + return true + } + return false +} + +func (client *httpClient) Write(p []byte) (int, error) { + resp, err := http.Post(client.serverHost+"/emit-output", "text/plain;charset=UTF-8 ", bytes.NewReader(p)) + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("failed to emit output") + } + return len(p), err +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go new file mode 100644 index 000000000..d2c71ab1b --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go @@ -0,0 +1,242 @@ +/* + +The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners. +This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser). + +*/ + +package parallel_support + +import ( + "encoding/json" + "io" + "net" + "net/http" + + "github.com/onsi/ginkgo/v2/reporters" + "github.com/onsi/ginkgo/v2/types" +) + +/* +httpServer spins up on an automatically selected port and listens for communication from the forwarding reporter. +It then forwards that communication to attached reporters. +*/ +type httpServer struct { + listener net.Listener + handler *ServerHandler +} + +// Create a new server, automatically selecting a port +func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + return &httpServer{ + listener: listener, + handler: newServerHandler(parallelTotal, reporter), + }, nil +} + +// Start the server. You don't need to `go s.Start()`, just `s.Start()` +func (server *httpServer) Start() { + httpServer := &http.Server{} + mux := http.NewServeMux() + httpServer.Handler = mux + + //streaming endpoints + mux.HandleFunc("/suite-will-begin", server.specSuiteWillBegin) + mux.HandleFunc("/did-run", server.didRun) + mux.HandleFunc("/suite-did-end", server.specSuiteDidEnd) + mux.HandleFunc("/emit-output", server.emitOutput) + mux.HandleFunc("/progress-report", server.emitProgressReport) + + //synchronization endpoints + mux.HandleFunc("/report-before-suite-completed", server.handleReportBeforeSuiteCompleted) + mux.HandleFunc("/report-before-suite-state", server.handleReportBeforeSuiteState) + mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted) + mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState) + mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished) + mux.HandleFunc("/aggregated-nonprimary-procs-report", server.handleAggregatedNonprimaryProcsReport) + mux.HandleFunc("/counter", server.handleCounter) + mux.HandleFunc("/up", server.handleUp) + mux.HandleFunc("/abort", server.handleAbort) + + go httpServer.Serve(server.listener) +} + +// Stop the server +func (server *httpServer) Close() { + server.listener.Close() +} + +// The address the server can be reached it. Pass this into the `ForwardingReporter`. +func (server *httpServer) Address() string { + return "http://" + server.listener.Addr().String() +} + +func (server *httpServer) GetSuiteDone() chan interface{} { + return server.handler.done +} + +func (server *httpServer) GetOutputDestination() io.Writer { + return server.handler.outputDestination +} + +func (server *httpServer) SetOutputDestination(w io.Writer) { + server.handler.outputDestination = w +} + +func (server *httpServer) RegisterAlive(node int, alive func() bool) { + server.handler.registerAlive(node, alive) +} + +// +// Streaming Endpoints +// + +// 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 { + defer request.Body.Close() + if json.NewDecoder(request.Body).Decode(object) != nil { + writer.WriteHeader(http.StatusBadRequest) + return false + } + return true +} + +func (server *httpServer) handleError(err error, writer http.ResponseWriter) bool { + if err == nil { + return false + } + switch err { + case ErrorEarly: + writer.WriteHeader(http.StatusTooEarly) + case ErrorGone: + writer.WriteHeader(http.StatusGone) + case ErrorFailed: + writer.WriteHeader(http.StatusFailedDependency) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + return true +} + +func (server *httpServer) specSuiteWillBegin(writer http.ResponseWriter, request *http.Request) { + var report types.Report + if !server.decode(writer, request, &report) { + return + } + + server.handleError(server.handler.SpecSuiteWillBegin(report, voidReceiver), writer) +} + +func (server *httpServer) didRun(writer http.ResponseWriter, request *http.Request) { + var report types.SpecReport + if !server.decode(writer, request, &report) { + return + } + + server.handleError(server.handler.DidRun(report, voidReceiver), writer) +} + +func (server *httpServer) specSuiteDidEnd(writer http.ResponseWriter, request *http.Request) { + var report types.Report + if !server.decode(writer, request, &report) { + return + } + server.handleError(server.handler.SpecSuiteDidEnd(report, voidReceiver), writer) +} + +func (server *httpServer) emitOutput(writer http.ResponseWriter, request *http.Request) { + output, err := io.ReadAll(request.Body) + if err != nil { + writer.WriteHeader(http.StatusInternalServerError) + return + } + var n int + server.handleError(server.handler.EmitOutput(output, &n), writer) +} + +func (server *httpServer) emitProgressReport(writer http.ResponseWriter, request *http.Request) { + var report types.ProgressReport + if !server.decode(writer, request, &report) { + return + } + server.handleError(server.handler.EmitProgressReport(report, voidReceiver), writer) +} + +func (server *httpServer) handleReportBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) { + var state types.SpecState + if !server.decode(writer, request, &state) { + return + } + + server.handleError(server.handler.ReportBeforeSuiteCompleted(state, voidReceiver), writer) +} + +func (server *httpServer) handleReportBeforeSuiteState(writer http.ResponseWriter, request *http.Request) { + var state types.SpecState + if server.handleError(server.handler.ReportBeforeSuiteState(voidSender, &state), writer) { + return + } + json.NewEncoder(writer).Encode(state) +} + +func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) { + var beforeSuiteState BeforeSuiteState + if !server.decode(writer, request, &beforeSuiteState) { + return + } + + server.handleError(server.handler.BeforeSuiteCompleted(beforeSuiteState, voidReceiver), writer) +} + +func (server *httpServer) handleBeforeSuiteState(writer http.ResponseWriter, request *http.Request) { + var beforeSuiteState BeforeSuiteState + if server.handleError(server.handler.BeforeSuiteState(voidSender, &beforeSuiteState), writer) { + return + } + json.NewEncoder(writer).Encode(beforeSuiteState) +} + +func (server *httpServer) handleHaveNonprimaryProcsFinished(writer http.ResponseWriter, request *http.Request) { + if server.handleError(server.handler.HaveNonprimaryProcsFinished(voidSender, voidReceiver), writer) { + return + } + writer.WriteHeader(http.StatusOK) +} + +func (server *httpServer) handleAggregatedNonprimaryProcsReport(writer http.ResponseWriter, request *http.Request) { + var aggregatedReport types.Report + if server.handleError(server.handler.AggregatedNonprimaryProcsReport(voidSender, &aggregatedReport), writer) { + return + } + json.NewEncoder(writer).Encode(aggregatedReport) +} + +func (server *httpServer) handleCounter(writer http.ResponseWriter, request *http.Request) { + var n int + if server.handleError(server.handler.Counter(voidSender, &n), writer) { + return + } + json.NewEncoder(writer).Encode(ParallelIndexCounter{Index: n}) +} + +func (server *httpServer) handleUp(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) +} + +func (server *httpServer) handleAbort(writer http.ResponseWriter, request *http.Request) { + if request.Method == "GET" { + var shouldAbort bool + server.handler.ShouldAbort(voidSender, &shouldAbort) + if shouldAbort { + writer.WriteHeader(http.StatusGone) + } else { + writer.WriteHeader(http.StatusOK) + } + } else { + server.handler.Abort(voidSender, voidReceiver) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go new file mode 100644 index 000000000..59e8e6fd0 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go @@ -0,0 +1,136 @@ +package parallel_support + +import ( + "net/rpc" + "time" + + "github.com/onsi/ginkgo/v2/types" +) + +type rpcClient struct { + serverHost string + client *rpc.Client +} + +func newRPCClient(serverHost string) *rpcClient { + return &rpcClient{ + serverHost: serverHost, + } +} + +func (client *rpcClient) Connect() bool { + var err error + if client.client != nil { + return true + } + client.client, err = rpc.DialHTTPPath("tcp", client.serverHost, "/") + if err != nil { + client.client = nil + return false + } + return true +} + +func (client *rpcClient) Close() error { + return client.client.Close() +} + +func (client *rpcClient) poll(method string, data interface{}) error { + for { + err := client.client.Call(method, voidSender, data) + if err == nil { + return nil + } + switch err.Error() { + case ErrorEarly.Error(): + time.Sleep(POLLING_INTERVAL) + case ErrorGone.Error(): + return ErrorGone + case ErrorFailed.Error(): + return ErrorFailed + default: + return err + } + } +} + +func (client *rpcClient) PostSuiteWillBegin(report types.Report) error { + return client.client.Call("Server.SpecSuiteWillBegin", report, voidReceiver) +} + +func (client *rpcClient) PostDidRun(report types.SpecReport) error { + return client.client.Call("Server.DidRun", report, voidReceiver) +} + +func (client *rpcClient) PostSuiteDidEnd(report types.Report) error { + return client.client.Call("Server.SpecSuiteDidEnd", report, voidReceiver) +} + +func (client *rpcClient) Write(p []byte) (int, error) { + var n int + err := client.client.Call("Server.EmitOutput", p, &n) + return n, err +} + +func (client *rpcClient) PostEmitProgressReport(report types.ProgressReport) error { + return client.client.Call("Server.EmitProgressReport", report, voidReceiver) +} + +func (client *rpcClient) PostReportBeforeSuiteCompleted(state types.SpecState) error { + return client.client.Call("Server.ReportBeforeSuiteCompleted", state, voidReceiver) +} + +func (client *rpcClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) { + var state types.SpecState + err := client.poll("Server.ReportBeforeSuiteState", &state) + if err == ErrorGone { + return types.SpecStateFailed, nil + } + return state, err +} + +func (client *rpcClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { + beforeSuiteState := BeforeSuiteState{ + State: state, + Data: data, + } + return client.client.Call("Server.BeforeSuiteCompleted", beforeSuiteState, voidReceiver) +} + +func (client *rpcClient) BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) { + var beforeSuiteState BeforeSuiteState + err := client.poll("Server.BeforeSuiteState", &beforeSuiteState) + if err == ErrorGone { + return types.SpecStateInvalid, nil, types.GinkgoErrors.SynchronizedBeforeSuiteDisappearedOnProc1() + } + return beforeSuiteState.State, beforeSuiteState.Data, err +} + +func (client *rpcClient) BlockUntilNonprimaryProcsHaveFinished() error { + return client.poll("Server.HaveNonprimaryProcsFinished", voidReceiver) +} + +func (client *rpcClient) BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) { + var report types.Report + err := client.poll("Server.AggregatedNonprimaryProcsReport", &report) + if err == ErrorGone { + return types.Report{}, types.GinkgoErrors.AggregatedReportUnavailableDueToNodeDisappearing() + } + return report, err +} + +func (client *rpcClient) FetchNextCounter() (int, error) { + var counter int + err := client.client.Call("Server.Counter", voidSender, &counter) + return counter, err +} + +func (client *rpcClient) PostAbort() error { + return client.client.Call("Server.Abort", voidSender, voidReceiver) +} + +func (client *rpcClient) ShouldAbort() bool { + var shouldAbort bool + client.client.Call("Server.ShouldAbort", voidSender, &shouldAbort) + return shouldAbort +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go new file mode 100644 index 000000000..2620fd562 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go @@ -0,0 +1,75 @@ +/* + +The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners. +This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser). + +*/ + +package parallel_support + +import ( + "io" + "net" + "net/http" + "net/rpc" + + "github.com/onsi/ginkgo/v2/reporters" +) + +/* +RPCServer spins up on an automatically selected port and listens for communication from the forwarding reporter. +It then forwards that communication to attached reporters. +*/ +type RPCServer struct { + listener net.Listener + handler *ServerHandler +} + +//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 { + return nil, err + } + return &RPCServer{ + listener: listener, + handler: newServerHandler(parallelTotal, reporter), + }, nil +} + +//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 + + httpServer := &http.Server{} + httpServer.Handler = rpcServer + + go httpServer.Serve(server.listener) +} + +//Stop the server +func (server *RPCServer) Close() { + server.listener.Close() +} + +//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{} { + return server.handler.done +} + +func (server *RPCServer) GetOutputDestination() io.Writer { + return server.handler.outputDestination +} + +func (server *RPCServer) SetOutputDestination(w io.Writer) { + server.handler.outputDestination = w +} + +func (server *RPCServer) RegisterAlive(node int, alive func() bool) { + server.handler.registerAlive(node, alive) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go new file mode 100644 index 000000000..a6d98793e --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go @@ -0,0 +1,234 @@ +package parallel_support + +import ( + "io" + "os" + "sync" + + "github.com/onsi/ginkgo/v2/reporters" + "github.com/onsi/ginkgo/v2/types" +) + +type Void struct{} + +var voidReceiver *Void = &Void{} +var voidSender Void + +// ServerHandler is an RPC-compatible handler that is shared between the http server and the rpc server. +// It handles all the business logic to avoid duplication between the two servers + +type ServerHandler struct { + done chan interface{} + outputDestination io.Writer + reporter reporters.Reporter + alives []func() bool + lock *sync.Mutex + beforeSuiteState BeforeSuiteState + reportBeforeSuiteState types.SpecState + parallelTotal int + counter int + counterLock *sync.Mutex + shouldAbort bool + + numSuiteDidBegins int + numSuiteDidEnds int + aggregatedReport types.Report + reportHoldingArea []types.SpecReport +} + +func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHandler { + return &ServerHandler{ + reporter: reporter, + lock: &sync.Mutex{}, + counterLock: &sync.Mutex{}, + alives: make([]func() bool, parallelTotal), + beforeSuiteState: BeforeSuiteState{Data: nil, State: types.SpecStateInvalid}, + + parallelTotal: parallelTotal, + outputDestination: os.Stdout, + done: make(chan interface{}), + } +} + +func (handler *ServerHandler) SpecSuiteWillBegin(report types.Report, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + + handler.numSuiteDidBegins += 1 + + // all summaries are identical, so it's fine to simply emit the last one of these + if handler.numSuiteDidBegins == handler.parallelTotal { + handler.reporter.SuiteWillBegin(report) + + for _, summary := range handler.reportHoldingArea { + handler.reporter.WillRun(summary) + handler.reporter.DidRun(summary) + } + + handler.reportHoldingArea = nil + } + + return nil +} + +func (handler *ServerHandler) DidRun(report types.SpecReport, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + + if handler.numSuiteDidBegins == handler.parallelTotal { + handler.reporter.WillRun(report) + handler.reporter.DidRun(report) + } else { + handler.reportHoldingArea = append(handler.reportHoldingArea, report) + } + + return nil +} + +func (handler *ServerHandler) SpecSuiteDidEnd(report types.Report, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + + handler.numSuiteDidEnds += 1 + if handler.numSuiteDidEnds == 1 { + handler.aggregatedReport = report + } else { + handler.aggregatedReport = handler.aggregatedReport.Add(report) + } + + if handler.numSuiteDidEnds == handler.parallelTotal { + handler.reporter.SuiteDidEnd(handler.aggregatedReport) + close(handler.done) + } + + return nil +} + +func (handler *ServerHandler) EmitOutput(output []byte, n *int) error { + var err error + *n, err = handler.outputDestination.Write(output) + return err +} + +func (handler *ServerHandler) EmitProgressReport(report types.ProgressReport, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + handler.reporter.EmitProgressReport(report) + return nil +} + +func (handler *ServerHandler) registerAlive(proc int, alive func() bool) { + handler.lock.Lock() + defer handler.lock.Unlock() + handler.alives[proc-1] = alive +} + +func (handler *ServerHandler) procIsAlive(proc int) bool { + handler.lock.Lock() + defer handler.lock.Unlock() + alive := handler.alives[proc-1] + if alive == nil { + return true + } + return alive() +} + +func (handler *ServerHandler) haveNonprimaryProcsFinished() bool { + for i := 2; i <= handler.parallelTotal; i++ { + if handler.procIsAlive(i) { + return false + } + } + return true +} + +func (handler *ServerHandler) ReportBeforeSuiteCompleted(reportBeforeSuiteState types.SpecState, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + handler.reportBeforeSuiteState = reportBeforeSuiteState + + return nil +} + +func (handler *ServerHandler) ReportBeforeSuiteState(_ Void, reportBeforeSuiteState *types.SpecState) error { + proc1IsAlive := handler.procIsAlive(1) + handler.lock.Lock() + defer handler.lock.Unlock() + if handler.reportBeforeSuiteState == types.SpecStateInvalid { + if proc1IsAlive { + return ErrorEarly + } else { + return ErrorGone + } + } + *reportBeforeSuiteState = handler.reportBeforeSuiteState + return nil +} + +func (handler *ServerHandler) BeforeSuiteCompleted(beforeSuiteState BeforeSuiteState, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + handler.beforeSuiteState = beforeSuiteState + + return nil +} + +func (handler *ServerHandler) BeforeSuiteState(_ Void, beforeSuiteState *BeforeSuiteState) error { + proc1IsAlive := handler.procIsAlive(1) + handler.lock.Lock() + defer handler.lock.Unlock() + if handler.beforeSuiteState.State == types.SpecStateInvalid { + if proc1IsAlive { + return ErrorEarly + } else { + return ErrorGone + } + } + *beforeSuiteState = handler.beforeSuiteState + return nil +} + +func (handler *ServerHandler) HaveNonprimaryProcsFinished(_ Void, _ *Void) error { + if handler.haveNonprimaryProcsFinished() { + return nil + } else { + return ErrorEarly + } +} + +func (handler *ServerHandler) AggregatedNonprimaryProcsReport(_ Void, report *types.Report) error { + if handler.haveNonprimaryProcsFinished() { + handler.lock.Lock() + defer handler.lock.Unlock() + if handler.numSuiteDidEnds == handler.parallelTotal-1 { + *report = handler.aggregatedReport + return nil + } else { + return ErrorGone + } + } else { + return ErrorEarly + } +} + +func (handler *ServerHandler) Counter(_ Void, counter *int) error { + handler.counterLock.Lock() + defer handler.counterLock.Unlock() + *counter = handler.counter + handler.counter++ + return nil +} + +func (handler *ServerHandler) Abort(_ Void, _ *Void) error { + handler.lock.Lock() + defer handler.lock.Unlock() + handler.shouldAbort = true + return nil +} + +func (handler *ServerHandler) ShouldAbort(_ Void, shouldAbort *bool) error { + handler.lock.Lock() + defer handler.lock.Unlock() + *shouldAbort = handler.shouldAbort + return nil +} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go b/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go new file mode 100644 index 000000000..56b7be758 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go @@ -0,0 +1,759 @@ +/* +Ginkgo's Default Reporter + +A number of command line flags are available to tweak Ginkgo's default output. + +These are documented [here](http://onsi.github.io/ginkgo/#running_tests) +*/ +package reporters + +import ( + "fmt" + "io" + "runtime" + "strings" + "sync" + "time" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/onsi/ginkgo/v2/types" +) + +type DefaultReporter struct { + conf types.ReporterConfig + writer io.Writer + + // managing the emission stream + lastCharWasNewline bool + lastEmissionWasDelimiter bool + + // rendering + specDenoter string + retryDenoter string + formatter formatter.Formatter + + runningInParallel bool + lock *sync.Mutex +} + +func NewDefaultReporterUnderTest(conf types.ReporterConfig, writer io.Writer) *DefaultReporter { + reporter := NewDefaultReporter(conf, writer) + reporter.formatter = formatter.New(formatter.ColorModePassthrough) + + return reporter +} + +func NewDefaultReporter(conf types.ReporterConfig, writer io.Writer) *DefaultReporter { + reporter := &DefaultReporter{ + conf: conf, + writer: writer, + + lastCharWasNewline: true, + lastEmissionWasDelimiter: false, + + specDenoter: "•", + retryDenoter: "↺", + formatter: formatter.NewWithNoColorBool(conf.NoColor), + lock: &sync.Mutex{}, + } + if runtime.GOOS == "windows" { + reporter.specDenoter = "+" + reporter.retryDenoter = "R" + } + + return reporter +} + +/* The Reporter Interface */ + +func (r *DefaultReporter) SuiteWillBegin(report types.Report) { + if r.conf.Verbosity().Is(types.VerbosityLevelSuccinct) { + r.emit(r.f("[%d] {{bold}}%s{{/}} ", report.SuiteConfig.RandomSeed, report.SuiteDescription)) + if len(report.SuiteLabels) > 0 { + r.emit(r.f("{{coral}}[%s]{{/}} ", strings.Join(report.SuiteLabels, ", "))) + } + r.emit(r.f("- %d/%d specs ", report.PreRunStats.SpecsThatWillRun, report.PreRunStats.TotalSpecs)) + if report.SuiteConfig.ParallelTotal > 1 { + r.emit(r.f("- %d procs ", report.SuiteConfig.ParallelTotal)) + } + } else { + banner := r.f("Running Suite: %s - %s", report.SuiteDescription, report.SuitePath) + r.emitBlock(banner) + bannerWidth := len(banner) + if len(report.SuiteLabels) > 0 { + labels := strings.Join(report.SuiteLabels, ", ") + r.emitBlock(r.f("{{coral}}[%s]{{/}} ", labels)) + if len(labels)+2 > bannerWidth { + bannerWidth = len(labels) + 2 + } + } + r.emitBlock(strings.Repeat("=", bannerWidth)) + + out := r.f("Random Seed: {{bold}}%d{{/}}", report.SuiteConfig.RandomSeed) + if report.SuiteConfig.RandomizeAllSpecs { + out += r.f(" - will randomize all specs") + } + r.emitBlock(out) + r.emit("\n") + r.emitBlock(r.f("Will run {{bold}}%d{{/}} of {{bold}}%d{{/}} specs", report.PreRunStats.SpecsThatWillRun, report.PreRunStats.TotalSpecs)) + if report.SuiteConfig.ParallelTotal > 1 { + r.emitBlock(r.f("Running in parallel across {{bold}}%d{{/}} processes", report.SuiteConfig.ParallelTotal)) + } + } +} + +func (r *DefaultReporter) SuiteDidEnd(report types.Report) { + failures := report.SpecReports.WithState(types.SpecStateFailureStates) + if len(failures) > 0 { + r.emitBlock("\n") + if len(failures) > 1 { + r.emitBlock(r.f("{{red}}{{bold}}Summarizing %d Failures:{{/}}", len(failures))) + } else { + r.emitBlock(r.f("{{red}}{{bold}}Summarizing 1 Failure:{{/}}")) + } + for _, specReport := range failures { + highlightColor, heading := "{{red}}", "[FAIL]" + switch specReport.State { + case types.SpecStatePanicked: + highlightColor, heading = "{{magenta}}", "[PANICKED!]" + case types.SpecStateAborted: + highlightColor, heading = "{{coral}}", "[ABORTED]" + case types.SpecStateTimedout: + highlightColor, heading = "{{orange}}", "[TIMEDOUT]" + case types.SpecStateInterrupted: + highlightColor, heading = "{{orange}}", "[INTERRUPTED]" + } + locationBlock := r.codeLocationBlock(specReport, highlightColor, false, true) + r.emitBlock(r.fi(1, highlightColor+"%s{{/}} %s", heading, locationBlock)) + } + } + + //summarize the suite + if r.conf.Verbosity().Is(types.VerbosityLevelSuccinct) && report.SuiteSucceeded { + r.emit(r.f(" {{green}}SUCCESS!{{/}} %s ", report.RunTime)) + return + } + + r.emitBlock("\n") + color, status := "{{green}}{{bold}}", "SUCCESS!" + if !report.SuiteSucceeded { + color, status = "{{red}}{{bold}}", "FAIL!" + } + + specs := report.SpecReports.WithLeafNodeType(types.NodeTypeIt) //exclude any suite setup nodes + r.emitBlock(r.f(color+"Ran %d of %d Specs in %.3f seconds{{/}}", + specs.CountWithState(types.SpecStatePassed)+specs.CountWithState(types.SpecStateFailureStates), + report.PreRunStats.TotalSpecs, + report.RunTime.Seconds()), + ) + + switch len(report.SpecialSuiteFailureReasons) { + case 0: + r.emit(r.f(color+"%s{{/}} -- ", status)) + case 1: + r.emit(r.f(color+"%s - %s{{/}} -- ", status, report.SpecialSuiteFailureReasons[0])) + default: + r.emitBlock(r.f(color+"%s - %s{{/}}\n", status, strings.Join(report.SpecialSuiteFailureReasons, ", "))) + } + + if len(specs) == 0 && report.SpecReports.WithLeafNodeType(types.NodeTypeBeforeSuite|types.NodeTypeSynchronizedBeforeSuite).CountWithState(types.SpecStateFailureStates) > 0 { + r.emit(r.f("{{cyan}}{{bold}}A BeforeSuite node failed so all tests were skipped.{{/}}\n")) + } else { + r.emit(r.f("{{green}}{{bold}}%d Passed{{/}} | ", specs.CountWithState(types.SpecStatePassed))) + r.emit(r.f("{{red}}{{bold}}%d Failed{{/}} | ", specs.CountWithState(types.SpecStateFailureStates))) + if specs.CountOfFlakedSpecs() > 0 { + r.emit(r.f("{{light-yellow}}{{bold}}%d Flaked{{/}} | ", specs.CountOfFlakedSpecs())) + } + if specs.CountOfRepeatedSpecs() > 0 { + r.emit(r.f("{{light-yellow}}{{bold}}%d Repeated{{/}} | ", specs.CountOfRepeatedSpecs())) + } + r.emit(r.f("{{yellow}}{{bold}}%d Pending{{/}} | ", specs.CountWithState(types.SpecStatePending))) + r.emit(r.f("{{cyan}}{{bold}}%d Skipped{{/}}\n", specs.CountWithState(types.SpecStateSkipped))) + } +} + +func (r *DefaultReporter) WillRun(report types.SpecReport) { + v := r.conf.Verbosity() + if v.LT(types.VerbosityLevelVerbose) || report.State.Is(types.SpecStatePending|types.SpecStateSkipped) || report.RunningInParallel { + return + } + + r.emitDelimiter(0) + r.emitBlock(r.f(r.codeLocationBlock(report, "{{/}}", v.Is(types.VerbosityLevelVeryVerbose), false))) +} + +func (r *DefaultReporter) DidRun(report types.SpecReport) { + v := r.conf.Verbosity() + inParallel := report.RunningInParallel + + header := r.specDenoter + if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { + header = fmt.Sprintf("[%s]", report.LeafNodeType) + } + highlightColor := r.highlightColorForState(report.State) + + // have we already been streaming the timeline? + timelineHasBeenStreaming := v.GTE(types.VerbosityLevelVerbose) && !inParallel + + // should we show the timeline? + var timeline types.Timeline + showTimeline := !timelineHasBeenStreaming && (v.GTE(types.VerbosityLevelVerbose) || report.Failed()) + if showTimeline { + timeline = report.Timeline().WithoutHiddenReportEntries() + keepVeryVerboseSpecEvents := v.Is(types.VerbosityLevelVeryVerbose) || + (v.Is(types.VerbosityLevelVerbose) && r.conf.ShowNodeEvents) || + (report.Failed() && r.conf.ShowNodeEvents) + if !keepVeryVerboseSpecEvents { + timeline = timeline.WithoutVeryVerboseSpecEvents() + } + if len(timeline) == 0 && report.CapturedGinkgoWriterOutput == "" { + // the timeline is completely empty - don't show it + showTimeline = false + } + if v.LT(types.VerbosityLevelVeryVerbose) && report.CapturedGinkgoWriterOutput == "" && len(timeline) > 0 { + //if we aren't -vv and the timeline only has a single failure, don't show it as it will appear at the end of the report + failure, isFailure := timeline[0].(types.Failure) + if isFailure && (len(timeline) == 1 || (len(timeline) == 2 && failure.AdditionalFailure != nil)) { + showTimeline = false + } + } + } + + // should we have a separate section for always-visible reports? + showSeparateVisibilityAlwaysReportsSection := !timelineHasBeenStreaming && !showTimeline && report.ReportEntries.HasVisibility(types.ReportEntryVisibilityAlways) + + // should we have a separate section for captured stdout/stderr + showSeparateStdSection := inParallel && (report.CapturedStdOutErr != "") + + // given all that - do we have any actual content to show? or are we a single denoter in a stream? + reportHasContent := v.Is(types.VerbosityLevelVeryVerbose) || showTimeline || showSeparateVisibilityAlwaysReportsSection || showSeparateStdSection || report.Failed() || (v.Is(types.VerbosityLevelVerbose) && !report.State.Is(types.SpecStateSkipped)) + + // should we show a runtime? + includeRuntime := !report.State.Is(types.SpecStateSkipped|types.SpecStatePending) || (report.State.Is(types.SpecStateSkipped) && report.Failure.Message != "") + + // should we show the codelocation block? + showCodeLocation := !timelineHasBeenStreaming || !report.State.Is(types.SpecStatePassed) + + switch report.State { + case types.SpecStatePassed: + if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) && !reportHasContent { + return + } + if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { + header = fmt.Sprintf("%s PASSED", header) + } + if report.NumAttempts > 1 && report.MaxFlakeAttempts > 1 { + header, reportHasContent = fmt.Sprintf("%s [FLAKEY TEST - TOOK %d ATTEMPTS TO PASS]", r.retryDenoter, report.NumAttempts), true + } + case types.SpecStatePending: + header = "P" + if v.GT(types.VerbosityLevelSuccinct) { + header, reportHasContent = "P [PENDING]", true + } + case types.SpecStateSkipped: + header = "S" + if v.Is(types.VerbosityLevelVeryVerbose) || (v.Is(types.VerbosityLevelVerbose) && report.Failure.Message != "") { + header, reportHasContent = "S [SKIPPED]", true + } + default: + header = fmt.Sprintf("%s [%s]", header, r.humanReadableState(report.State)) + if report.MaxMustPassRepeatedly > 1 { + header = fmt.Sprintf("%s DURING REPETITION #%d", header, report.NumAttempts) + } + } + + // If we have no content to show, jsut emit the header and return + if !reportHasContent { + r.emit(r.f(highlightColor + header + "{{/}}")) + return + } + + if includeRuntime { + header = r.f("%s [%.3f seconds]", header, report.RunTime.Seconds()) + } + + // Emit header + if !timelineHasBeenStreaming { + r.emitDelimiter(0) + } + r.emitBlock(r.f(highlightColor + header + "{{/}}")) + if showCodeLocation { + r.emitBlock(r.codeLocationBlock(report, highlightColor, v.Is(types.VerbosityLevelVeryVerbose), false)) + } + + //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{{/}}")) + } + + 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{{/}}")) + } + + if showTimeline { + r.emitBlock("\n") + r.emitBlock(r.fi(1, "{{gray}}Timeline >>{{/}}")) + r.emitTimeline(1, report, timeline) + r.emitBlock(r.fi(1, "{{gray}}<< Timeline{{/}}")) + } + + // Emit Failure Message + if !report.Failure.IsZero() && !v.Is(types.VerbosityLevelVeryVerbose) { + r.emitBlock("\n") + r.emitFailure(1, report.State, report.Failure, true) + if len(report.AdditionalFailures) > 0 { + r.emitBlock(r.fi(1, "\nThere were {{bold}}{{red}}additional failures{{/}} detected. To view them in detail run {{bold}}ginkgo -vv{{/}}")) + } + } + + r.emitDelimiter(0) +} + +func (r *DefaultReporter) highlightColorForState(state types.SpecState) string { + switch state { + case types.SpecStatePassed: + return "{{green}}" + case types.SpecStatePending: + return "{{yellow}}" + case types.SpecStateSkipped: + return "{{cyan}}" + case types.SpecStateFailed: + return "{{red}}" + case types.SpecStateTimedout: + return "{{orange}}" + case types.SpecStatePanicked: + return "{{magenta}}" + case types.SpecStateInterrupted: + return "{{orange}}" + case types.SpecStateAborted: + return "{{coral}}" + default: + return "{{gray}}" + } +} + +func (r *DefaultReporter) humanReadableState(state types.SpecState) string { + return strings.ToUpper(state.String()) +} + +func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, timeline types.Timeline) { + isVeryVerbose := r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose) + gw := report.CapturedGinkgoWriterOutput + cursor := 0 + for _, entry := range timeline { + tl := entry.GetTimelineLocation() + if tl.Offset < len(gw) { + r.emit(r.fi(indent, "%s", gw[cursor:tl.Offset])) + cursor = tl.Offset + } else if cursor < len(gw) { + r.emit(r.fi(indent, "%s", gw[cursor:])) + cursor = len(gw) + } + switch x := entry.(type) { + case types.Failure: + if isVeryVerbose { + r.emitFailure(indent, report.State, x, false) + } else { + r.emitShortFailure(indent, report.State, x) + } + case types.AdditionalFailure: + if isVeryVerbose { + r.emitFailure(indent, x.State, x.Failure, true) + } else { + r.emitShortFailure(indent, x.State, x.Failure) + } + case types.ReportEntry: + r.emitReportEntry(indent, x) + case types.ProgressReport: + r.emitProgressReport(indent, false, x) + case types.SpecEvent: + if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents { + r.emitSpecEvent(indent, x, isVeryVerbose) + } + } + } + if cursor < len(gw) { + r.emit(r.fi(indent, "%s", gw[cursor:])) + } +} + +func (r *DefaultReporter) EmitFailure(state types.SpecState, failure types.Failure) { + if r.conf.Verbosity().Is(types.VerbosityLevelVerbose) { + r.emitShortFailure(1, state, failure) + } else if r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose) { + r.emitFailure(1, state, failure, true) + } +} + +func (r *DefaultReporter) emitShortFailure(indent uint, state types.SpecState, failure types.Failure) { + r.emitBlock(r.fi(indent, r.highlightColorForState(state)+"[%s]{{/}} in [%s] - %s {{gray}}@ %s{{/}}", + r.humanReadableState(state), + failure.FailureNodeType, + failure.Location, + failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), + )) +} + +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 failure.ForwardedPanic != "" { + r.emitBlock("\n") + r.emitBlock(r.fi(indent, highlightColor+"%s{{/}}", failure.ForwardedPanic)) + } + + if r.conf.FullTrace || failure.ForwardedPanic != "" { + r.emitBlock("\n") + r.emitBlock(r.fi(indent, highlightColor+"Full Stack Trace{{/}}")) + r.emitBlock(r.fi(indent+1, "%s", failure.Location.FullStackTrace)) + } + + if !failure.ProgressReport.IsZero() { + r.emitBlock("\n") + r.emitProgressReport(indent, false, failure.ProgressReport) + } + + if failure.AdditionalFailure != nil && includeAdditionalFailure { + r.emitBlock("\n") + r.emitFailure(indent, failure.AdditionalFailure.State, failure.AdditionalFailure.Failure, true) + } +} + +func (r *DefaultReporter) EmitProgressReport(report types.ProgressReport) { + r.emitDelimiter(1) + + if report.RunningInParallel { + r.emit(r.fi(1, "{{coral}}Progress Report for Ginkgo Process #{{bold}}%d{{/}}\n", report.ParallelProcess)) + } + shouldEmitGW := report.RunningInParallel || r.conf.Verbosity().LT(types.VerbosityLevelVerbose) + r.emitProgressReport(1, shouldEmitGW, report) + r.emitDelimiter(1) +} + +func (r *DefaultReporter) emitProgressReport(indent uint, emitGinkgoWriterOutput bool, report types.ProgressReport) { + if report.Message != "" { + r.emitBlock(r.fi(indent, report.Message+"\n")) + indent += 1 + } + if report.LeafNodeText != "" { + subjectIndent := indent + if len(report.ContainerHierarchyTexts) > 0 { + r.emit(r.fi(indent, r.cycleJoin(report.ContainerHierarchyTexts, " "))) + r.emit(" ") + subjectIndent = 0 + } + r.emit(r.fi(subjectIndent, "{{bold}}{{orange}}%s{{/}} (Spec Runtime: %s)\n", report.LeafNodeText, report.Time().Sub(report.SpecStartTime).Round(time.Millisecond))) + r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.LeafNodeLocation)) + indent += 1 + } + if report.CurrentNodeType != types.NodeTypeInvalid { + r.emit(r.fi(indent, "In {{bold}}{{orange}}[%s]{{/}}", report.CurrentNodeType)) + if report.CurrentNodeText != "" && !report.CurrentNodeType.Is(types.NodeTypeIt) { + r.emit(r.f(" {{bold}}{{orange}}%s{{/}}", report.CurrentNodeText)) + } + + r.emit(r.f(" (Node Runtime: %s)\n", report.Time().Sub(report.CurrentNodeStartTime).Round(time.Millisecond))) + r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.CurrentNodeLocation)) + indent += 1 + } + if report.CurrentStepText != "" { + r.emit(r.fi(indent, "At {{bold}}{{orange}}[By Step] %s{{/}} (Step Runtime: %s)\n", report.CurrentStepText, report.Time().Sub(report.CurrentStepStartTime).Round(time.Millisecond))) + r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.CurrentStepLocation)) + indent += 1 + } + + if indent > 0 { + indent -= 1 + } + + if emitGinkgoWriterOutput && report.CapturedGinkgoWriterOutput != "" { + r.emit("\n") + r.emitBlock(r.fi(indent, "{{gray}}Begin Captured GinkgoWriter Output >>{{/}}")) + limit, lines := 10, strings.Split(report.CapturedGinkgoWriterOutput, "\n") + if len(lines) <= limit { + r.emitBlock(r.fi(indent+1, "%s", report.CapturedGinkgoWriterOutput)) + } else { + r.emitBlock(r.fi(indent+1, "{{gray}}...{{/}}")) + for _, line := range lines[len(lines)-limit-1:] { + r.emitBlock(r.fi(indent+1, "%s", line)) + } + } + r.emitBlock(r.fi(indent, "{{gray}}<< End Captured GinkgoWriter Output{{/}}")) + } + + if !report.SpecGoroutine().IsZero() { + r.emit("\n") + r.emit(r.fi(indent, "{{bold}}{{underline}}Spec Goroutine{{/}}\n")) + r.emitGoroutines(indent, report.SpecGoroutine()) + } + + if len(report.AdditionalReports) > 0 { + r.emit("\n") + r.emitBlock(r.fi(indent, "{{gray}}Begin Additional Progress Reports >>{{/}}")) + for i, additionalReport := range report.AdditionalReports { + r.emit(r.fi(indent+1, additionalReport)) + if i < len(report.AdditionalReports)-1 { + r.emitBlock(r.fi(indent+1, "{{gray}}%s{{/}}", strings.Repeat("-", 10))) + } + } + r.emitBlock(r.fi(indent, "{{gray}}<< End Additional Progress Reports{{/}}")) + } + + highlightedGoroutines := report.HighlightedGoroutines() + if len(highlightedGoroutines) > 0 { + r.emit("\n") + r.emit(r.fi(indent, "{{bold}}{{underline}}Goroutines of Interest{{/}}\n")) + r.emitGoroutines(indent, highlightedGoroutines...) + } + + otherGoroutines := report.OtherGoroutines() + if len(otherGoroutines) > 0 { + r.emit("\n") + r.emit(r.fi(indent, "{{gray}}{{bold}}{{underline}}Other Goroutines{{/}}\n")) + r.emitGoroutines(indent, otherGoroutines...) + } +} + +func (r *DefaultReporter) EmitReportEntry(entry types.ReportEntry) { + if r.conf.Verbosity().LT(types.VerbosityLevelVerbose) || entry.Visibility == types.ReportEntryVisibilityNever { + return + } + r.emitReportEntry(1, entry) +} + +func (r *DefaultReporter) emitReportEntry(indent uint, entry types.ReportEntry) { + r.emitBlock(r.fi(indent, "{{bold}}"+entry.Name+"{{gray}} "+fmt.Sprintf("- %s @ %s{{/}}", entry.Location, entry.Time.Format(types.GINKGO_TIME_FORMAT)))) + if representation := entry.StringRepresentation(); representation != "" { + r.emitBlock(r.fi(indent+1, representation)) + } +} + +func (r *DefaultReporter) EmitSpecEvent(event types.SpecEvent) { + v := r.conf.Verbosity() + if v.Is(types.VerbosityLevelVeryVerbose) || (v.Is(types.VerbosityLevelVerbose) && (r.conf.ShowNodeEvents || !event.IsOnlyVisibleAtVeryVerbose())) { + r.emitSpecEvent(1, event, r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose)) + } +} + +func (r *DefaultReporter) emitSpecEvent(indent uint, event types.SpecEvent, includeLocation bool) { + location := "" + if includeLocation { + location = fmt.Sprintf("- %s ", event.CodeLocation.String()) + } + switch event.SpecEventType { + case types.SpecEventInvalid: + return + case types.SpecEventByStart: + r.emitBlock(r.fi(indent, "{{bold}}STEP:{{/}} %s {{gray}}%s@ %s{{/}}", event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) + case types.SpecEventByEnd: + r.emitBlock(r.fi(indent, "{{bold}}END STEP:{{/}} %s {{gray}}%s@ %s (%s){{/}}", event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), event.Duration.Round(time.Millisecond))) + case types.SpecEventNodeStart: + r.emitBlock(r.fi(indent, "> Enter {{bold}}[%s]{{/}} %s {{gray}}%s@ %s{{/}}", event.NodeType.String(), event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) + case types.SpecEventNodeEnd: + r.emitBlock(r.fi(indent, "< Exit {{bold}}[%s]{{/}} %s {{gray}}%s@ %s (%s){{/}}", event.NodeType.String(), event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), event.Duration.Round(time.Millisecond))) + case types.SpecEventSpecRepeat: + r.emitBlock(r.fi(indent, "\n{{bold}}Attempt #%d {{green}}Passed{{/}}{{bold}}. Repeating %s{{/}} {{gray}}@ %s{{/}}\n\n", event.Attempt, r.retryDenoter, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) + case types.SpecEventSpecRetry: + r.emitBlock(r.fi(indent, "\n{{bold}}Attempt #%d {{red}}Failed{{/}}{{bold}}. Retrying %s{{/}} {{gray}}@ %s{{/}}\n\n", event.Attempt, r.retryDenoter, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) + } +} + +func (r *DefaultReporter) emitGoroutines(indent uint, goroutines ...types.Goroutine) { + for idx, g := range goroutines { + color := "{{gray}}" + if g.HasHighlights() { + color = "{{orange}}" + } + r.emit(r.fi(indent, color+"goroutine %d [%s]{{/}}\n", g.ID, g.State)) + for _, fc := range g.Stack { + if fc.Highlight { + r.emit(r.fi(indent, color+"{{bold}}> %s{{/}}\n", fc.Function)) + r.emit(r.fi(indent+2, color+"{{bold}}%s:%d{{/}}\n", fc.Filename, fc.Line)) + r.emitSource(indent+3, fc) + } else { + r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", fc.Function)) + r.emit(r.fi(indent+2, "{{gray}}%s:%d{{/}}\n", fc.Filename, fc.Line)) + } + } + + if idx+1 < len(goroutines) { + r.emit("\n") + } + } +} + +func (r *DefaultReporter) emitSource(indent uint, fc types.FunctionCall) { + lines := fc.Source + if len(lines) == 0 { + return + } + + lTrim := 100000 + for _, line := range lines { + lTrimLine := len(line) - len(strings.TrimLeft(line, " \t")) + if lTrimLine < lTrim && len(line) > 0 { + lTrim = lTrimLine + } + } + if lTrim == 100000 { + lTrim = 0 + } + + for idx, line := range lines { + if len(line) > lTrim { + line = line[lTrim:] + } + if idx == fc.SourceHighlight { + r.emit(r.fi(indent, "{{bold}}{{orange}}> %s{{/}}\n", line)) + } else { + r.emit(r.fi(indent, "| %s\n", line)) + } + } +} + +/* Emitting to the writer */ +func (r *DefaultReporter) emit(s string) { + r._emit(s, false, false) +} + +func (r *DefaultReporter) emitBlock(s string) { + r._emit(s, true, false) +} + +func (r *DefaultReporter) emitDelimiter(indent uint) { + r._emit(r.fi(indent, "{{gray}}%s{{/}}", strings.Repeat("-", 30)), true, true) +} + +// a bit ugly - but we're trying to minimize locking on this hot codepath +func (r *DefaultReporter) _emit(s string, block bool, isDelimiter bool) { + if len(s) == 0 { + return + } + r.lock.Lock() + defer r.lock.Unlock() + if isDelimiter && r.lastEmissionWasDelimiter { + return + } + if block && !r.lastCharWasNewline { + r.writer.Write([]byte("\n")) + } + r.lastCharWasNewline = (s[len(s)-1:] == "\n") + r.writer.Write([]byte(s)) + if block && !r.lastCharWasNewline { + r.writer.Write([]byte("\n")) + r.lastCharWasNewline = true + } + r.lastEmissionWasDelimiter = isDelimiter +} + +/* Rendering text */ +func (r *DefaultReporter) f(format string, args ...interface{}) string { + return r.formatter.F(format, args...) +} + +func (r *DefaultReporter) fi(indentation uint, format string, args ...interface{}) string { + return r.formatter.Fi(indentation, format, args...) +} + +func (r *DefaultReporter) cycleJoin(elements []string, joiner string) string { + return r.formatter.CycleJoin(elements, joiner, []string{"{{/}}", "{{gray}}"}) +} + +func (r *DefaultReporter) codeLocationBlock(report types.SpecReport, highlightColor string, veryVerbose bool, usePreciseFailureLocation bool) string { + texts, locations, labels := []string{}, []types.CodeLocation{}, [][]string{} + texts, locations, labels = append(texts, report.ContainerHierarchyTexts...), append(locations, report.ContainerHierarchyLocations...), append(labels, report.ContainerHierarchyLabels...) + + if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { + texts = append(texts, r.f("[%s] %s", report.LeafNodeType, report.LeafNodeText)) + } else { + texts = append(texts, r.f(report.LeafNodeText)) + } + labels = append(labels, report.LeafNodeLabels) + locations = append(locations, report.LeafNodeLocation) + + failureLocation := report.Failure.FailureNodeLocation + if usePreciseFailureLocation { + failureLocation = report.Failure.Location + } + + highlightIndex := -1 + switch report.Failure.FailureNodeContext { + case types.FailureNodeAtTopLevel: + texts = append([]string{fmt.Sprintf("TOP-LEVEL [%s]", report.Failure.FailureNodeType)}, texts...) + locations = append([]types.CodeLocation{failureLocation}, locations...) + labels = append([][]string{{}}, labels...) + highlightIndex = 0 + case types.FailureNodeInContainer: + i := report.Failure.FailureNodeContainerIndex + texts[i] = fmt.Sprintf("%s [%s]", texts[i], report.Failure.FailureNodeType) + locations[i] = failureLocation + highlightIndex = i + case types.FailureNodeIsLeafNode: + i := len(texts) - 1 + texts[i] = fmt.Sprintf("[%s] %s", report.LeafNodeType, report.LeafNodeText) + locations[i] = failureLocation + highlightIndex = i + default: + //there is no failure, so we highlight the leaf ndoe + highlightIndex = len(texts) - 1 + } + + out := "" + if veryVerbose { + for i := range texts { + if i == highlightIndex { + out += r.fi(uint(i), highlightColor+"{{bold}}%s{{/}}", texts[i]) + } else { + out += r.fi(uint(i), "%s", texts[i]) + } + if len(labels[i]) > 0 { + out += r.f(" {{coral}}[%s]{{/}}", strings.Join(labels[i], ", ")) + } + out += "\n" + out += r.fi(uint(i), "{{gray}}%s{{/}}\n", locations[i]) + } + } else { + for i := range texts { + style := "{{/}}" + if i%2 == 1 { + style = "{{gray}}" + } + if i == highlightIndex { + style = highlightColor + "{{bold}}" + } + out += r.f(style+"%s", texts[i]) + if i < len(texts)-1 { + out += " " + } else { + out += r.f("{{/}}") + } + } + flattenedLabels := report.Labels() + if len(flattenedLabels) > 0 { + out += r.f(" {{coral}}[%s]{{/}}", strings.Join(flattenedLabels, ", ")) + } + out += "\n" + if usePreciseFailureLocation { + out += r.f("{{gray}}%s{{/}}", failureLocation) + } else { + leafLocation := locations[len(locations)-1] + if (report.Failure.FailureNodeLocation != types.CodeLocation{}) && (report.Failure.FailureNodeLocation != leafLocation) { + out += r.fi(1, highlightColor+"[%s]{{/}} {{gray}}%s{{/}}\n", report.Failure.FailureNodeType, report.Failure.FailureNodeLocation) + out += r.fi(1, "{{gray}}[%s] %s{{/}}", report.LeafNodeType, leafLocation) + } else { + out += r.f("{{gray}}%s{{/}}", leafLocation) + } + } + + } + return out +} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go b/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go new file mode 100644 index 000000000..613072ebf --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go @@ -0,0 +1,149 @@ +package reporters + +import ( + "github.com/onsi/ginkgo/v2/config" + "github.com/onsi/ginkgo/v2/types" +) + +// Deprecated: DeprecatedReporter was how Ginkgo V1 provided support for CustomReporters +// this has been removed in V2. +// Please read the documentation at: +// https://onsi.github.io/ginkgo/MIGRATING_TO_V2#removed-custom-reporters +// for Ginkgo's new behavior and for a migration path. +type DeprecatedReporter interface { + SuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) + BeforeSuiteDidRun(setupSummary *types.SetupSummary) + SpecWillRun(specSummary *types.SpecSummary) + SpecDidComplete(specSummary *types.SpecSummary) + AfterSuiteDidRun(setupSummary *types.SetupSummary) + SuiteDidEnd(summary *types.SuiteSummary) +} + +// ReportViaDeprecatedReporter takes a V1 custom reporter and a V2 report and +// calls the custom reporter's methods with appropriately transformed data from the V2 report. +// +// ReportViaDeprecatedReporter should be called in a `ReportAfterSuite()` +// +// Deprecated: ReportViaDeprecatedReporter method exists to help developer bridge between deprecated V1 functionality and the new +// reporting support in V2. It will be removed in a future minor version of Ginkgo. +func ReportViaDeprecatedReporter(reporter DeprecatedReporter, report types.Report) { + conf := config.DeprecatedGinkgoConfigType{ + RandomSeed: report.SuiteConfig.RandomSeed, + RandomizeAllSpecs: report.SuiteConfig.RandomizeAllSpecs, + FocusStrings: report.SuiteConfig.FocusStrings, + SkipStrings: report.SuiteConfig.SkipStrings, + FailOnPending: report.SuiteConfig.FailOnPending, + FailFast: report.SuiteConfig.FailFast, + FlakeAttempts: report.SuiteConfig.FlakeAttempts, + EmitSpecProgress: false, + DryRun: report.SuiteConfig.DryRun, + ParallelNode: report.SuiteConfig.ParallelProcess, + ParallelTotal: report.SuiteConfig.ParallelTotal, + SyncHost: report.SuiteConfig.ParallelHost, + StreamHost: report.SuiteConfig.ParallelHost, + } + + summary := &types.DeprecatedSuiteSummary{ + SuiteDescription: report.SuiteDescription, + SuiteID: report.SuitePath, + + NumberOfSpecsBeforeParallelization: report.PreRunStats.TotalSpecs, + NumberOfTotalSpecs: report.PreRunStats.TotalSpecs, + NumberOfSpecsThatWillBeRun: report.PreRunStats.SpecsThatWillRun, + } + + reporter.SuiteWillBegin(conf, summary) + + for _, spec := range report.SpecReports { + switch spec.LeafNodeType { + case types.NodeTypeBeforeSuite, types.NodeTypeSynchronizedBeforeSuite: + setupSummary := &types.DeprecatedSetupSummary{ + ComponentType: spec.LeafNodeType, + CodeLocation: spec.LeafNodeLocation, + State: spec.State, + RunTime: spec.RunTime, + Failure: failureFor(spec), + CapturedOutput: spec.CombinedOutput(), + SuiteID: report.SuitePath, + } + reporter.BeforeSuiteDidRun(setupSummary) + case types.NodeTypeAfterSuite, types.NodeTypeSynchronizedAfterSuite: + setupSummary := &types.DeprecatedSetupSummary{ + ComponentType: spec.LeafNodeType, + CodeLocation: spec.LeafNodeLocation, + State: spec.State, + RunTime: spec.RunTime, + Failure: failureFor(spec), + CapturedOutput: spec.CombinedOutput(), + SuiteID: report.SuitePath, + } + reporter.AfterSuiteDidRun(setupSummary) + case types.NodeTypeIt: + componentTexts, componentCodeLocations := []string{}, []types.CodeLocation{} + componentTexts = append(componentTexts, spec.ContainerHierarchyTexts...) + componentCodeLocations = append(componentCodeLocations, spec.ContainerHierarchyLocations...) + componentTexts = append(componentTexts, spec.LeafNodeText) + componentCodeLocations = append(componentCodeLocations, spec.LeafNodeLocation) + + specSummary := &types.DeprecatedSpecSummary{ + ComponentTexts: componentTexts, + ComponentCodeLocations: componentCodeLocations, + State: spec.State, + RunTime: spec.RunTime, + Failure: failureFor(spec), + NumberOfSamples: spec.NumAttempts, + CapturedOutput: spec.CombinedOutput(), + SuiteID: report.SuitePath, + } + reporter.SpecWillRun(specSummary) + reporter.SpecDidComplete(specSummary) + + switch spec.State { + case types.SpecStatePending: + summary.NumberOfPendingSpecs += 1 + case types.SpecStateSkipped: + summary.NumberOfSkippedSpecs += 1 + case types.SpecStateFailed, types.SpecStatePanicked, types.SpecStateInterrupted: + summary.NumberOfFailedSpecs += 1 + case types.SpecStatePassed: + summary.NumberOfPassedSpecs += 1 + if spec.NumAttempts > 1 { + summary.NumberOfFlakedSpecs += 1 + } + } + } + } + + summary.SuiteSucceeded = report.SuiteSucceeded + summary.RunTime = report.RunTime + + reporter.SuiteDidEnd(summary) +} + +func failureFor(spec types.SpecReport) types.DeprecatedSpecFailure { + if spec.Failure.IsZero() { + return types.DeprecatedSpecFailure{} + } + + index := 0 + switch spec.Failure.FailureNodeContext { + case types.FailureNodeInContainer: + index = spec.Failure.FailureNodeContainerIndex + case types.FailureNodeAtTopLevel: + index = -1 + case types.FailureNodeIsLeafNode: + index = len(spec.ContainerHierarchyTexts) - 1 + if spec.LeafNodeText != "" { + index += 1 + } + } + + return types.DeprecatedSpecFailure{ + Message: spec.Failure.Message, + Location: spec.Failure.Location, + ForwardedPanic: spec.Failure.ForwardedPanic, + ComponentIndex: index, + ComponentType: spec.Failure.FailureNodeType, + ComponentCodeLocation: spec.Failure.FailureNodeLocation, + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go b/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go new file mode 100644 index 000000000..be506f9b4 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go @@ -0,0 +1,67 @@ +package reporters + +import ( + "encoding/json" + "fmt" + "os" + "path" + + "github.com/onsi/ginkgo/v2/types" +) + +// GenerateJSONReport produces a JSON-formatted report at the passed in destination +func GenerateJSONReport(report types.Report, destination string) error { + if err := os.MkdirAll(path.Dir(destination), 0770); err != nil { + return err + } + f, err := os.Create(destination) + if err != nil { + return err + } + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + err = enc.Encode([]types.Report{ + report, + }) + if err != nil { + return err + } + return f.Close() +} + +// MergeJSONReports produces a single JSON-formatted report at the passed in destination by merging the JSON-formatted reports provided in sources +// It skips over reports that fail to decode but reports on them via the returned messages []string +func MergeAndCleanupJSONReports(sources []string, destination string) ([]string, error) { + messages := []string{} + allReports := []types.Report{} + for _, source := range sources { + reports := []types.Report{} + data, err := os.ReadFile(source) + if err != nil { + messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) + continue + } + err = json.Unmarshal(data, &reports) + if err != nil { + messages = append(messages, fmt.Sprintf("Could not decode %s:\n%s", source, err.Error())) + continue + } + os.Remove(source) + allReports = append(allReports, reports...) + } + + if err := os.MkdirAll(path.Dir(destination), 0770); err != nil { + return messages, err + } + f, err := os.Create(destination) + if err != nil { + return messages, err + } + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + err = enc.Encode(allReports) + if err != nil { + return messages, err + } + return messages, f.Close() +} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go new file mode 100644 index 000000000..816042208 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go @@ -0,0 +1,376 @@ +/* + +JUnit XML Reporter for Ginkgo + +For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output + +The schema used for the generated JUnit xml file was adapted from https://llg.cubic.org/docs/junit/ + +*/ + +package reporters + +import ( + "encoding/xml" + "fmt" + "os" + "path" + "strings" + + "github.com/onsi/ginkgo/v2/config" + "github.com/onsi/ginkgo/v2/types" +) + +type JunitReportConfig struct { + // Spec States for which no timeline should be emitted for system-err + // set this to types.SpecStatePassed|types.SpecStateSkipped|types.SpecStatePending to only match failing specs + OmitTimelinesForSpecState types.SpecState + + // Enable OmitFailureMessageAttr to prevent failure messages appearing in the "message" attribute of the Failure and Error tags + OmitFailureMessageAttr bool + + //Enable OmitCapturedStdOutErr to prevent captured stdout/stderr appearing in system-out + OmitCapturedStdOutErr bool + + // Enable OmitSpecLabels to prevent labels from appearing in the spec name + OmitSpecLabels bool + + // Enable OmitLeafNodeType to prevent the spec leaf node type from appearing in the spec name + OmitLeafNodeType bool + + // Enable OmitSuiteSetupNodes to prevent the creation of testcase entries for setup nodes + OmitSuiteSetupNodes bool +} + +type JUnitTestSuites struct { + XMLName xml.Name `xml:"testsuites"` + // Tests maps onto the total number of specs in all test suites (this includes any suite nodes such as BeforeSuite) + Tests int `xml:"tests,attr"` + // Disabled maps onto specs that are pending and/or skipped + Disabled int `xml:"disabled,attr"` + // Errors maps onto specs that panicked or were interrupted + Errors int `xml:"errors,attr"` + // Failures maps onto specs that failed + Failures int `xml:"failures,attr"` + // Time is the time in seconds to execute all test suites + Time float64 `xml:"time,attr"` + + //The set of all test suites + TestSuites []JUnitTestSuite `xml:"testsuite"` +} + +type JUnitTestSuite struct { + // Name maps onto the description of the test suite - maps onto Report.SuiteDescription + Name string `xml:"name,attr"` + // Package maps onto the absolute path to the test suite - maps onto Report.SuitePath + Package string `xml:"package,attr"` + // Tests maps onto the total number of specs in the test suite (this includes any suite nodes such as BeforeSuite) + Tests int `xml:"tests,attr"` + // Disabled maps onto specs that are pending + Disabled int `xml:"disabled,attr"` + // Skiped maps onto specs that are skipped + Skipped int `xml:"skipped,attr"` + // Errors maps onto specs that panicked or were interrupted + Errors int `xml:"errors,attr"` + // Failures maps onto specs that failed + Failures int `xml:"failures,attr"` + // Time is the time in seconds to execute all the test suite - maps onto Report.RunTime + Time float64 `xml:"time,attr"` + // Timestamp is the ISO 8601 formatted start-time of the suite - maps onto Report.StartTime + Timestamp string `xml:"timestamp,attr"` + + //Properties captures the information stored in the rest of the Report type (including SuiteConfig) as key-value pairs + Properties JUnitProperties `xml:"properties"` + + //TestCases capture the individual specs + TestCases []JUnitTestCase `xml:"testcase"` +} + +type JUnitProperties struct { + Properties []JUnitProperty `xml:"property"` +} + +func (jup JUnitProperties) WithName(name string) string { + for _, property := range jup.Properties { + if property.Name == name { + return property.Value + } + } + return "" +} + +type JUnitProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +type JUnitTestCase struct { + // Name maps onto the full text of the spec - equivalent to "[SpecReport.LeafNodeType] SpecReport.FullText()" + Name string `xml:"name,attr"` + // Classname maps onto the name of the test suite - equivalent to Report.SuiteDescription + Classname string `xml:"classname,attr"` + // Status maps onto the string representation of SpecReport.State + Status string `xml:"status,attr"` + // Time is the time in seconds to execute the spec - maps onto SpecReport.RunTime + Time float64 `xml:"time,attr"` + //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 + Error *JUnitError `xml:"error,omitempty"` + //Failure is populated if the test failed + Failure *JUnitFailure `xml:"failure,omitempty"` + //SystemOut maps onto any captured stdout/stderr output - maps onto SpecReport.CapturedStdOutErr + SystemOut string `xml:"system-out,omitempty"` + //SystemOut maps onto any captured GinkgoWriter output - maps onto SpecReport.CapturedGinkgoWriterOutput + SystemErr string `xml:"system-err,omitempty"` +} + +type JUnitSkipped struct { + // Message maps onto "pending" if the test was marked pending, "skipped" if the test was marked skipped, and "skipped - REASON" if the user called Skip(REASON) + Message string `xml:"message,attr"` +} + +type JUnitError struct { + //Message maps onto the panic/exception thrown - equivalent to SpecReport.Failure.ForwardedPanic - or to "interrupted" + Message string `xml:"message,attr"` + //Type is one of "panicked" or "interrupted" + Type string `xml:"type,attr"` + //Description maps onto the captured stack trace for a panic, or the failure message for an interrupt which will include the dump of running goroutines + Description string `xml:",chardata"` +} + +type JUnitFailure struct { + //Message maps onto the failure message - equivalent to SpecReport.Failure.Message + Message string `xml:"message,attr"` + //Type is "failed" + Type string `xml:"type,attr"` + //Description maps onto the location and stack trace of the failure + Description string `xml:",chardata"` +} + +func GenerateJUnitReport(report types.Report, dst string) error { + return GenerateJUnitReportWithConfig(report, dst, JunitReportConfig{}) +} + +func GenerateJUnitReportWithConfig(report types.Report, dst string, config JunitReportConfig) error { + suite := JUnitTestSuite{ + Name: report.SuiteDescription, + Package: report.SuitePath, + Time: report.RunTime.Seconds(), + Timestamp: report.StartTime.Format("2006-01-02T15:04:05"), + Properties: JUnitProperties{ + Properties: []JUnitProperty{ + {"SuiteSucceeded", fmt.Sprintf("%t", report.SuiteSucceeded)}, + {"SuiteHasProgrammaticFocus", fmt.Sprintf("%t", report.SuiteHasProgrammaticFocus)}, + {"SpecialSuiteFailureReason", strings.Join(report.SpecialSuiteFailureReasons, ",")}, + {"SuiteLabels", fmt.Sprintf("[%s]", strings.Join(report.SuiteLabels, ","))}, + {"RandomSeed", fmt.Sprintf("%d", report.SuiteConfig.RandomSeed)}, + {"RandomizeAllSpecs", fmt.Sprintf("%t", report.SuiteConfig.RandomizeAllSpecs)}, + {"LabelFilter", report.SuiteConfig.LabelFilter}, + {"FocusStrings", strings.Join(report.SuiteConfig.FocusStrings, ",")}, + {"SkipStrings", strings.Join(report.SuiteConfig.SkipStrings, ",")}, + {"FocusFiles", strings.Join(report.SuiteConfig.FocusFiles, ";")}, + {"SkipFiles", strings.Join(report.SuiteConfig.SkipFiles, ";")}, + {"FailOnPending", fmt.Sprintf("%t", report.SuiteConfig.FailOnPending)}, + {"FailFast", fmt.Sprintf("%t", report.SuiteConfig.FailFast)}, + {"FlakeAttempts", fmt.Sprintf("%d", report.SuiteConfig.FlakeAttempts)}, + {"DryRun", fmt.Sprintf("%t", report.SuiteConfig.DryRun)}, + {"ParallelTotal", fmt.Sprintf("%d", report.SuiteConfig.ParallelTotal)}, + {"OutputInterceptorMode", report.SuiteConfig.OutputInterceptorMode}, + }, + }, + } + for _, spec := range report.SpecReports { + if config.OmitSuiteSetupNodes && spec.LeafNodeType != types.NodeTypeIt { + continue + } + name := fmt.Sprintf("[%s]", spec.LeafNodeType) + if config.OmitLeafNodeType { + name = "" + } + if spec.FullText() != "" { + name = name + " " + spec.FullText() + } + labels := spec.Labels() + if len(labels) > 0 && !config.OmitSpecLabels { + name = name + " [" + strings.Join(labels, ", ") + "]" + } + name = strings.TrimSpace(name) + + test := JUnitTestCase{ + Name: name, + Classname: report.SuiteDescription, + Status: spec.State.String(), + Time: spec.RunTime.Seconds(), + } + if !spec.State.Is(config.OmitTimelinesForSpecState) { + test.SystemErr = systemErrForUnstructuredReporters(spec) + } + if !config.OmitCapturedStdOutErr { + test.SystemOut = systemOutForUnstructuredReporters(spec) + } + suite.Tests += 1 + + switch spec.State { + case types.SpecStateSkipped: + message := "skipped" + if spec.Failure.Message != "" { + message += " - " + spec.Failure.Message + } + test.Skipped = &JUnitSkipped{Message: message} + suite.Skipped += 1 + case types.SpecStatePending: + test.Skipped = &JUnitSkipped{Message: "pending"} + suite.Disabled += 1 + case types.SpecStateFailed: + test.Failure = &JUnitFailure{ + Message: spec.Failure.Message, + Type: "failed", + Description: failureDescriptionForUnstructuredReporters(spec), + } + if config.OmitFailureMessageAttr { + test.Failure.Message = "" + } + suite.Failures += 1 + case types.SpecStateTimedout: + test.Failure = &JUnitFailure{ + Message: spec.Failure.Message, + Type: "timedout", + Description: failureDescriptionForUnstructuredReporters(spec), + } + if config.OmitFailureMessageAttr { + test.Failure.Message = "" + } + suite.Failures += 1 + case types.SpecStateInterrupted: + test.Error = &JUnitError{ + Message: spec.Failure.Message, + Type: "interrupted", + Description: failureDescriptionForUnstructuredReporters(spec), + } + if config.OmitFailureMessageAttr { + test.Error.Message = "" + } + suite.Errors += 1 + case types.SpecStateAborted: + test.Failure = &JUnitFailure{ + Message: spec.Failure.Message, + Type: "aborted", + Description: failureDescriptionForUnstructuredReporters(spec), + } + if config.OmitFailureMessageAttr { + test.Failure.Message = "" + } + suite.Errors += 1 + case types.SpecStatePanicked: + test.Error = &JUnitError{ + Message: spec.Failure.ForwardedPanic, + Type: "panicked", + Description: failureDescriptionForUnstructuredReporters(spec), + } + if config.OmitFailureMessageAttr { + test.Error.Message = "" + } + suite.Errors += 1 + } + + suite.TestCases = append(suite.TestCases, test) + } + + junitReport := JUnitTestSuites{ + Tests: suite.Tests, + Disabled: suite.Disabled + suite.Skipped, + Errors: suite.Errors, + Failures: suite.Failures, + Time: suite.Time, + TestSuites: []JUnitTestSuite{suite}, + } + + if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { + return err + } + f, err := os.Create(dst) + if err != nil { + return err + } + f.WriteString(xml.Header) + encoder := xml.NewEncoder(f) + encoder.Indent(" ", " ") + encoder.Encode(junitReport) + + return f.Close() +} + +func MergeAndCleanupJUnitReports(sources []string, dst string) ([]string, error) { + messages := []string{} + mergedReport := JUnitTestSuites{} + for _, source := range sources { + report := JUnitTestSuites{} + f, err := os.Open(source) + if err != nil { + messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) + continue + } + err = xml.NewDecoder(f).Decode(&report) + if err != nil { + messages = append(messages, fmt.Sprintf("Could not decode %s:\n%s", source, err.Error())) + continue + } + os.Remove(source) + + mergedReport.Tests += report.Tests + mergedReport.Disabled += report.Disabled + mergedReport.Errors += report.Errors + mergedReport.Failures += report.Failures + mergedReport.Time += report.Time + mergedReport.TestSuites = append(mergedReport.TestSuites, report.TestSuites...) + } + + if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { + return messages, err + } + f, err := os.Create(dst) + if err != nil { + return messages, err + } + f.WriteString(xml.Header) + encoder := xml.NewEncoder(f) + encoder.Indent(" ", " ") + encoder.Encode(mergedReport) + + return messages, f.Close() +} + +func failureDescriptionForUnstructuredReporters(spec types.SpecReport) string { + out := &strings.Builder{} + NewDefaultReporter(types.ReporterConfig{NoColor: true, VeryVerbose: true}, out).emitFailure(0, spec.State, spec.Failure, true) + if len(spec.AdditionalFailures) > 0 { + out.WriteString("\nThere were additional failures detected after the initial failure. These are visible in the timeline\n") + } + return out.String() +} + +func systemErrForUnstructuredReporters(spec types.SpecReport) string { + return RenderTimeline(spec, true) +} + +func RenderTimeline(spec types.SpecReport, noColor bool) string { + out := &strings.Builder{} + NewDefaultReporter(types.ReporterConfig{NoColor: noColor, VeryVerbose: true}, out).emitTimeline(0, spec, spec.Timeline()) + return out.String() +} + +func systemOutForUnstructuredReporters(spec types.SpecReport) string { + return spec.CapturedStdOutErr +} + +// Deprecated JUnitReporter (so folks can still compile their suites) +type JUnitReporter struct{} + +func NewJUnitReporter(_ string) *JUnitReporter { return &JUnitReporter{} } +func (reporter *JUnitReporter) SuiteWillBegin(_ config.GinkgoConfigType, _ *types.SuiteSummary) {} +func (reporter *JUnitReporter) BeforeSuiteDidRun(_ *types.SetupSummary) {} +func (reporter *JUnitReporter) SpecWillRun(_ *types.SpecSummary) {} +func (reporter *JUnitReporter) SpecDidComplete(_ *types.SpecSummary) {} +func (reporter *JUnitReporter) AfterSuiteDidRun(_ *types.SetupSummary) {} +func (reporter *JUnitReporter) SuiteDidEnd(_ *types.SuiteSummary) {} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go b/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go new file mode 100644 index 000000000..5e726c464 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go @@ -0,0 +1,29 @@ +package reporters + +import ( + "github.com/onsi/ginkgo/v2/types" +) + +type Reporter interface { + SuiteWillBegin(report types.Report) + WillRun(report types.SpecReport) + DidRun(report types.SpecReport) + SuiteDidEnd(report types.Report) + + //Timeline emission + EmitFailure(state types.SpecState, failure types.Failure) + EmitProgressReport(progressReport types.ProgressReport) + EmitReportEntry(entry types.ReportEntry) + EmitSpecEvent(event types.SpecEvent) +} + +type NoopReporter struct{} + +func (n NoopReporter) SuiteWillBegin(report types.Report) {} +func (n NoopReporter) WillRun(report types.SpecReport) {} +func (n NoopReporter) DidRun(report types.SpecReport) {} +func (n NoopReporter) SuiteDidEnd(report types.Report) {} +func (n NoopReporter) EmitFailure(state types.SpecState, failure types.Failure) {} +func (n NoopReporter) EmitProgressReport(progressReport types.ProgressReport) {} +func (n NoopReporter) EmitReportEntry(entry types.ReportEntry) {} +func (n NoopReporter) EmitSpecEvent(event types.SpecEvent) {} diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go b/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go new file mode 100644 index 000000000..e990ad82e --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go @@ -0,0 +1,105 @@ +/* + +TeamCity Reporter for Ginkgo + +Makes use of TeamCity's support for Service Messages +http://confluence.jetbrains.com/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests +*/ + +package reporters + +import ( + "fmt" + "os" + "path" + "strings" + + "github.com/onsi/ginkgo/v2/types" +) + +func tcEscape(s string) string { + s = strings.ReplaceAll(s, "|", "||") + s = strings.ReplaceAll(s, "'", "|'") + s = strings.ReplaceAll(s, "\n", "|n") + s = strings.ReplaceAll(s, "\r", "|r") + s = strings.ReplaceAll(s, "[", "|[") + s = strings.ReplaceAll(s, "]", "|]") + return s +} + +func GenerateTeamcityReport(report types.Report, dst string) error { + if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { + return err + } + f, err := os.Create(dst) + if err != nil { + return err + } + + name := report.SuiteDescription + labels := report.SuiteLabels + if len(labels) > 0 { + name = name + " [" + strings.Join(labels, ", ") + "]" + } + fmt.Fprintf(f, "##teamcity[testSuiteStarted name='%s']\n", tcEscape(name)) + for _, spec := range report.SpecReports { + name := fmt.Sprintf("[%s]", spec.LeafNodeType) + if spec.FullText() != "" { + name = name + " " + spec.FullText() + } + labels := spec.Labels() + if len(labels) > 0 { + name = name + " [" + strings.Join(labels, ", ") + "]" + } + + name = tcEscape(name) + fmt.Fprintf(f, "##teamcity[testStarted name='%s']\n", name) + switch spec.State { + case types.SpecStatePending: + fmt.Fprintf(f, "##teamcity[testIgnored name='%s' message='pending']\n", name) + case types.SpecStateSkipped: + message := "skipped" + if spec.Failure.Message != "" { + message += " - " + spec.Failure.Message + } + fmt.Fprintf(f, "##teamcity[testIgnored name='%s' message='%s']\n", name, tcEscape(message)) + case types.SpecStateFailed: + details := failureDescriptionForUnstructuredReporters(spec) + fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='failed - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) + case types.SpecStatePanicked: + details := failureDescriptionForUnstructuredReporters(spec) + fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='panicked - %s' details='%s']\n", name, tcEscape(spec.Failure.ForwardedPanic), tcEscape(details)) + case types.SpecStateTimedout: + details := failureDescriptionForUnstructuredReporters(spec) + fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='timedout - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) + case types.SpecStateInterrupted: + details := failureDescriptionForUnstructuredReporters(spec) + fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='interrupted - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) + case types.SpecStateAborted: + details := failureDescriptionForUnstructuredReporters(spec) + fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='aborted - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) + } + + fmt.Fprintf(f, "##teamcity[testStdOut name='%s' out='%s']\n", name, tcEscape(systemOutForUnstructuredReporters(spec))) + fmt.Fprintf(f, "##teamcity[testStdErr name='%s' out='%s']\n", name, tcEscape(systemErrForUnstructuredReporters(spec))) + fmt.Fprintf(f, "##teamcity[testFinished name='%s' duration='%d']\n", name, int(spec.RunTime.Seconds()*1000.0)) + } + fmt.Fprintf(f, "##teamcity[testSuiteFinished name='%s']\n", tcEscape(report.SuiteDescription)) + + return f.Close() +} + +func MergeAndCleanupTeamcityReports(sources []string, dst string) ([]string, error) { + messages := []string{} + merged := []byte{} + for _, source := range sources { + data, err := os.ReadFile(source) + if err != nil { + messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) + continue + } + os.Remove(source) + merged = append(merged, data...) + } + return messages, os.WriteFile(dst, merged, 0666) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/code_location.go b/vendor/github.com/onsi/ginkgo/v2/types/code_location.go new file mode 100644 index 000000000..9cd576817 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/code_location.go @@ -0,0 +1,159 @@ +package types + +import ( + "fmt" + "os" + "regexp" + "runtime" + "runtime/debug" + "strings" + "sync" +) + +type CodeLocation struct { + FileName string `json:",omitempty"` + LineNumber int `json:",omitempty"` + FullStackTrace string `json:",omitempty"` + CustomMessage string `json:",omitempty"` +} + +func (codeLocation CodeLocation) String() string { + if codeLocation.CustomMessage != "" { + return codeLocation.CustomMessage + } + return fmt.Sprintf("%s:%d", codeLocation.FileName, codeLocation.LineNumber) +} + +func (codeLocation CodeLocation) ContentsOfLine() string { + if codeLocation.CustomMessage != "" { + return "" + } + contents, err := os.ReadFile(codeLocation.FileName) + if err != nil { + return "" + } + lines := strings.Split(string(contents), "\n") + if len(lines) < codeLocation.LineNumber { + return "" + } + return lines[codeLocation.LineNumber-1] +} + +type codeLocationLocator struct { + pcs map[uintptr]bool + helpers map[string]bool + lock *sync.Mutex +} + +func (c *codeLocationLocator) addHelper(pc uintptr) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.pcs[pc] { + return + } + c.lock.Unlock() + f := runtime.FuncForPC(pc) + c.lock.Lock() + if f == nil { + return + } + c.helpers[f.Name()] = true + c.pcs[pc] = true +} + +func (c *codeLocationLocator) hasHelper(name string) bool { + c.lock.Lock() + defer c.lock.Unlock() + return c.helpers[name] +} + +func (c *codeLocationLocator) getCodeLocation(skip int) CodeLocation { + pc := make([]uintptr, 40) + n := runtime.Callers(skip+2, pc) + if n == 0 { + return CodeLocation{} + } + pc = pc[:n] + frames := runtime.CallersFrames(pc) + for { + frame, more := frames.Next() + if !c.hasHelper(frame.Function) { + return CodeLocation{FileName: frame.File, LineNumber: frame.Line} + } + if !more { + break + } + } + return CodeLocation{} +} + +var clLocator = &codeLocationLocator{ + pcs: map[uintptr]bool{}, + helpers: map[string]bool{}, + lock: &sync.Mutex{}, +} + +// MarkAsHelper is used by GinkgoHelper to mark the caller (appropriately offset by skip)as a helper. You can use this directly if you need to provide an optional `skip` to mark functions further up the call stack as helpers. +func MarkAsHelper(optionalSkip ...int) { + skip := 1 + if len(optionalSkip) > 0 { + skip += optionalSkip[0] + } + pc, _, _, ok := runtime.Caller(skip) + if ok { + clLocator.addHelper(pc) + } +} + +func NewCustomCodeLocation(message string) CodeLocation { + return CodeLocation{ + CustomMessage: message, + } +} + +func NewCodeLocation(skip int) CodeLocation { + return clLocator.getCodeLocation(skip + 1) +} + +func NewCodeLocationWithStackTrace(skip int) CodeLocation { + cl := clLocator.getCodeLocation(skip + 1) + cl.FullStackTrace = PruneStack(string(debug.Stack()), skip+1) + return cl +} + +// PruneStack removes references to functions that are internal to Ginkgo +// and the Go runtime from a stack string and a certain number of stack entries +// at the beginning of the stack. The stack string has the format +// as returned by runtime/debug.Stack. The leading goroutine information is +// optional and always removed if present. Beware that runtime/debug.Stack +// adds itself as first entry, so typically skip must be >= 1 to remove that +// entry. +func PruneStack(fullStackTrace string, skip int) string { + stack := strings.Split(fullStackTrace, "\n") + // Ensure that the even entries are the method names and the + // odd entries the source code information. + if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") { + // Ignore "goroutine 29 [running]:" line. + stack = stack[1:] + } + // The "+1" is for skipping over the initial entry, which is + // runtime/debug.Stack() itself. + if len(stack) > 2*(skip+1) { + stack = stack[2*(skip+1):] + } + prunedStack := []string{} + if os.Getenv("GINKGO_PRUNE_STACK") == "FALSE" { + prunedStack = stack + } else { + 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])) { + prunedStack = append(prunedStack, stack[i*2]) + prunedStack = append(prunedStack, stack[i*2+1]) + } + } + } + return strings.Join(prunedStack, "\n") +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/config.go b/vendor/github.com/onsi/ginkgo/v2/types/config.go new file mode 100644 index 000000000..c88fc85a7 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/config.go @@ -0,0 +1,758 @@ +/* +Ginkgo accepts a number of configuration options. +These are documented [here](http://onsi.github.io/ginkgo/#the-ginkgo-cli) +*/ + +package types + +import ( + "flag" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" +) + +// Configuration controlling how an individual test suite is run +type SuiteConfig struct { + RandomSeed int64 + RandomizeAllSpecs bool + FocusStrings []string + SkipStrings []string + FocusFiles []string + SkipFiles []string + LabelFilter string + FailOnPending bool + FailFast bool + FlakeAttempts int + MustPassRepeatedly int + DryRun bool + PollProgressAfter time.Duration + PollProgressInterval time.Duration + Timeout time.Duration + EmitSpecProgress bool // this is deprecated but its removal is causing compile issue for some users that were setting it manually + OutputInterceptorMode string + SourceRoots []string + GracePeriod time.Duration + + ParallelProcess int + ParallelTotal int + ParallelHost string +} + +func NewDefaultSuiteConfig() SuiteConfig { + return SuiteConfig{ + RandomSeed: time.Now().Unix(), + Timeout: time.Hour, + ParallelProcess: 1, + ParallelTotal: 1, + GracePeriod: 30 * time.Second, + } +} + +type VerbosityLevel uint + +const ( + VerbosityLevelSuccinct VerbosityLevel = iota + VerbosityLevelNormal + VerbosityLevelVerbose + VerbosityLevelVeryVerbose +) + +func (vl VerbosityLevel) GT(comp VerbosityLevel) bool { + return vl > comp +} + +func (vl VerbosityLevel) GTE(comp VerbosityLevel) bool { + return vl >= comp +} + +func (vl VerbosityLevel) Is(comp VerbosityLevel) bool { + return vl == comp +} + +func (vl VerbosityLevel) LTE(comp VerbosityLevel) bool { + return vl <= comp +} + +func (vl VerbosityLevel) LT(comp VerbosityLevel) bool { + return vl < comp +} + +// Configuration for Ginkgo's reporter +type ReporterConfig struct { + NoColor bool + Succinct bool + Verbose bool + VeryVerbose bool + FullTrace bool + ShowNodeEvents bool + + JSONReport string + JUnitReport string + TeamcityReport string +} + +func (rc ReporterConfig) Verbosity() VerbosityLevel { + if rc.Succinct { + return VerbosityLevelSuccinct + } else if rc.Verbose { + return VerbosityLevelVerbose + } else if rc.VeryVerbose { + return VerbosityLevelVeryVerbose + } + return VerbosityLevelNormal +} + +func (rc ReporterConfig) WillGenerateReport() bool { + return rc.JSONReport != "" || rc.JUnitReport != "" || rc.TeamcityReport != "" +} + +func NewDefaultReporterConfig() ReporterConfig { + return ReporterConfig{} +} + +// Configuration for the Ginkgo CLI +type CLIConfig struct { + //for build, run, and watch + Recurse bool + SkipPackage string + RequireSuite bool + NumCompilers int + + //for run and watch only + Procs int + Parallel bool + AfterRunHook string + OutputDir string + KeepSeparateCoverprofiles bool + KeepSeparateReports bool + + //for run only + KeepGoing bool + UntilItFails bool + Repeat int + RandomizeSuites bool + + //for watch only + Depth int + WatchRegExp string +} + +func NewDefaultCLIConfig() CLIConfig { + return CLIConfig{ + Depth: 1, + WatchRegExp: `\.go$`, + } +} + +func (g CLIConfig) ComputedProcs() int { + if g.Procs > 0 { + return g.Procs + } + + n := 1 + if g.Parallel { + n = runtime.NumCPU() + if n > 4 { + n = n - 1 + } + } + return n +} + +func (g CLIConfig) ComputedNumCompilers() int { + if g.NumCompilers > 0 { + return g.NumCompilers + } + + return runtime.NumCPU() +} + +// Configuration for the Ginkgo CLI capturing available go flags +// A subset of Go flags are exposed by Ginkgo. Some are available at compile time (e.g. ginkgo build) and others only at run time (e.g. ginkgo run - which has both build and run time flags). +// More details can be found at: +// https://docs.google.com/spreadsheets/d/1zkp-DS4hU4sAJl5eHh1UmgwxCPQhf3s5a8fbiOI8tJU/ +type GoFlagsConfig struct { + //build-time flags for code-and-performance analysis + Race bool + Cover bool + CoverMode string + CoverPkg string + Vet string + + //run-time flags for code-and-performance analysis + BlockProfile string + BlockProfileRate int + CoverProfile string + CPUProfile string + MemProfile string + MemProfileRate int + MutexProfile string + MutexProfileFraction int + Trace string + + //build-time flags for building + A bool + ASMFlags string + BuildMode string + Compiler string + GCCGoFlags string + GCFlags string + InstallSuffix string + LDFlags string + LinkShared bool + Mod string + N bool + ModFile string + ModCacheRW bool + MSan bool + PkgDir string + Tags string + TrimPath bool + ToolExec string + Work bool + X bool +} + +func NewDefaultGoFlagsConfig() GoFlagsConfig { + return GoFlagsConfig{} +} + +func (g GoFlagsConfig) BinaryMustBePreserved() bool { + return g.BlockProfile != "" || g.CPUProfile != "" || g.MemProfile != "" || g.MutexProfile != "" +} + +// Configuration that were deprecated in 2.0 +type deprecatedConfig struct { + DebugParallel bool + NoisySkippings bool + NoisyPendings bool + RegexScansFilePath bool + SlowSpecThresholdWithFLoatUnits float64 + Stream bool + Notify bool + EmitSpecProgress bool + SlowSpecThreshold time.Duration + AlwaysEmitGinkgoWriter bool +} + +// Flags + +// Flags sections used by both the CLI and the Ginkgo test process +var FlagSections = GinkgoFlagSections{ + {Key: "multiple-suites", Style: "{{dark-green}}", Heading: "Running Multiple Test Suites"}, + {Key: "order", Style: "{{green}}", Heading: "Controlling Test Order"}, + {Key: "parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism"}, + {Key: "low-level-parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism", + Description: "These are set by the Ginkgo CLI, {{red}}{{bold}}do not set them manually{{/}} via go test.\nUse ginkgo -p or ginkgo -procs=N instead."}, + {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: "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"}, + {Key: "misc", Style: "{{light-gray}}", Heading: "Miscellaneous"}, + {Key: "go-build", Style: "{{light-gray}}", Heading: "Go Build Flags", Succinct: true, + Description: "These flags are inherited from go build. Run {{bold}}ginkgo help build{{/}} for more detailed flag documentation."}, +} + +// 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."}, + {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."}, + + {KeyPath: "S.FailOnPending", Name: "fail-on-pending", SectionKey: "failure", DeprecatedName: "failOnPending", DeprecatedDocLink: "changed-command-line-flags", + Usage: "If set, ginkgo will mark the test suite as failed if any specs are pending."}, + {KeyPath: "S.FailFast", Name: "fail-fast", SectionKey: "failure", DeprecatedName: "failFast", DeprecatedDocLink: "changed-command-line-flags", + 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.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."}, + {KeyPath: "S.PollProgressAfter", Name: "poll-progress-after", SectionKey: "debug", UsageDefaultValue: "0", + Usage: "Emit node progress reports periodically if node hasn't completed after this duration."}, + {KeyPath: "S.PollProgressInterval", Name: "poll-progress-interval", SectionKey: "debug", UsageDefaultValue: "10s", + Usage: "The rate at which to emit node progress reports after poll-progress-after has elapsed."}, + {KeyPath: "S.SourceRoots", Name: "source-root", SectionKey: "debug", + Usage: "The location to look for source code when generating progress reports. You can pass multiple --source-root flags."}, + {KeyPath: "S.Timeout", Name: "timeout", SectionKey: "debug", UsageDefaultValue: "1h", + Usage: "Test suite fails if it does not complete within the specified timeout."}, + {KeyPath: "S.GracePeriod", Name: "grace-period", SectionKey: "debug", UsageDefaultValue: "30s", + Usage: "When interrupted, Ginkgo will wait for GracePeriod for the current running node to exit before moving on to the next one."}, + {KeyPath: "S.OutputInterceptorMode", Name: "output-interceptor-mode", SectionKey: "debug", UsageArgument: "dup, swap, or none", + Usage: "If set, ginkgo will use the specified output interception strategy when running in parallel. Defaults to dup on unix and swap on windows."}, + + {KeyPath: "S.LabelFilter", Name: "label-filter", SectionKey: "filter", UsageArgument: "expression", + Usage: "If set, ginkgo will only run specs with labels that match the label-filter. The passed-in expression can include boolean operations (!, &&, ||, ','), groupings via '()', and regular expressions '/regexp/'. e.g. '(cat || dog) && !fruit'"}, + {KeyPath: "S.FocusStrings", Name: "focus", SectionKey: "filter", + Usage: "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed."}, + {KeyPath: "S.SkipStrings", Name: "skip", SectionKey: "filter", + Usage: "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed."}, + {KeyPath: "S.FocusFiles", Name: "focus-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line", + Usage: "If set, ginkgo will only run specs in matching files. Can be specified multiple times, values are ORed."}, + {KeyPath: "S.SkipFiles", Name: "skip-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line", + Usage: "If set, ginkgo will skip specs in matching files. Can be specified multiple times, values are ORed."}, + + {KeyPath: "D.RegexScansFilePath", DeprecatedName: "regexScansFilePath", DeprecatedDocLink: "removed--regexscansfilepath", DeprecatedVersion: "2.0.0"}, + {KeyPath: "D.DebugParallel", DeprecatedName: "debug", DeprecatedDocLink: "removed--debug", DeprecatedVersion: "2.0.0"}, + {KeyPath: "D.EmitSpecProgress", DeprecatedName: "progress", SectionKey: "debug", + DeprecatedVersion: "2.5.0", Usage: ". The functionality provided by --progress was confusing and is no longer needed. Use --show-node-events instead to see node entry and exit events included in the timeline of failed and verbose specs. Or you can run with -vv to always see all node events. Lastly, --poll-progress-after and the PollProgressAfter decorator now provide a better mechanism for debugging specs that tend to get stuck."}, +} + +// ParallelConfigFlags provides flags for the Ginkgo test process (not the CLI) +var ParallelConfigFlags = GinkgoFlags{ + {KeyPath: "S.ParallelProcess", Name: "parallel.process", SectionKey: "low-level-parallel", UsageDefaultValue: "1", + Usage: "This worker process's (one-indexed) process number. For running specs in parallel."}, + {KeyPath: "S.ParallelTotal", Name: "parallel.total", SectionKey: "low-level-parallel", UsageDefaultValue: "1", + Usage: "The total number of worker processes. For running specs in parallel."}, + {KeyPath: "S.ParallelHost", Name: "parallel.host", SectionKey: "low-level-parallel", UsageDefaultValue: "set by Ginkgo CLI", + Usage: "The address for the server that will synchronize the processes."}, +} + +// 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."}, + {KeyPath: "R.Verbose", Name: "v", SectionKey: "output", + Usage: "If set, emits more output including GinkgoWriter contents."}, + {KeyPath: "R.VeryVerbose", Name: "vv", SectionKey: "output", + Usage: "If set, emits with maximal verbosity - includes skipped and pending tests."}, + {KeyPath: "R.Succinct", Name: "succinct", SectionKey: "output", + Usage: "If set, default reporter prints out a very succinct report"}, + {KeyPath: "R.FullTrace", Name: "trace", SectionKey: "output", + 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.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output", + Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."}, + {KeyPath: "R.JUnitReport", Name: "junit-report", UsageArgument: "filename.xml", SectionKey: "output", DeprecatedName: "reportFile", DeprecatedDocLink: "improved-reporting-infrastructure", + Usage: "If set, Ginkgo will generate a conformant junit test report in the specified file."}, + {KeyPath: "R.TeamcityReport", Name: "teamcity-report", UsageArgument: "filename", SectionKey: "output", + Usage: "If set, Ginkgo will generate a Teamcity-formatted test report at the specified location."}, + + {KeyPath: "D.SlowSpecThresholdWithFLoatUnits", DeprecatedName: "slowSpecThreshold", DeprecatedDocLink: "changed--slowspecthreshold", + Usage: "use --slow-spec-threshold instead and pass in a duration string (e.g. '5s', not '5.0')"}, + {KeyPath: "D.NoisyPendings", DeprecatedName: "noisyPendings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"}, + {KeyPath: "D.NoisySkippings", DeprecatedName: "noisySkippings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"}, + {KeyPath: "D.SlowSpecThreshold", DeprecatedName: "slow-spec-threshold", SectionKey: "output", Usage: "--slow-spec-threshold has been deprecated and will be removed in a future version of Ginkgo. This feature has proved to be more noisy than useful. You can use --poll-progress-after, instead, to get more actionable feedback about potentially slow specs and understand where they might be getting stuck.", DeprecatedVersion: "2.5.0"}, + {KeyPath: "D.AlwaysEmitGinkgoWriter", DeprecatedName: "always-emit-ginkgo-writer", SectionKey: "output", Usage: " - use -v instead, or one of Ginkgo's machine-readable report formats to get GinkgoWriter output for passing specs."}, +} + +// BuildTestSuiteFlagSet attaches to the CommandLine flagset and provides flags for the Ginkgo test process +func BuildTestSuiteFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig) (GinkgoFlagSet, error) { + flags := SuiteConfigFlags.CopyAppend(ParallelConfigFlags...).CopyAppend(ReporterConfigFlags...) + flags = flags.WithPrefix("ginkgo") + bindings := map[string]interface{}{ + "S": suiteConfig, + "R": reporterConfig, + "D": &deprecatedConfig{}, + } + extraGoFlagsSection := GinkgoFlagSection{Style: "{{gray}}", Heading: "Go test flags"} + + return NewAttachedGinkgoFlagSet(flag.CommandLine, flags, bindings, FlagSections, extraGoFlagsSection) +} + +// VetConfig validates that the Ginkgo test process' configuration is sound +func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig ReporterConfig) []error { + errors := []error{} + + if flagSet.WasSet("count") || flagSet.WasSet("test.count") { + flag := flagSet.Lookup("count") + if flag == nil { + flag = flagSet.Lookup("test.count") + } + count, err := strconv.Atoi(flag.Value.String()) + if err != nil || count != 1 { + errors = append(errors, GinkgoErrors.InvalidGoFlagCount()) + } + } + + if flagSet.WasSet("parallel") || flagSet.WasSet("test.parallel") { + errors = append(errors, GinkgoErrors.InvalidGoFlagParallel()) + } + + if suiteConfig.ParallelTotal < 1 { + errors = append(errors, GinkgoErrors.InvalidParallelTotalConfiguration()) + } + + if suiteConfig.ParallelProcess > suiteConfig.ParallelTotal || suiteConfig.ParallelProcess < 1 { + errors = append(errors, GinkgoErrors.InvalidParallelProcessConfiguration()) + } + + if suiteConfig.ParallelTotal > 1 && suiteConfig.ParallelHost == "" { + errors = append(errors, GinkgoErrors.MissingParallelHostConfiguration()) + } + + if suiteConfig.DryRun && suiteConfig.ParallelTotal > 1 { + errors = append(errors, GinkgoErrors.DryRunInParallelConfiguration()) + } + + if suiteConfig.GracePeriod <= 0 { + errors = append(errors, GinkgoErrors.GracePeriodCannotBeZero()) + } + + if len(suiteConfig.FocusFiles) > 0 { + _, err := ParseFileFilters(suiteConfig.FocusFiles) + if err != nil { + errors = append(errors, err) + } + } + + if len(suiteConfig.SkipFiles) > 0 { + _, err := ParseFileFilters(suiteConfig.SkipFiles) + if err != nil { + errors = append(errors, err) + } + } + + if suiteConfig.LabelFilter != "" { + _, err := ParseLabelFilter(suiteConfig.LabelFilter) + if err != nil { + errors = append(errors, err) + } + } + + switch strings.ToLower(suiteConfig.OutputInterceptorMode) { + case "", "dup", "swap", "none": + default: + errors = append(errors, GinkgoErrors.InvalidOutputInterceptorModeConfiguration(suiteConfig.OutputInterceptorMode)) + } + + numVerbosity := 0 + for _, v := range []bool{reporterConfig.Succinct, reporterConfig.Verbose, reporterConfig.VeryVerbose} { + if v { + numVerbosity++ + } + } + if numVerbosity > 1 { + errors = append(errors, GinkgoErrors.ConflictingVerbosityConfiguration()) + } + + return errors +} + +// GinkgoCLISharedFlags provides flags shared by the Ginkgo CLI's build, watch, and run commands +var GinkgoCLISharedFlags = GinkgoFlags{ + {KeyPath: "C.Recurse", Name: "r", SectionKey: "multiple-suites", + Usage: "If set, ginkgo finds and runs test suites under the current directory recursively."}, + {KeyPath: "C.SkipPackage", Name: "skip-package", SectionKey: "multiple-suites", DeprecatedName: "skipPackage", DeprecatedDocLink: "changed-command-line-flags", + UsageArgument: "comma-separated list of packages", + Usage: "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored."}, + {KeyPath: "C.RequireSuite", Name: "require-suite", SectionKey: "failure", DeprecatedName: "requireSuite", DeprecatedDocLink: "changed-command-line-flags", + Usage: "If set, Ginkgo fails if there are ginkgo tests in a directory but no invocation of RunSpecs."}, + {KeyPath: "C.NumCompilers", Name: "compilers", SectionKey: "multiple-suites", UsageDefaultValue: "0 (will autodetect)", + Usage: "When running multiple packages, the number of concurrent compilations to perform."}, +} + +// GinkgoCLIRunAndWatchFlags provides flags shared by the Ginkgo CLI's build and watch commands (but not run) +var GinkgoCLIRunAndWatchFlags = GinkgoFlags{ + {KeyPath: "C.Procs", Name: "procs", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)", + Usage: "The number of parallel test nodes to run."}, + {KeyPath: "C.Procs", Name: "nodes", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)", + Usage: "--nodes is an alias for --procs"}, + {KeyPath: "C.Parallel", Name: "p", SectionKey: "parallel", + Usage: "If set, ginkgo will run in parallel with an auto-detected number of nodes."}, + {KeyPath: "C.AfterRunHook", Name: "after-run-hook", SectionKey: "misc", DeprecatedName: "afterSuiteHook", DeprecatedDocLink: "changed-command-line-flags", + Usage: "Command to run when a test suite completes."}, + {KeyPath: "C.OutputDir", Name: "output-dir", SectionKey: "output", UsageArgument: "directory", DeprecatedName: "outputdir", DeprecatedDocLink: "improved-profiling-support", + Usage: "A location to place all generated profiles and reports."}, + {KeyPath: "C.KeepSeparateCoverprofiles", Name: "keep-separate-coverprofiles", SectionKey: "code-and-coverage-analysis", + Usage: "If set, Ginkgo does not merge coverprofiles into one monolithic coverprofile. The coverprofiles will remain in their respective package directories or in -output-dir if set."}, + {KeyPath: "C.KeepSeparateReports", Name: "keep-separate-reports", SectionKey: "output", + Usage: "If set, Ginkgo does not merge per-suite reports (e.g. -json-report) into one monolithic report for the entire testrun. The reports will remain in their respective package directories or in -output-dir if set."}, + + {KeyPath: "D.Stream", DeprecatedName: "stream", DeprecatedDocLink: "removed--stream", DeprecatedVersion: "2.0.0"}, + {KeyPath: "D.Notify", DeprecatedName: "notify", DeprecatedDocLink: "removed--notify", DeprecatedVersion: "2.0.0"}, +} + +// GinkgoCLIRunFlags provides flags for Ginkgo CLI's run command that aren't shared by any other commands +var GinkgoCLIRunFlags = GinkgoFlags{ + {KeyPath: "C.KeepGoing", Name: "keep-going", SectionKey: "multiple-suites", DeprecatedName: "keepGoing", DeprecatedDocLink: "changed-command-line-flags", + Usage: "If set, failures from earlier test suites do not prevent later test suites from running."}, + {KeyPath: "C.UntilItFails", Name: "until-it-fails", SectionKey: "debug", DeprecatedName: "untilItFails", DeprecatedDocLink: "changed-command-line-flags", + Usage: "If set, ginkgo will keep rerunning test suites until a failure occurs."}, + {KeyPath: "C.Repeat", Name: "repeat", SectionKey: "debug", UsageArgument: "n", UsageDefaultValue: "0 - i.e. no repetition, run only once", + Usage: "The number of times to re-run a test-suite. Useful for debugging flaky tests. If set to N the suite will be run N+1 times and will be required to pass each time."}, + {KeyPath: "C.RandomizeSuites", Name: "randomize-suites", SectionKey: "order", DeprecatedName: "randomizeSuites", DeprecatedDocLink: "changed-command-line-flags", + Usage: "If set, ginkgo will randomize the order in which test suites run."}, +} + +// GinkgoCLIRunFlags provides flags for Ginkgo CLI's watch command that aren't shared by any other commands +var GinkgoCLIWatchFlags = GinkgoFlags{ + {KeyPath: "C.Depth", Name: "depth", SectionKey: "watch", + Usage: "Ginkgo will watch dependencies down to this depth in the dependency tree."}, + {KeyPath: "C.WatchRegExp", Name: "watch-regexp", SectionKey: "watch", DeprecatedName: "watchRegExp", DeprecatedDocLink: "changed-command-line-flags", + UsageArgument: "Regular Expression", + UsageDefaultValue: `\.go$`, + Usage: "Only files matching this regular expression will be watched for changes."}, +} + +// 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)."}, + {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'`}, + {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", + Usage: `Set the mode for coverage analysis for the package[s] being tested. 'set': does this statement run? 'count': how many times does this statement run? 'atomic': like count, but correct in multithreaded tests and more expensive (must use atomic with -race). Sets -cover`}, + {KeyPath: "Go.CoverPkg", Name: "coverpkg", UsageArgument: "pattern1,pattern2,pattern3", SectionKey: "code-and-coverage-analysis", + Usage: "Apply coverage analysis in each test to packages matching the patterns. The default is for each test to analyze only the package being tested. See 'go help packages' for a description of package patterns. Sets -cover."}, + + {KeyPath: "Go.A", Name: "a", SectionKey: "go-build", + Usage: "force rebuilding of packages that are already up-to-date."}, + {KeyPath: "Go.ASMFlags", Name: "asmflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", + 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.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", + Usage: "arguments to pass on each gccgo compiler/linker invocation."}, + {KeyPath: "Go.GCFlags", Name: "gcflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", + Usage: "arguments to pass on each go tool compile invocation."}, + {KeyPath: "Go.InstallSuffix", Name: "installsuffix", SectionKey: "go-build", + Usage: "a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to raceor, if set explicitly, has _race appended to it. Likewise for the -msan flag. Using a -buildmode option that requires non-default compile flags has a similar effect."}, + {KeyPath: "Go.LDFlags", Name: "ldflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", + Usage: "arguments to pass on each go tool link invocation."}, + {KeyPath: "Go.LinkShared", Name: "linkshared", SectionKey: "go-build", + Usage: "build code that will be linked against shared libraries previously created with -buildmode=shared."}, + {KeyPath: "Go.Mod", Name: "mod", UsageArgument: "mode (readonly, vendor, or mod)", SectionKey: "go-build", + Usage: "module download mode to use: readonly, vendor, or mod. See 'go help modules' for more."}, + {KeyPath: "Go.ModCacheRW", Name: "modcacherw", SectionKey: "go-build", + Usage: "leave newly-created directories in the module cache read-write instead of making them read-only."}, + {KeyPath: "Go.ModFile", Name: "modfile", UsageArgument: "file", SectionKey: "go-build", + Usage: `in module aware mode, read (and possibly write) an alternate go.mod file instead of the one in the module root directory. A file named go.mod must still be present in order to determine the module root directory, but it is not accessed. When -modfile is specified, an alternate go.sum file is also used: its path is derived from the -modfile flag by trimming the ".mod" extension and appending ".sum".`}, + {KeyPath: "Go.MSan", Name: "msan", SectionKey: "go-build", + Usage: "enable interoperation with memory sanitizer. Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. On linux/arm64, pie build mode will be used."}, + {KeyPath: "Go.N", Name: "n", SectionKey: "go-build", + Usage: "print the commands but do not run them."}, + {KeyPath: "Go.PkgDir", Name: "pkgdir", UsageArgument: "dir", SectionKey: "go-build", + Usage: "install and load all packages from dir instead of the usual locations. For example, when building with a non-standard configuration, use -pkgdir to keep generated packages in a separate location."}, + {KeyPath: "Go.Tags", Name: "tags", UsageArgument: "tag,list", SectionKey: "go-build", + Usage: "a comma-separated list of build tags to consider satisfied during the build. For more information about build tags, see the description of build constraints in the documentation for the go/build package. (Earlier versions of Go used a space-separated list, and that form is deprecated but still recognized.)"}, + {KeyPath: "Go.TrimPath", Name: "trimpath", SectionKey: "go-build", + Usage: `remove all file system paths from the resulting executable. Instead of absolute file system paths, the recorded file names will begin with either "go" (for the standard library), or a module path@version (when using modules), or a plain import path (when using GOPATH).`}, + {KeyPath: "Go.ToolExec", Name: "toolexec", UsageArgument: "'cmd args'", SectionKey: "go-build", + Usage: "a program to use to invoke toolchain programs like vet and asm. For example, instead of running asm, the go command will run cmd args /path/to/asm '."}, + {KeyPath: "Go.Work", Name: "work", SectionKey: "go-build", + 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."}, +} + +// 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.`}, + {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", + Usage: `Control the detail provided in goroutine blocking profiles by calling runtime.SetBlockProfileRate with rate. See 'go doc runtime.SetBlockProfileRate'. The profiler aims to sample, on average, one blocking event every n nanoseconds the program spends blocked. By default, if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1.`}, + {KeyPath: "Go.CPUProfile", Name: "cpuprofile", UsageArgument: "file", SectionKey: "performance-analysis", + Usage: `Write a CPU profile to the specified file before exiting. Preserves test binary.`}, + {KeyPath: "Go.MemProfile", Name: "memprofile", UsageArgument: "file", SectionKey: "performance-analysis", + Usage: `Write an allocation profile to the file after all tests have passed. Preserves test binary.`}, + {KeyPath: "Go.MemProfileRate", Name: "memprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis", + Usage: `Enable more precise (and expensive) memory allocation profiles by setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1.`}, + {KeyPath: "Go.MutexProfile", Name: "mutexprofile", UsageArgument: "file", SectionKey: "performance-analysis", + Usage: `Write a mutex contention profile to the specified file when all tests are complete. Preserves test binary.`}, + {KeyPath: "Go.MutexProfileFraction", Name: "mutexprofilefraction", UsageArgument: "n", SectionKey: "performance-analysis", + Usage: `if >= 0, calls runtime.SetMutexProfileFraction() Sample 1 in n stack traces of goroutines holding a contended mutex.`}, + {KeyPath: "Go.Trace", Name: "execution-trace", UsageArgument: "file", ExportAs: "trace", SectionKey: "performance-analysis", + Usage: `Write an execution trace to the specified file before exiting.`}, +} + +// VetAndInitializeCLIAndGoConfig validates that the Ginkgo CLI's configuration is sound +// It returns a potentially mutated copy of the config that rationalizes the configuration to ensure consistency for downstream consumers +func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsConfig) (CLIConfig, GoFlagsConfig, []error) { + errors := []error{} + + if cliConfig.Repeat > 0 && cliConfig.UntilItFails { + errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails()) + } + + //initialize the output directory + if cliConfig.OutputDir != "" { + err := os.MkdirAll(cliConfig.OutputDir, 0777) + if err != nil { + errors = append(errors, err) + } + } + + //ensure cover mode is configured appropriately + if goFlagsConfig.CoverMode != "" || goFlagsConfig.CoverPkg != "" || goFlagsConfig.CoverProfile != "" { + goFlagsConfig.Cover = true + } + if goFlagsConfig.Cover && goFlagsConfig.CoverProfile == "" { + goFlagsConfig.CoverProfile = "coverprofile.out" + } + + return cliConfig, goFlagsConfig, errors +} + +// 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) { + // 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 != "" { + goFlagsConfig.Cover = true + } + + if goFlagsConfig.CoverPkg != "" { + coverPkgs := strings.Split(goFlagsConfig.CoverPkg, ",") + adjustedCoverPkgs := make([]string, len(coverPkgs)) + for i, coverPkg := range coverPkgs { + coverPkg = strings.Trim(coverPkg, " ") + if strings.HasPrefix(coverPkg, "./") { + // this is a relative coverPkg - we need to reroot it + adjustedCoverPkgs[i] = "./" + filepath.Join(pathToInvocationPath, strings.TrimPrefix(coverPkg, "./")) + } else { + // this is a package name - don't touch it + adjustedCoverPkgs[i] = coverPkg + } + } + goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",") + } + + args := []string{"test", "-c", "-o", destination, packageToBuild} + goArgs, err := GenerateFlagArgs( + GoBuildFlags, + map[string]interface{}{ + "Go": &goFlagsConfig, + }, + ) + + if err != nil { + return []string{}, err + } + args = append(args, goArgs...) + return args, nil +} + +// GenerateGinkgoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled Ginkgo test binary +func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterConfig, goFlagsConfig GoFlagsConfig) ([]string, error) { + var flags GinkgoFlags + flags = SuiteConfigFlags.WithPrefix("ginkgo") + flags = flags.CopyAppend(ParallelConfigFlags.WithPrefix("ginkgo")...) + flags = flags.CopyAppend(ReporterConfigFlags.WithPrefix("ginkgo")...) + flags = flags.CopyAppend(GoRunFlags.WithPrefix("test")...) + bindings := map[string]interface{}{ + "S": &suiteConfig, + "R": &reporterConfig, + "Go": &goFlagsConfig, + } + + return GenerateFlagArgs(flags, bindings) +} + +// 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{}{ + "Go": &goFlagsConfig, + } + + args, err := GenerateFlagArgs(flags, bindings) + if err != nil { + return args, err + } + args = append(args, "--test.v") + return args, nil +} + +// BuildRunCommandFlagSet builds the FlagSet for the `ginkgo run` command +func BuildRunCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { + flags := SuiteConfigFlags + flags = flags.CopyAppend(ReporterConfigFlags...) + flags = flags.CopyAppend(GinkgoCLISharedFlags...) + flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...) + flags = flags.CopyAppend(GinkgoCLIRunFlags...) + flags = flags.CopyAppend(GoBuildFlags...) + flags = flags.CopyAppend(GoRunFlags...) + + bindings := map[string]interface{}{ + "S": suiteConfig, + "R": reporterConfig, + "C": cliConfig, + "Go": goFlagsConfig, + "D": &deprecatedConfig{}, + } + + return NewGinkgoFlagSet(flags, bindings, FlagSections) +} + +// BuildWatchCommandFlagSet builds the FlagSet for the `ginkgo watch` command +func BuildWatchCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { + flags := SuiteConfigFlags + flags = flags.CopyAppend(ReporterConfigFlags...) + flags = flags.CopyAppend(GinkgoCLISharedFlags...) + flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...) + flags = flags.CopyAppend(GinkgoCLIWatchFlags...) + flags = flags.CopyAppend(GoBuildFlags...) + flags = flags.CopyAppend(GoRunFlags...) + + bindings := map[string]interface{}{ + "S": suiteConfig, + "R": reporterConfig, + "C": cliConfig, + "Go": goFlagsConfig, + "D": &deprecatedConfig{}, + } + + return NewGinkgoFlagSet(flags, bindings, FlagSections) +} + +// BuildBuildCommandFlagSet builds the FlagSet for the `ginkgo build` command +func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { + flags := GinkgoCLISharedFlags + flags = flags.CopyAppend(GoBuildFlags...) + + bindings := map[string]interface{}{ + "C": cliConfig, + "Go": goFlagsConfig, + "D": &deprecatedConfig{}, + } + + flagSections := make(GinkgoFlagSections, len(FlagSections)) + copy(flagSections, FlagSections) + for i := range flagSections { + if flagSections[i].Key == "multiple-suites" { + flagSections[i].Heading = "Building Multiple Suites" + } + if flagSections[i].Key == "go-build" { + flagSections[i] = GinkgoFlagSection{Key: "go-build", Style: "{{/}}", Heading: "Go Build Flags", + Description: "These flags are inherited from go build."} + } + } + + return NewGinkgoFlagSet(flags, bindings, flagSections) +} + +func BuildLabelsCommandFlagSet(cliConfig *CLIConfig) (GinkgoFlagSet, error) { + flags := GinkgoCLISharedFlags.SubsetWithNames("r", "skip-package") + + bindings := map[string]interface{}{ + "C": cliConfig, + } + + flagSections := make(GinkgoFlagSections, len(FlagSections)) + copy(flagSections, FlagSections) + for i := range flagSections { + if flagSections[i].Key == "multiple-suites" { + flagSections[i].Heading = "Fetching Labels from Multiple Suites" + } + } + + return NewGinkgoFlagSet(flags, bindings, flagSections) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go b/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go new file mode 100644 index 000000000..17922304b --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go @@ -0,0 +1,141 @@ +package types + +import ( + "strconv" + "time" +) + +/* + A set of deprecations to make the transition from v1 to v2 easier for users who have written custom reporters. +*/ + +type SuiteSummary = DeprecatedSuiteSummary +type SetupSummary = DeprecatedSetupSummary +type SpecSummary = DeprecatedSpecSummary +type SpecMeasurement = DeprecatedSpecMeasurement +type SpecComponentType = NodeType +type SpecFailure = DeprecatedSpecFailure + +var ( + SpecComponentTypeInvalid = NodeTypeInvalid + SpecComponentTypeContainer = NodeTypeContainer + SpecComponentTypeIt = NodeTypeIt + SpecComponentTypeBeforeEach = NodeTypeBeforeEach + SpecComponentTypeJustBeforeEach = NodeTypeJustBeforeEach + SpecComponentTypeAfterEach = NodeTypeAfterEach + SpecComponentTypeJustAfterEach = NodeTypeJustAfterEach + SpecComponentTypeBeforeSuite = NodeTypeBeforeSuite + SpecComponentTypeSynchronizedBeforeSuite = NodeTypeSynchronizedBeforeSuite + SpecComponentTypeAfterSuite = NodeTypeAfterSuite + SpecComponentTypeSynchronizedAfterSuite = NodeTypeSynchronizedAfterSuite +) + +type DeprecatedSuiteSummary struct { + SuiteDescription string + SuiteSucceeded bool + SuiteID string + + NumberOfSpecsBeforeParallelization int + NumberOfTotalSpecs int + NumberOfSpecsThatWillBeRun int + NumberOfPendingSpecs int + NumberOfSkippedSpecs int + NumberOfPassedSpecs int + NumberOfFailedSpecs int + NumberOfFlakedSpecs int + RunTime time.Duration +} + +type DeprecatedSetupSummary struct { + ComponentType SpecComponentType + CodeLocation CodeLocation + + State SpecState + RunTime time.Duration + Failure SpecFailure + + CapturedOutput string + SuiteID string +} + +type DeprecatedSpecSummary struct { + ComponentTexts []string + ComponentCodeLocations []CodeLocation + + State SpecState + RunTime time.Duration + Failure SpecFailure + IsMeasurement bool + NumberOfSamples int + Measurements map[string]*DeprecatedSpecMeasurement + + CapturedOutput string + SuiteID string +} + +func (s DeprecatedSpecSummary) HasFailureState() bool { + return s.State.Is(SpecStateFailureStates) +} + +func (s DeprecatedSpecSummary) TimedOut() bool { + return false +} + +func (s DeprecatedSpecSummary) Panicked() bool { + return s.State == SpecStatePanicked +} + +func (s DeprecatedSpecSummary) Failed() bool { + return s.State == SpecStateFailed +} + +func (s DeprecatedSpecSummary) Passed() bool { + return s.State == SpecStatePassed +} + +func (s DeprecatedSpecSummary) Skipped() bool { + return s.State == SpecStateSkipped +} + +func (s DeprecatedSpecSummary) Pending() bool { + return s.State == SpecStatePending +} + +type DeprecatedSpecFailure struct { + Message string + Location CodeLocation + ForwardedPanic string + + ComponentIndex int + ComponentType SpecComponentType + ComponentCodeLocation CodeLocation +} + +type DeprecatedSpecMeasurement struct { + Name string + Info interface{} + Order int + + Results []float64 + + Smallest float64 + Largest float64 + Average float64 + StdDeviation float64 + + SmallestLabel string + LargestLabel string + AverageLabel string + Units string + Precision int +} + +func (s DeprecatedSpecMeasurement) PrecisionFmt() string { + if s.Precision == 0 { + return "%f" + } + + str := strconv.Itoa(s.Precision) + + return "%." + str + "f" +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go b/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go new file mode 100644 index 000000000..e2519f673 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go @@ -0,0 +1,177 @@ +package types + +import ( + "os" + "strconv" + "strings" + "sync" + "unicode" + + "github.com/onsi/ginkgo/v2/formatter" +) + +type Deprecation struct { + Message string + DocLink string + Version string +} + +type deprecations struct{} + +var Deprecations = deprecations{} + +func (d deprecations) CustomReporter() Deprecation { + return Deprecation{ + Message: "Support for custom reporters has been removed in V2. Please read the documentation linked to below for Ginkgo's new behavior and for a migration path:", + DocLink: "removed-custom-reporters", + Version: "1.16.0", + } +} + +func (d deprecations) Async() Deprecation { + return Deprecation{ + Message: "You are passing a Done channel to a test node to test asynchronous behavior. This is deprecated in Ginkgo V2. Your test will run synchronously and the timeout will be ignored.", + DocLink: "removed-async-testing", + Version: "1.16.0", + } +} + +func (d deprecations) Measure() Deprecation { + return Deprecation{ + Message: "Measure is deprecated and has been removed from Ginkgo V2. Any Measure tests in your spec will not run. Please migrate to gomega/gmeasure.", + DocLink: "removed-measure", + Version: "1.16.3", + } +} + +func (d deprecations) ParallelNode() Deprecation { + return Deprecation{ + Message: "GinkgoParallelNode is deprecated and will be removed in Ginkgo V2. Please use GinkgoParallelProcess instead.", + DocLink: "renamed-ginkgoparallelnode", + Version: "1.16.4", + } +} + +func (d deprecations) CurrentGinkgoTestDescription() Deprecation { + return Deprecation{ + Message: "CurrentGinkgoTestDescription() is deprecated in Ginkgo V2. Use CurrentSpecReport() instead.", + DocLink: "changed-currentginkgotestdescription", + Version: "1.16.0", + } +} + +func (d deprecations) Convert() Deprecation { + return Deprecation{ + Message: "The convert command is deprecated in Ginkgo V2", + DocLink: "removed-ginkgo-convert", + Version: "1.16.0", + } +} + +func (d deprecations) Blur() Deprecation { + return Deprecation{ + Message: "The blur command is deprecated in Ginkgo V2. Use 'ginkgo unfocus' instead.", + Version: "1.16.0", + } +} + +func (d deprecations) Nodot() Deprecation { + return Deprecation{ + Message: "The nodot command is deprecated in Ginkgo V2. Please either dot-import Ginkgo or use the package identifier in your code to references objects and types provided by Ginkgo and Gomega.", + DocLink: "removed-ginkgo-nodot", + Version: "1.16.0", + } +} + +func (d deprecations) SuppressProgressReporting() Deprecation { + return Deprecation{ + Message: "Improvements to how reporters emit timeline information means that SuppressProgressReporting is no longer necessary and has been deprecated.", + Version: "2.5.0", + } +} + +type DeprecationTracker struct { + deprecations map[Deprecation][]CodeLocation + lock *sync.Mutex +} + +func NewDeprecationTracker() *DeprecationTracker { + return &DeprecationTracker{ + deprecations: map[Deprecation][]CodeLocation{}, + lock: &sync.Mutex{}, + } +} + +func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) { + ackVersion := os.Getenv("ACK_GINKGO_DEPRECATIONS") + if deprecation.Version != "" && ackVersion != "" { + ack := ParseSemVer(ackVersion) + version := ParseSemVer(deprecation.Version) + if ack.GreaterThanOrEqualTo(version) { + return + } + } + + d.lock.Lock() + defer d.lock.Unlock() + if len(cl) == 1 { + d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0]) + } else { + d.deprecations[deprecation] = []CodeLocation{} + } +} + +func (d *DeprecationTracker) DidTrackDeprecations() bool { + d.lock.Lock() + defer d.lock.Unlock() + return len(d.deprecations) > 0 +} + +func (d *DeprecationTracker) DeprecationsReport() string { + d.lock.Lock() + defer d.lock.Unlock() + out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") + out += formatter.F("{{light-yellow}}============================================={{/}}\n") + for deprecation, locations := range d.deprecations { + out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n") + if deprecation.DocLink != "" { + out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://onsi.github.io/ginkgo/MIGRATING_TO_V2#%s{{/}}\n", deprecation.DocLink) + } + for _, location := range locations { + out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location) + } + } + out += formatter.F("\n{{gray}}To silence deprecations that can be silenced set the following environment variable:{{/}}\n") + out += formatter.Fi(1, "{{gray}}ACK_GINKGO_DEPRECATIONS=%s{{/}}\n", VERSION) + return out +} + +type SemVer struct { + Major int + Minor int + Patch int +} + +func (s SemVer) GreaterThanOrEqualTo(o SemVer) bool { + return (s.Major > o.Major) || + (s.Major == o.Major && s.Minor > o.Minor) || + (s.Major == o.Major && s.Minor == o.Minor && s.Patch >= o.Patch) +} + +func ParseSemVer(semver string) SemVer { + out := SemVer{} + semver = strings.TrimFunc(semver, func(r rune) bool { + return !(unicode.IsNumber(r) || r == '.') + }) + components := strings.Split(semver, ".") + if len(components) > 0 { + out.Major, _ = strconv.Atoi(components[0]) + } + if len(components) > 1 { + out.Minor, _ = strconv.Atoi(components[1]) + } + if len(components) > 2 { + out.Patch, _ = strconv.Atoi(components[2]) + } + return out +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go b/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go new file mode 100644 index 000000000..1d96ae028 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go @@ -0,0 +1,43 @@ +package types + +import "encoding/json" + +type EnumSupport struct { + toString map[uint]string + toEnum map[string]uint + maxEnum uint +} + +func NewEnumSupport(toString map[uint]string) EnumSupport { + toEnum, maxEnum := map[string]uint{}, uint(0) + for k, v := range toString { + toEnum[v] = k + if maxEnum < k { + maxEnum = k + } + } + return EnumSupport{toString: toString, toEnum: toEnum, maxEnum: maxEnum} +} + +func (es EnumSupport) String(e uint) string { + if e > es.maxEnum { + return es.toString[0] + } + return es.toString[e] +} + +func (es EnumSupport) UnmarshJSON(b []byte) (uint, error) { + var dec string + if err := json.Unmarshal(b, &dec); err != nil { + return 0, err + } + out := es.toEnum[dec] // if we miss we get 0 which is what we want anyway + return out, nil +} + +func (es EnumSupport) MarshJSON(e uint) ([]byte, error) { + if e == 0 || e > es.maxEnum { + return json.Marshal(nil) + } + return json.Marshal(es.toString[e]) +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/vendor/github.com/onsi/ginkgo/v2/types/errors.go new file mode 100644 index 000000000..4fbdc3e9b --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/errors.go @@ -0,0 +1,630 @@ +package types + +import ( + "fmt" + "reflect" + "strings" + + "github.com/onsi/ginkgo/v2/formatter" +) + +type GinkgoError struct { + Heading string + Message string + DocLink string + CodeLocation CodeLocation +} + +func (g GinkgoError) Error() string { + out := formatter.F("{{bold}}{{red}}%s{{/}}\n", g.Heading) + if (g.CodeLocation != CodeLocation{}) { + contentsOfLine := strings.TrimLeft(g.CodeLocation.ContentsOfLine(), "\t ") + if contentsOfLine != "" { + out += formatter.F("{{light-gray}}%s{{/}}\n", contentsOfLine) + } + out += formatter.F("{{gray}}%s{{/}}\n", g.CodeLocation) + } + if g.Message != "" { + out += formatter.Fiw(1, formatter.COLS, g.Message) + out += "\n\n" + } + if g.DocLink != "" { + out += formatter.Fiw(1, formatter.COLS, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}http://onsi.github.io/ginkgo/#%s{{/}}\n", g.DocLink) + } + + return out +} + +type ginkgoErrors struct{} + +var GinkgoErrors = ginkgoErrors{} + +func (g ginkgoErrors) UncaughtGinkgoPanic(cl CodeLocation) error { + return GinkgoError{ + Heading: "Your Test Panicked", + Message: `When you, or your assertion library, calls Ginkgo's Fail(), +Ginkgo panics to prevent subsequent assertions from running. + +Normally Ginkgo rescues this panic so you shouldn't see it. + +However, if you make an assertion in a goroutine, Ginkgo can't capture the panic. +To circumvent this, you should call + + defer GinkgoRecover() + +at the top of the goroutine that caused this panic. + +Alternatively, you may have made an assertion outside of a Ginkgo +leaf node (e.g. in a container node or some out-of-band function) - please move your assertion to +an appropriate Ginkgo node (e.g. a BeforeSuite, BeforeEach, It, etc...).`, + DocLink: "mental-model-how-ginkgo-handles-failure", + CodeLocation: cl, + } +} + +func (g ginkgoErrors) RerunningSuite() error { + return GinkgoError{ + Heading: "Rerunning Suite", + Message: formatter.F(`It looks like you are calling RunSpecs more than once. Ginkgo does not support rerunning suites. If you want to rerun a suite try {{bold}}ginkgo --repeat=N{{/}} or {{bold}}ginkgo --until-it-fails{{/}}`), + DocLink: "repeating-spec-runs-and-managing-flaky-specs", + } +} + +/* Tree construction errors */ + +func (g ginkgoErrors) PushingNodeInRunPhase(nodeType NodeType, cl CodeLocation) error { + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F( + `It looks like you are trying to add a {{bold}}[%s]{{/}} node +to the Ginkgo spec tree in a leaf node {{bold}}after{{/}} the specs started running. + +To enable randomization and parallelization Ginkgo requires the spec tree +to be fully constructed up front. In practice, this means that you can +only create nodes like {{bold}}[%s]{{/}} at the top-level or within the +body of a {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}.`, nodeType, nodeType), + CodeLocation: cl, + DocLink: "mental-model-how-ginkgo-traverses-the-spec-hierarchy", + } +} + +func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic interface{}, cl CodeLocation) error { + return GinkgoError{ + Heading: "Assertion or Panic detected during tree construction", + Message: formatter.F( + `Ginkgo detected a panic while constructing the spec tree. +You may be trying to make an assertion in the body of a container node +(i.e. {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}). + +Please ensure all assertions are inside leaf nodes such as {{bold}}BeforeEach{{/}}, +{{bold}}It{{/}}, etc. + +{{bold}}Here's the content of the panic that was caught:{{/}} +%v`, caughtPanic), + CodeLocation: cl, + DocLink: "no-assertions-in-container-nodes", + } +} + +func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocation) error { + docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" + if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) { + docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite" + } + + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F( + `It looks like you are trying to add a {{bold}}[%s]{{/}} node within a container node. + +{{bold}}%s{{/}} can only be called at the top level.`, nodeType, nodeType), + CodeLocation: cl, + DocLink: docLink, + } +} + +func (g ginkgoErrors) SuiteNodeDuringRunPhase(nodeType NodeType, cl CodeLocation) error { + docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" + if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) { + docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite" + } + + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F( + `It looks like you are trying to add a {{bold}}[%s]{{/}} node within a leaf node after the spec started running. + +{{bold}}%s{{/}} can only be called at the top level.`, nodeType, nodeType), + CodeLocation: cl, + DocLink: docLink, + } +} + +func (g ginkgoErrors) MultipleBeforeSuiteNodes(nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { + return ginkgoErrorMultipleSuiteNodes("setup", nodeType, cl, earlierNodeType, earlierCodeLocation) +} + +func (g ginkgoErrors) MultipleAfterSuiteNodes(nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { + return ginkgoErrorMultipleSuiteNodes("teardown", nodeType, cl, earlierNodeType, earlierCodeLocation) +} + +func ginkgoErrorMultipleSuiteNodes(setupOrTeardown string, nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F( + `It looks like you are trying to add a {{bold}}[%s]{{/}} node but +you already have a {{bold}}[%s]{{/}} node defined at: {{gray}}%s{{/}}. + +Ginkgo only allows you to define one suite %s node.`, nodeType, earlierNodeType, earlierCodeLocation, setupOrTeardown), + CodeLocation: cl, + DocLink: "suite-setup-and-cleanup-beforesuite-and-aftersuite", + } +} + +/* Decorator errors */ +func (g ginkgoErrors) InvalidDecoratorForNodeType(cl CodeLocation, nodeType NodeType, decorator string) error { + return GinkgoError{ + Heading: "Invalid Decorator", + Message: formatter.F(`[%s] node cannot be passed a(n) '%s' decorator`, nodeType, decorator), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidDeclarationOfFocusedAndPending(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Invalid Combination of Decorators: Focused and Pending", + Message: formatter.F(`[%s] node was decorated with both Focus and Pending. At most one is allowed.`, nodeType), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Invalid Combination of Decorators: FlakeAttempts and MustPassRepeatedly", + Message: formatter.F(`[%s] node was decorated with both FlakeAttempts and MustPassRepeatedly. At most one is allowed.`, nodeType), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator interface{}) error { + return GinkgoError{ + Heading: "Unknown Decorator", + Message: formatter.F(`[%s] node was passed an unknown decorator: '%#v'`, nodeType, decorator), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidBodyTypeForContainer(t reflect.Type, cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Invalid Function", + Message: formatter.F(`[%s] node must be passed {{bold}}func(){{/}} - i.e. functions that take nothing and return nothing. You passed {{bold}}%s{{/}} instead.`, nodeType, t), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidBodyType(t reflect.Type, cl CodeLocation, nodeType NodeType) error { + mustGet := "{{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}" + if nodeType.Is(NodeTypeContainer) { + mustGet = "{{bold}}func(){{/}}" + } + return GinkgoError{ + Heading: "Invalid Function", + Message: formatter.F(`[%s] node must be passed `+mustGet+`. +You passed {{bold}}%s{{/}} instead.`, nodeType, t), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidBodyTypeForSynchronizedBeforeSuiteProc1(t reflect.Type, cl CodeLocation) error { + mustGet := "{{bold}}func() []byte{{/}}, {{bold}}func(ctx SpecContext) []byte{{/}}, or {{bold}}func(ctx context.Context) []byte{{/}}, {{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}" + return GinkgoError{ + Heading: "Invalid Function", + Message: formatter.F(`[SynchronizedBeforeSuite] node must be passed `+mustGet+` for its first function. +You passed {{bold}}%s{{/}} instead.`, t), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidBodyTypeForSynchronizedBeforeSuiteAllProcs(t reflect.Type, cl CodeLocation) error { + mustGet := "{{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}, {{bold}}func([]byte){{/}}, {{bold}}func(ctx SpecContext, []byte){{/}}, or {{bold}}func(ctx context.Context, []byte){{/}}" + return GinkgoError{ + Heading: "Invalid Function", + Message: formatter.F(`[SynchronizedBeforeSuite] node must be passed `+mustGet+` for its second function. +You passed {{bold}}%s{{/}} instead.`, t), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) MultipleBodyFunctions(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Multiple Functions", + Message: formatter.F(`[%s] node must be passed a single function - but more than one was passed in.`, nodeType), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) MissingBodyFunction(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Missing Functions", + Message: formatter.F(`[%s] node must be passed a single function - but none was passed in.`, nodeType), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) InvalidTimeoutOrGracePeriodForNonContextNode(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Invalid NodeTimeout SpecTimeout, or GracePeriod", + Message: formatter.F(`[%s] was passed NodeTimeout, SpecTimeout, or GracePeriod but does not have a callback that accepts a {{bold}}SpecContext{{/}} or {{bold}}context.Context{{/}}. You must accept a context to enable timeouts and grace periods`, nodeType), + CodeLocation: cl, + DocLink: "spec-timeouts-and-interruptible-nodes", + } +} + +func (g ginkgoErrors) InvalidTimeoutOrGracePeriodForNonContextCleanupNode(cl CodeLocation) error { + return GinkgoError{ + Heading: "Invalid NodeTimeout SpecTimeout, or GracePeriod", + Message: formatter.F(`[DeferCleanup] was passed NodeTimeout or GracePeriod but does not have a callback that accepts a {{bold}}SpecContext{{/}} or {{bold}}context.Context{{/}}. You must accept a context to enable timeouts and grace periods`), + CodeLocation: cl, + DocLink: "spec-timeouts-and-interruptible-nodes", + } +} + +/* Ordered Container errors */ +func (g ginkgoErrors) InvalidSerialNodeInNonSerialOrderedContainer(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Invalid Serial Node in Non-Serial Ordered Container", + Message: formatter.F(`[%s] node was decorated with Serial but occurs in an Ordered container that is not marked Serial. Move the Serial decorator to the outer-most Ordered container to mark all ordered specs within the container as serial.`, nodeType), + CodeLocation: cl, + DocLink: "node-decorators-overview", + } +} + +func (g ginkgoErrors) SetupNodeNotInOrderedContainer(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: "Setup Node not in Ordered Container", + Message: fmt.Sprintf("[%s] setup nodes must appear inside an Ordered container. They cannot be nested within other containers, even containers in an ordered container.", nodeType), + CodeLocation: cl, + DocLink: "ordered-containers", + } +} + +func (g ginkgoErrors) InvalidContinueOnFailureDecoration(cl CodeLocation) error { + return GinkgoError{ + Heading: "ContinueOnFailure not decorating an outermost Ordered Container", + Message: "ContinueOnFailure can only decorate an Ordered container, and this Ordered container must be the outermost Ordered container.", + CodeLocation: cl, + DocLink: "ordered-containers", + } +} + +/* DeferCleanup errors */ +func (g ginkgoErrors) DeferCleanupInvalidFunction(cl CodeLocation) error { + return GinkgoError{ + Heading: "DeferCleanup requires a valid function", + Message: "You must pass DeferCleanup a function to invoke. This function must return zero or one values - if it does return, it must return an error. The function can take arbitrarily many arguments and you should provide these to DeferCleanup to pass along to the function.", + CodeLocation: cl, + DocLink: "cleaning-up-our-cleanup-code-defercleanup", + } +} + +func (g ginkgoErrors) PushingCleanupNodeDuringTreeConstruction(cl CodeLocation) error { + return GinkgoError{ + Heading: "DeferCleanup must be called inside a setup or subject node", + Message: "You must call DeferCleanup inside a setup node (e.g. BeforeEach, BeforeSuite, AfterAll...) or a subject node (i.e. It). You can't call DeferCleanup at the top-level or in a container node - use the After* family of setup nodes instead.", + CodeLocation: cl, + DocLink: "cleaning-up-our-cleanup-code-defercleanup", + } +} + +func (g ginkgoErrors) PushingCleanupInReportingNode(cl CodeLocation, nodeType NodeType) error { + return GinkgoError{ + Heading: fmt.Sprintf("DeferCleanup cannot be called in %s", nodeType), + Message: "Please inline your cleanup code - Ginkgo won't run cleanup code after a Reporting node.", + CodeLocation: cl, + DocLink: "cleaning-up-our-cleanup-code-defercleanup", + } +} + +func (g ginkgoErrors) PushingCleanupInCleanupNode(cl CodeLocation) error { + return GinkgoError{ + Heading: "DeferCleanup cannot be called in a DeferCleanup callback", + Message: "Please inline your cleanup code - Ginkgo doesn't let you call DeferCleanup from within DeferCleanup", + CodeLocation: cl, + DocLink: "cleaning-up-our-cleanup-code-defercleanup", + } +} + +/* ReportEntry errors */ +func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg interface{}) error { + return GinkgoError{ + Heading: "Too Many ReportEntry Values", + Message: formatter.F(`{{bold}}AddGinkgoReport{{/}} can only be given one value. Got unexpected value: %#v`, arg), + CodeLocation: cl, + DocLink: "attaching-data-to-reports", + } +} + +func (g ginkgoErrors) AddReportEntryNotDuringRunPhase(cl CodeLocation) error { + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F(`It looks like you are calling {{bold}}AddGinkgoReport{{/}} outside of a running spec. Make sure you call {{bold}}AddGinkgoReport{{/}} inside a runnable node such as It or BeforeEach and not inside the body of a container such as Describe or Context.`), + CodeLocation: cl, + DocLink: "attaching-data-to-reports", + } +} + +/* By errors */ +func (g ginkgoErrors) ByNotDuringRunPhase(cl CodeLocation) error { + return GinkgoError{ + Heading: "Ginkgo detected an issue with your spec structure", + Message: formatter.F(`It looks like you are calling {{bold}}By{{/}} outside of a running spec. Make sure you call {{bold}}By{{/}} inside a runnable node such as It or BeforeEach and not inside the body of a container such as Describe or Context.`), + CodeLocation: cl, + DocLink: "documenting-complex-specs-by", + } +} + +/* FileFilter and SkipFilter errors */ +func (g ginkgoErrors) InvalidFileFilter(filter string) error { + return GinkgoError{ + Heading: "Invalid File Filter", + Message: fmt.Sprintf(`The provided file filter: "%s" is invalid. File filters must have the format "file", "file:lines" where "file" is a regular expression that will match against the file path and lines is a comma-separated list of integers (e.g. file:1,5,7) or line-ranges (e.g. file:1-3,5-9) or both (e.g. file:1,5-9)`, filter), + DocLink: "filtering-specs", + } +} + +func (g ginkgoErrors) InvalidFileFilterRegularExpression(filter string, err error) error { + return GinkgoError{ + Heading: "Invalid File Filter Regular Expression", + Message: fmt.Sprintf(`The provided file filter: "%s" included an invalid regular expression. regexp.Compile error: %s`, filter, err), + DocLink: "filtering-specs", + } +} + +/* Label Errors */ +func (g ginkgoErrors) SyntaxErrorParsingLabelFilter(input string, location int, error string) error { + var message string + if location >= 0 { + for i, r := range input { + if i == location { + message += "{{red}}{{bold}}{{underline}}" + } + message += string(r) + if i == location { + message += "{{/}}" + } + } + } else { + message = input + } + message += "\n" + error + return GinkgoError{ + Heading: "Syntax Error Parsing Label Filter", + Message: message, + DocLink: "spec-labels", + } +} + +func (g ginkgoErrors) InvalidLabel(label string, cl CodeLocation) error { + return GinkgoError{ + Heading: "Invalid Label", + Message: fmt.Sprintf("'%s' is an invalid label. Labels cannot contain of the following characters: '&|!,()/'", label), + CodeLocation: cl, + DocLink: "spec-labels", + } +} + +func (g ginkgoErrors) InvalidEmptyLabel(cl CodeLocation) error { + return GinkgoError{ + Heading: "Invalid Empty Label", + Message: "Labels cannot be empty", + CodeLocation: cl, + DocLink: "spec-labels", + } +} + +/* Table errors */ +func (g ginkgoErrors) MultipleEntryBodyFunctionsForTable(cl CodeLocation) error { + return GinkgoError{ + Heading: "DescribeTable passed multiple functions", + Message: "It looks like you are passing multiple functions into DescribeTable. Only one function can be passed in. This function will be called for each Entry in the table.", + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) InvalidEntryDescription(cl CodeLocation) error { + return GinkgoError{ + Heading: "Invalid Entry description", + Message: "Entry description functions must be a string, a function that accepts the entry parameters and returns a string, or nil.", + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) MissingParametersForTableFunction(cl CodeLocation) error { + return GinkgoError{ + Heading: "No parameters have been passed to the Table Function", + Message: "The Table Function expected at least 1 parameter", + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) IncorrectParameterTypeForTable(i int, name string, cl CodeLocation) error { + return GinkgoError{ + Heading: "DescribeTable passed incorrect parameter type", + Message: fmt.Sprintf("Parameter #%d passed to DescribeTable is of incorrect type <%s>", i, name), + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) TooFewParametersToTableFunction(expected, actual int, kind string, cl CodeLocation) error { + return GinkgoError{ + Heading: fmt.Sprintf("Too few parameters passed in to %s", kind), + Message: fmt.Sprintf("The %s expected %d parameters but you passed in %d", kind, expected, actual), + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) TooManyParametersToTableFunction(expected, actual int, kind string, cl CodeLocation) error { + return GinkgoError{ + Heading: fmt.Sprintf("Too many parameters passed in to %s", kind), + Message: fmt.Sprintf("The %s expected %d parameters but you passed in %d", kind, expected, actual), + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) IncorrectParameterTypeToTableFunction(i int, expected, actual reflect.Type, kind string, cl CodeLocation) error { + return GinkgoError{ + Heading: fmt.Sprintf("Incorrect parameters type passed to %s", kind), + Message: fmt.Sprintf("The %s expected parameter #%d to be of type <%s> but you passed in <%s>", kind, i, expected, actual), + CodeLocation: cl, + DocLink: "table-specs", + } +} + +func (g ginkgoErrors) IncorrectVariadicParameterTypeToTableFunction(expected, actual reflect.Type, kind string, cl CodeLocation) error { + return GinkgoError{ + Heading: fmt.Sprintf("Incorrect parameters type passed to %s", kind), + Message: fmt.Sprintf("The %s expected its variadic parameters to be of type <%s> but you passed in <%s>", kind, expected, actual), + CodeLocation: cl, + DocLink: "table-specs", + } +} + +/* Parallel Synchronization errors */ + +func (g ginkgoErrors) AggregatedReportUnavailableDueToNodeDisappearing() error { + return GinkgoError{ + Heading: "Test Report unavailable because a Ginkgo parallel process disappeared", + Message: "The aggregated report could not be fetched for a ReportAfterSuite node. A Ginkgo parallel process disappeared before it could finish reporting.", + } +} + +func (g ginkgoErrors) SynchronizedBeforeSuiteFailedOnProc1() error { + return GinkgoError{ + Heading: "SynchronizedBeforeSuite failed on Ginkgo parallel process #1", + Message: "The first SynchronizedBeforeSuite function running on Ginkgo parallel process #1 failed. This suite will now abort.", + } +} + +func (g ginkgoErrors) SynchronizedBeforeSuiteDisappearedOnProc1() error { + return GinkgoError{ + Heading: "Process #1 disappeared before SynchronizedBeforeSuite could report back", + Message: "Ginkgo parallel process #1 disappeared before the first SynchronizedBeforeSuite function completed. This suite will now abort.", + } +} + +/* Configuration errors */ + +func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value interface{}) 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), + } +} + +var sharedParallelErrorMessage = "It looks like you are trying to run specs in parallel with go test.\nThis is unsupported and you should use the ginkgo CLI instead." + +func (g ginkgoErrors) InvalidParallelTotalConfiguration() error { + return GinkgoError{ + Heading: "-ginkgo.parallel.total must be >= 1", + Message: sharedParallelErrorMessage, + DocLink: "spec-parallelization", + } +} + +func (g ginkgoErrors) InvalidParallelProcessConfiguration() error { + return GinkgoError{ + Heading: "-ginkgo.parallel.process is one-indexed and must be <= ginkgo.parallel.total", + Message: sharedParallelErrorMessage, + DocLink: "spec-parallelization", + } +} + +func (g ginkgoErrors) MissingParallelHostConfiguration() error { + return GinkgoError{ + Heading: "-ginkgo.parallel.host is missing", + Message: sharedParallelErrorMessage, + DocLink: "spec-parallelization", + } +} + +func (g ginkgoErrors) UnreachableParallelHost(host string) error { + return GinkgoError{ + Heading: "Could not reach ginkgo.parallel.host:" + host, + Message: sharedParallelErrorMessage, + DocLink: "spec-parallelization", + } +} + +func (g ginkgoErrors) DryRunInParallelConfiguration() error { + return GinkgoError{ + Heading: "Ginkgo only performs -dryRun in serial mode.", + Message: "Please try running ginkgo -dryRun again, but without -p or -procs to ensure the suite is running in series.", + } +} + +func (g ginkgoErrors) GracePeriodCannotBeZero() error { + return GinkgoError{ + Heading: "Ginkgo requires a positive --grace-period.", + Message: "Please set --grace-period to a positive duration. The default is 30s.", + } +} + +func (g ginkgoErrors) ConflictingVerbosityConfiguration() error { + return GinkgoError{ + Heading: "Conflicting reporter verbosity settings.", + Message: "You can't set more than one of -v, -vv and --succinct. Please pick one!", + } +} + +func (g ginkgoErrors) InvalidOutputInterceptorModeConfiguration(value string) error { + return GinkgoError{ + Heading: fmt.Sprintf("Invalid value '%s' for --output-interceptor-mode.", value), + Message: "You must choose one of 'dup', 'swap', or 'none'.", + } +} + +func (g ginkgoErrors) InvalidGoFlagCount() error { + return GinkgoError{ + Heading: "Use of go test -count", + Message: "Ginkgo does not support using go test -count to rerun suites. Only -count=1 is allowed. To repeat suite runs, please use the ginkgo cli and `ginkgo -until-it-fails` or `ginkgo -repeat=N`.", + } +} + +func (g ginkgoErrors) InvalidGoFlagParallel() error { + return GinkgoError{ + Heading: "Use of go test -parallel", + Message: "Go test's implementation of parallelization does not actually parallelize Ginkgo specs. Please use the ginkgo cli and `ginkgo -p` or `ginkgo -procs=N` instead.", + } +} + +func (g ginkgoErrors) BothRepeatAndUntilItFails() error { + return GinkgoError{ + Heading: "--repeat and --until-it-fails are both set", + Message: "--until-it-fails directs Ginkgo to rerun specs indefinitely until they fail. --repeat directs Ginkgo to rerun specs a set number of times. You can't set both... which would you like?", + } +} + +/* Stack-Trace parsing errors */ + +func (g ginkgoErrors) FailedToParseStackTrace(message string) error { + return GinkgoError{ + Heading: "Failed to Parse Stack Trace", + Message: message, + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go b/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go new file mode 100644 index 000000000..cc21df71e --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go @@ -0,0 +1,106 @@ +package types + +import ( + "regexp" + "strconv" + "strings" +) + +func ParseFileFilters(filters []string) (FileFilters, error) { + ffs := FileFilters{} + for _, filter := range filters { + ff := FileFilter{} + if filter == "" { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + components := strings.Split(filter, ":") + if !(len(components) == 1 || len(components) == 2) { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + + var err error + ff.Filename, err = regexp.Compile(components[0]) + if err != nil { + return nil, err + } + if len(components) == 2 { + lineFilters := strings.Split(components[1], ",") + for _, lineFilter := range lineFilters { + components := strings.Split(lineFilter, "-") + if len(components) == 1 { + line, err := strconv.Atoi(strings.TrimSpace(components[0])) + if err != nil { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + ff.LineFilters = append(ff.LineFilters, LineFilter{line, line + 1}) + } else if len(components) == 2 { + line1, err := strconv.Atoi(strings.TrimSpace(components[0])) + if err != nil { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + line2, err := strconv.Atoi(strings.TrimSpace(components[1])) + if err != nil { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + ff.LineFilters = append(ff.LineFilters, LineFilter{line1, line2}) + } else { + return nil, GinkgoErrors.InvalidFileFilter(filter) + } + } + } + ffs = append(ffs, ff) + } + return ffs, nil +} + +type FileFilter struct { + Filename *regexp.Regexp + LineFilters LineFilters +} + +func (f FileFilter) Matches(locations []CodeLocation) bool { + for _, location := range locations { + if f.Filename.MatchString(location.FileName) && + f.LineFilters.Matches(location.LineNumber) { + return true + } + + } + return false +} + +type FileFilters []FileFilter + +func (ffs FileFilters) Matches(locations []CodeLocation) bool { + for _, ff := range ffs { + if ff.Matches(locations) { + return true + } + } + + return false +} + +type LineFilter struct { + Min int + Max int +} + +func (lf LineFilter) Matches(line int) bool { + return lf.Min <= line && line < lf.Max +} + +type LineFilters []LineFilter + +func (lfs LineFilters) Matches(line int) bool { + if len(lfs) == 0 { + return true + } + + for _, lf := range lfs { + if lf.Matches(line) { + return true + } + } + return false +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/flags.go b/vendor/github.com/onsi/ginkgo/v2/types/flags.go new file mode 100644 index 000000000..9186ae873 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/flags.go @@ -0,0 +1,489 @@ +package types + +import ( + "flag" + "fmt" + "io" + "reflect" + "strings" + "time" + + "github.com/onsi/ginkgo/v2/formatter" +) + +type GinkgoFlag struct { + Name string + KeyPath string + SectionKey string + + Usage string + UsageArgument string + UsageDefaultValue string + + DeprecatedName string + DeprecatedDocLink string + DeprecatedVersion string + + ExportAs string +} + +type GinkgoFlags []GinkgoFlag + +func (f GinkgoFlags) CopyAppend(flags ...GinkgoFlag) GinkgoFlags { + out := GinkgoFlags{} + out = append(out, f...) + out = append(out, flags...) + return out +} + +func (f GinkgoFlags) WithPrefix(prefix string) GinkgoFlags { + if prefix == "" { + return f + } + out := GinkgoFlags{} + for _, flag := range f { + if flag.Name != "" { + flag.Name = prefix + "." + flag.Name + } + if flag.DeprecatedName != "" { + flag.DeprecatedName = prefix + "." + flag.DeprecatedName + } + if flag.ExportAs != "" { + flag.ExportAs = prefix + "." + flag.ExportAs + } + out = append(out, flag) + } + return out +} + +func (f GinkgoFlags) SubsetWithNames(names ...string) GinkgoFlags { + out := GinkgoFlags{} + for _, flag := range f { + for _, name := range names { + if flag.Name == name { + out = append(out, flag) + break + } + } + } + return out +} + +type GinkgoFlagSection struct { + Key string + Style string + Succinct bool + Heading string + Description string +} + +type GinkgoFlagSections []GinkgoFlagSection + +func (gfs GinkgoFlagSections) Lookup(key string) (GinkgoFlagSection, bool) { + for _, section := range gfs { + if section.Key == key { + return section, true + } + } + + return GinkgoFlagSection{}, false +} + +type GinkgoFlagSet struct { + flags GinkgoFlags + bindings interface{} + + sections GinkgoFlagSections + extraGoFlagsSection GinkgoFlagSection + + flagSet *flag.FlagSet +} + +// 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) { + return bindFlagSet(GinkgoFlagSet{ + flags: flags, + bindings: bindings, + sections: sections, + }, nil) +} + +// 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) { + return bindFlagSet(GinkgoFlagSet{ + flags: flags, + bindings: bindings, + sections: sections, + extraGoFlagsSection: extraGoFlagsSection, + }, flagSet) +} + +func bindFlagSet(f GinkgoFlagSet, flagSet *flag.FlagSet) (GinkgoFlagSet, error) { + if flagSet == nil { + f.flagSet = flag.NewFlagSet("", flag.ContinueOnError) + //suppress all output as Ginkgo is responsible for formatting usage + f.flagSet.SetOutput(io.Discard) + } else { + f.flagSet = flagSet + //we're piggybacking on an existing flagset (typically go test) so we have limited control + //on user feedback + f.flagSet.Usage = f.substituteUsage + } + + for _, flag := range f.flags { + name := flag.Name + + deprecatedUsage := "[DEPRECATED]" + deprecatedName := flag.DeprecatedName + if name != "" { + deprecatedUsage = fmt.Sprintf("[DEPRECATED] use --%s instead", name) + } else if flag.Usage != "" { + deprecatedUsage += " " + flag.Usage + } + + value, ok := valueAtKeyPath(f.bindings, flag.KeyPath) + if !ok { + return GinkgoFlagSet{}, fmt.Errorf("could not load KeyPath: %s", flag.KeyPath) + } + + iface, addr := value.Interface(), value.Addr().Interface() + + switch value.Type() { + case reflect.TypeOf(string("")): + if name != "" { + f.flagSet.StringVar(addr.(*string), name, iface.(string), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.StringVar(addr.(*string), deprecatedName, iface.(string), deprecatedUsage) + } + case reflect.TypeOf(int64(0)): + if name != "" { + f.flagSet.Int64Var(addr.(*int64), name, iface.(int64), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.Int64Var(addr.(*int64), deprecatedName, iface.(int64), deprecatedUsage) + } + case reflect.TypeOf(float64(0)): + if name != "" { + f.flagSet.Float64Var(addr.(*float64), name, iface.(float64), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.Float64Var(addr.(*float64), deprecatedName, iface.(float64), deprecatedUsage) + } + case reflect.TypeOf(int(0)): + if name != "" { + f.flagSet.IntVar(addr.(*int), name, iface.(int), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.IntVar(addr.(*int), deprecatedName, iface.(int), deprecatedUsage) + } + case reflect.TypeOf(bool(true)): + if name != "" { + f.flagSet.BoolVar(addr.(*bool), name, iface.(bool), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.BoolVar(addr.(*bool), deprecatedName, iface.(bool), deprecatedUsage) + } + case reflect.TypeOf(time.Duration(0)): + if name != "" { + f.flagSet.DurationVar(addr.(*time.Duration), name, iface.(time.Duration), flag.Usage) + } + if deprecatedName != "" { + f.flagSet.DurationVar(addr.(*time.Duration), deprecatedName, iface.(time.Duration), deprecatedUsage) + } + + case reflect.TypeOf([]string{}): + if name != "" { + f.flagSet.Var(stringSliceVar{value}, name, flag.Usage) + } + if deprecatedName != "" { + f.flagSet.Var(stringSliceVar{value}, deprecatedName, deprecatedUsage) + } + default: + return GinkgoFlagSet{}, fmt.Errorf("unsupported type %T", iface) + } + } + + return f, nil +} + +func (f GinkgoFlagSet) IsZero() bool { + return f.flagSet == nil +} + +func (f GinkgoFlagSet) WasSet(name string) bool { + found := false + f.flagSet.Visit(func(f *flag.Flag) { + if f.Name == name { + found = true + } + }) + + return found +} + +func (f GinkgoFlagSet) Lookup(name string) *flag.Flag { + return f.flagSet.Lookup(name) +} + +func (f GinkgoFlagSet) Parse(args []string) ([]string, error) { + if f.IsZero() { + return args, nil + } + err := f.flagSet.Parse(args) + if err != nil { + return []string{}, err + } + return f.flagSet.Args(), nil +} + +func (f GinkgoFlagSet) ValidateDeprecations(deprecationTracker *DeprecationTracker) { + if f.IsZero() { + return + } + f.flagSet.Visit(func(flag *flag.Flag) { + for _, ginkgoFlag := range f.flags { + if ginkgoFlag.DeprecatedName != "" && strings.HasSuffix(flag.Name, ginkgoFlag.DeprecatedName) { + message := fmt.Sprintf("--%s is deprecated", ginkgoFlag.DeprecatedName) + if ginkgoFlag.Name != "" { + message = fmt.Sprintf("--%s is deprecated, use --%s instead", ginkgoFlag.DeprecatedName, ginkgoFlag.Name) + } else if ginkgoFlag.Usage != "" { + message += " " + ginkgoFlag.Usage + } + + deprecationTracker.TrackDeprecation(Deprecation{ + Message: message, + DocLink: ginkgoFlag.DeprecatedDocLink, + Version: ginkgoFlag.DeprecatedVersion, + }) + } + } + }) +} + +func (f GinkgoFlagSet) Usage() string { + if f.IsZero() { + return "" + } + groupedFlags := map[GinkgoFlagSection]GinkgoFlags{} + ungroupedFlags := GinkgoFlags{} + managedFlags := map[string]bool{} + extraGoFlags := []*flag.Flag{} + + for _, flag := range f.flags { + managedFlags[flag.Name] = true + managedFlags[flag.DeprecatedName] = true + + if flag.Name == "" { + continue + } + + section, ok := f.sections.Lookup(flag.SectionKey) + if ok { + groupedFlags[section] = append(groupedFlags[section], flag) + } else { + ungroupedFlags = append(ungroupedFlags, flag) + } + } + + f.flagSet.VisitAll(func(flag *flag.Flag) { + if !managedFlags[flag.Name] { + extraGoFlags = append(extraGoFlags, flag) + } + }) + + out := "" + for _, section := range f.sections { + flags := groupedFlags[section] + if len(flags) == 0 { + continue + } + out += f.usageForSection(section) + if section.Succinct { + succinctFlags := []string{} + for _, flag := range flags { + if flag.Name != "" { + succinctFlags = append(succinctFlags, fmt.Sprintf("--%s", flag.Name)) + } + } + out += formatter.Fiw(1, formatter.COLS, section.Style+strings.Join(succinctFlags, ", ")+"{{/}}\n") + } else { + for _, flag := range flags { + out += f.usageForFlag(flag, section.Style) + } + } + out += "\n" + } + if len(ungroupedFlags) > 0 { + for _, flag := range ungroupedFlags { + out += f.usageForFlag(flag, "") + } + out += "\n" + } + if len(extraGoFlags) > 0 { + out += f.usageForSection(f.extraGoFlagsSection) + for _, goFlag := range extraGoFlags { + out += f.usageForGoFlag(goFlag) + } + } + + return out +} + +func (f GinkgoFlagSet) substituteUsage() { + fmt.Fprintln(f.flagSet.Output(), f.Usage()) +} + +func valueAtKeyPath(root interface{}, keyPath string) (reflect.Value, bool) { + if len(keyPath) == 0 { + return reflect.Value{}, false + } + + val := reflect.ValueOf(root) + components := strings.Split(keyPath, ".") + for _, component := range components { + val = reflect.Indirect(val) + switch val.Kind() { + case reflect.Map: + val = val.MapIndex(reflect.ValueOf(component)) + if val.Kind() == reflect.Interface { + val = reflect.ValueOf(val.Interface()) + } + case reflect.Struct: + val = val.FieldByName(component) + default: + return reflect.Value{}, false + } + if (val == reflect.Value{}) { + return reflect.Value{}, false + } + } + + return val, true +} + +func (f GinkgoFlagSet) usageForSection(section GinkgoFlagSection) string { + out := formatter.F(section.Style + "{{bold}}{{underline}}" + section.Heading + "{{/}}\n") + if section.Description != "" { + out += formatter.Fiw(0, formatter.COLS, section.Description+"\n") + } + return out +} + +func (f GinkgoFlagSet) usageForFlag(flag GinkgoFlag, style string) string { + argument := flag.UsageArgument + defValue := flag.UsageDefaultValue + if argument == "" { + value, _ := valueAtKeyPath(f.bindings, flag.KeyPath) + switch value.Type() { + case reflect.TypeOf(string("")): + argument = "string" + case reflect.TypeOf(int64(0)), reflect.TypeOf(int(0)): + argument = "int" + case reflect.TypeOf(time.Duration(0)): + argument = "duration" + case reflect.TypeOf(float64(0)): + argument = "float" + case reflect.TypeOf([]string{}): + argument = "string" + } + } + if argument != "" { + argument = "[" + argument + "] " + } + if defValue != "" { + defValue = fmt.Sprintf("(default: %s)", defValue) + } + hyphens := "--" + if len(flag.Name) == 1 { + hyphens = "-" + } + + out := formatter.Fi(1, style+"%s%s{{/}} %s{{gray}}%s{{/}}\n", hyphens, flag.Name, argument, defValue) + out += formatter.Fiw(2, formatter.COLS, "{{light-gray}}%s{{/}}\n", flag.Usage) + return out +} + +func (f GinkgoFlagSet) usageForGoFlag(goFlag *flag.Flag) string { + //Taken directly from the flag package + out := fmt.Sprintf(" -%s", goFlag.Name) + name, usage := flag.UnquoteUsage(goFlag) + if len(name) > 0 { + out += " " + name + } + if len(out) <= 4 { + out += "\t" + } else { + out += "\n \t" + } + out += strings.ReplaceAll(usage, "\n", "\n \t") + out += "\n" + return out +} + +type stringSliceVar struct { + slice reflect.Value +} + +func (ssv stringSliceVar) String() string { return "" } +func (ssv stringSliceVar) Set(s string) error { + ssv.slice.Set(reflect.AppendSlice(ssv.slice, reflect.ValueOf([]string{s}))) + 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) { + result := []string{} + for _, flag := range flags { + name := flag.ExportAs + if name == "" { + name = flag.Name + } + if name == "" { + continue + } + + value, ok := valueAtKeyPath(bindings, flag.KeyPath) + if !ok { + return []string{}, fmt.Errorf("could not load KeyPath: %s", flag.KeyPath) + } + + iface := value.Interface() + switch value.Type() { + case reflect.TypeOf(string("")): + if iface.(string) != "" { + result = append(result, fmt.Sprintf("--%s=%s", name, iface)) + } + case reflect.TypeOf(int64(0)): + if iface.(int64) != 0 { + result = append(result, fmt.Sprintf("--%s=%d", name, iface)) + } + case reflect.TypeOf(float64(0)): + if iface.(float64) != 0 { + result = append(result, fmt.Sprintf("--%s=%f", name, iface)) + } + case reflect.TypeOf(int(0)): + if iface.(int) != 0 { + result = append(result, fmt.Sprintf("--%s=%d", name, iface)) + } + case reflect.TypeOf(bool(true)): + if iface.(bool) { + result = append(result, fmt.Sprintf("--%s", name)) + } + case reflect.TypeOf(time.Duration(0)): + if iface.(time.Duration) != time.Duration(0) { + result = append(result, fmt.Sprintf("--%s=%s", name, iface)) + } + + case reflect.TypeOf([]string{}): + strings := iface.([]string) + for _, s := range strings { + result = append(result, fmt.Sprintf("--%s=%s", name, s)) + } + default: + return []string{}, fmt.Errorf("unsupported type %T", iface) + } + } + + return result, nil +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go b/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go new file mode 100644 index 000000000..b0d3b651e --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go @@ -0,0 +1,358 @@ +package types + +import ( + "fmt" + "regexp" + "strings" +) + +var DEBUG_LABEL_FILTER_PARSING = false + +type LabelFilter func([]string) bool + +func matchLabelAction(label string) LabelFilter { + expected := strings.ToLower(label) + return func(labels []string) bool { + for i := range labels { + if strings.ToLower(labels[i]) == expected { + return true + } + } + return false + } +} + +func matchLabelRegexAction(regex *regexp.Regexp) LabelFilter { + return func(labels []string) bool { + for i := range labels { + if regex.MatchString(labels[i]) { + return true + } + } + return false + } +} + +func notAction(filter LabelFilter) LabelFilter { + return func(labels []string) bool { return !filter(labels) } +} + +func andAction(a, b LabelFilter) LabelFilter { + return func(labels []string) bool { return a(labels) && b(labels) } +} + +func orAction(a, b LabelFilter) LabelFilter { + return func(labels []string) bool { return a(labels) || b(labels) } +} + +type lfToken uint + +const ( + lfTokenInvalid lfToken = iota + + lfTokenRoot + lfTokenOpenGroup + lfTokenCloseGroup + lfTokenNot + lfTokenAnd + lfTokenOr + lfTokenRegexp + lfTokenLabel + lfTokenEOF +) + +func (l lfToken) Precedence() int { + switch l { + case lfTokenRoot, lfTokenOpenGroup: + return 0 + case lfTokenOr: + return 1 + case lfTokenAnd: + return 2 + case lfTokenNot: + return 3 + } + return -1 +} + +func (l lfToken) String() string { + switch l { + case lfTokenRoot: + return "ROOT" + case lfTokenOpenGroup: + return "(" + case lfTokenCloseGroup: + return ")" + case lfTokenNot: + return "!" + case lfTokenAnd: + return "&&" + case lfTokenOr: + return "||" + case lfTokenRegexp: + return "/regexp/" + case lfTokenLabel: + return "label" + case lfTokenEOF: + return "EOF" + } + return "INVALID" +} + +type treeNode struct { + token lfToken + location int + value string + + parent *treeNode + leftNode *treeNode + rightNode *treeNode +} + +func (tn *treeNode) setRightNode(node *treeNode) { + tn.rightNode = node + node.parent = tn +} + +func (tn *treeNode) setLeftNode(node *treeNode) { + tn.leftNode = node + node.parent = tn +} + +func (tn *treeNode) firstAncestorWithPrecedenceLEQ(precedence int) *treeNode { + if tn.token.Precedence() <= precedence { + return tn + } + return tn.parent.firstAncestorWithPrecedenceLEQ(precedence) +} + +func (tn *treeNode) firstUnmatchedOpenNode() *treeNode { + if tn.token == lfTokenOpenGroup { + return tn + } + if tn.parent == nil { + return nil + } + return tn.parent.firstUnmatchedOpenNode() +} + +func (tn *treeNode) constructLabelFilter(input string) (LabelFilter, error) { + switch tn.token { + case lfTokenOpenGroup: + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, "Mismatched '(' - could not find matching ')'.") + case lfTokenLabel: + return matchLabelAction(tn.value), nil + case lfTokenRegexp: + re, err := regexp.Compile(tn.value) + if err != nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("RegExp compilation error: %s", err)) + } + return matchLabelRegexAction(re), nil + } + + if tn.rightNode == nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, -1, "Unexpected EOF.") + } + rightLF, err := tn.rightNode.constructLabelFilter(input) + if err != nil { + return nil, err + } + + switch tn.token { + case lfTokenRoot, lfTokenCloseGroup: + return rightLF, nil + case lfTokenNot: + return notAction(rightLF), nil + } + + if tn.leftNode == nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Malformed tree - '%s' is missing left operand.", tn.token)) + } + leftLF, err := tn.leftNode.constructLabelFilter(input) + if err != nil { + return nil, err + } + + switch tn.token { + case lfTokenAnd: + return andAction(leftLF, rightLF), nil + case lfTokenOr: + return orAction(leftLF, rightLF), nil + } + + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Invalid token '%s'.", tn.token)) +} + +func (tn *treeNode) tokenString() string { + out := fmt.Sprintf("<%s", tn.token) + if tn.value != "" { + out += " | " + tn.value + } + out += ">" + return out +} + +func (tn *treeNode) toString(indent int) string { + out := tn.tokenString() + "\n" + if tn.leftNode != nil { + out += fmt.Sprintf("%s |_(L)_%s", strings.Repeat(" ", indent), tn.leftNode.toString(indent+1)) + } + if tn.rightNode != nil { + out += fmt.Sprintf("%s |_(R)_%s", strings.Repeat(" ", indent), tn.rightNode.toString(indent+1)) + } + return out +} + +func tokenize(input string) func() (*treeNode, error) { + runes, i := []rune(input), 0 + + peekIs := func(r rune) bool { + if i+1 < len(runes) { + return runes[i+1] == r + } + return false + } + + consumeUntil := func(cutset string) (string, int) { + j := i + for ; j < len(runes); j++ { + if strings.IndexRune(cutset, runes[j]) >= 0 { + break + } + } + return string(runes[i:j]), j - i + } + + return func() (*treeNode, error) { + for i < len(runes) && runes[i] == ' ' { + i += 1 + } + + if i >= len(runes) { + return &treeNode{token: lfTokenEOF}, nil + } + + node := &treeNode{location: i} + switch runes[i] { + case '&': + if !peekIs('&') { + return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Invalid token '&'. Did you mean '&&'?") + } + i += 2 + node.token = lfTokenAnd + case '|': + if !peekIs('|') { + return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Invalid token '|'. Did you mean '||'?") + } + i += 2 + node.token = lfTokenOr + case '!': + i += 1 + node.token = lfTokenNot + case ',': + i += 1 + node.token = lfTokenOr + case '(': + i += 1 + node.token = lfTokenOpenGroup + case ')': + i += 1 + node.token = lfTokenCloseGroup + case '/': + i += 1 + value, n := consumeUntil("/") + i += n + 1 + node.token, node.value = lfTokenRegexp, value + default: + value, n := consumeUntil("&|!,()/") + i += n + node.token, node.value = lfTokenLabel, strings.TrimSpace(value) + } + return node, nil + } +} + +func MustParseLabelFilter(input string) LabelFilter { + filter, err := ParseLabelFilter(input) + if err != nil { + panic(err) + } + return filter +} + +func ParseLabelFilter(input string) (LabelFilter, error) { + if DEBUG_LABEL_FILTER_PARSING { + fmt.Println("\n==============") + fmt.Println("Input: ", input) + fmt.Print("Tokens: ") + } + if input == "" { + return func(_ []string) bool { return true }, nil + } + nextToken := tokenize(input) + + root := &treeNode{token: lfTokenRoot} + current := root +LOOP: + for { + node, err := nextToken() + if err != nil { + return nil, err + } + + if DEBUG_LABEL_FILTER_PARSING { + fmt.Print(node.tokenString() + " ") + } + + switch node.token { + case lfTokenEOF: + break LOOP + case lfTokenLabel, lfTokenRegexp: + if current.rightNode != nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found two adjacent labels. You need an operator between them.") + } + current.setRightNode(node) + case lfTokenNot, lfTokenOpenGroup: + if current.rightNode != nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Invalid token '%s'.", node.token)) + } + current.setRightNode(node) + current = node + case lfTokenAnd, lfTokenOr: + if current.rightNode == nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Operator '%s' missing left hand operand.", node.token)) + } + nodeToStealFrom := current.firstAncestorWithPrecedenceLEQ(node.token.Precedence()) + node.setLeftNode(nodeToStealFrom.rightNode) + nodeToStealFrom.setRightNode(node) + current = node + case lfTokenCloseGroup: + firstUnmatchedOpenNode := current.firstUnmatchedOpenNode() + if firstUnmatchedOpenNode == nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Mismatched ')' - could not find matching '('.") + } + if firstUnmatchedOpenNode == current && current.rightNode == nil { + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found empty '()' group.") + } + firstUnmatchedOpenNode.token = lfTokenCloseGroup //signify the group is now closed + current = firstUnmatchedOpenNode.parent + default: + return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Unknown token '%s'.", node.token)) + } + } + if DEBUG_LABEL_FILTER_PARSING { + fmt.Printf("\n Tree:\n%s", root.toString(0)) + } + return root.constructLabelFilter(input) +} + +func ValidateAndCleanupLabel(label string, cl CodeLocation) (string, error) { + out := strings.TrimSpace(label) + if out == "" { + return "", GinkgoErrors.InvalidEmptyLabel(cl) + } + if strings.ContainsAny(out, "&|!,()/") { + return "", GinkgoErrors.InvalidLabel(label, cl) + } + return out, nil +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go b/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go new file mode 100644 index 000000000..7b1524b52 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go @@ -0,0 +1,190 @@ +package types + +import ( + "encoding/json" + "fmt" + "time" +) + +// 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 + AsJSON string + Representation string +} + +func WrapEntryValue(value interface{}) ReportEntryValue { + return ReportEntryValue{ + raw: value, + } +} + +func (rev ReportEntryValue) GetRawValue() interface{} { + return rev.raw +} + +func (rev ReportEntryValue) String() string { + if rev.raw == nil { + return "" + } + if colorableStringer, ok := rev.raw.(ColorableStringer); ok { + return colorableStringer.ColorableString() + } + + if stringer, ok := rev.raw.(fmt.Stringer); ok { + return stringer.String() + } + if rev.Representation != "" { + return rev.Representation + } + return fmt.Sprintf("%+v", rev.raw) +} + +func (rev ReportEntryValue) MarshalJSON() ([]byte, error) { + //All this to capture the representation at encoding-time, not creating time + //This way users can Report on pointers and get their final values at reporting-time + out := struct { + AsJSON string + Representation string + }{ + Representation: rev.String(), + } + asJSON, err := json.Marshal(rev.raw) + if err != nil { + return nil, err + } + out.AsJSON = string(asJSON) + + return json.Marshal(out) +} + +func (rev *ReportEntryValue) UnmarshalJSON(data []byte) error { + in := struct { + AsJSON string + Representation string + }{} + err := json.Unmarshal(data, &in) + if err != nil { + return err + } + rev.AsJSON = in.AsJSON + rev.Representation = in.Representation + return json.Unmarshal([]byte(in.AsJSON), &(rev.raw)) +} + +func (rev ReportEntryValue) GobEncode() ([]byte, error) { + return rev.MarshalJSON() +} + +func (rev *ReportEntryValue) GobDecode(data []byte) error { + return rev.UnmarshalJSON(data) +} + +// ReportEntry captures information attached to `SpecReport` via `AddReportEntry` +type ReportEntry struct { + // Visibility captures the visibility policy for this ReportEntry + Visibility ReportEntryVisibility + // Location captures the location of the AddReportEntry call + Location CodeLocation + + Time time.Time //need this for backwards compatibility + TimelineLocation TimelineLocation + + // Name captures the name of this report + Name string + // Value captures the (optional) object passed into AddReportEntry - this can be + // anything the user wants. The value passed to AddReportEntry is wrapped in a ReportEntryValue to make + // encoding/decoding the value easier. To access the raw value call entry.GetRawValue() + Value ReportEntryValue +} + +// ColorableStringer is an interface that ReportEntry values can satisfy. If they do then ColorableString() is used to generate their representation. +type ColorableStringer interface { + ColorableString() string +} + +// StringRepresentation() returns the string representation of the value associated with the ReportEntry -- +// if value is nil, empty string is returned +// if value is a `ColorableStringer` then `Value.ColorableString()` is returned +// if value is a `fmt.Stringer` then `Value.String()` is returned +// otherwise the value is formatted with "%+v" +func (entry ReportEntry) StringRepresentation() string { + return entry.Value.String() +} + +// GetRawValue returns the Value object that was passed to AddReportEntry +// If called in-process this will be the same object that was passed into AddReportEntry. +// 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{} { + return entry.Value.GetRawValue() +} + +func (entry ReportEntry) GetTimelineLocation() TimelineLocation { + return entry.TimelineLocation +} + +type ReportEntries []ReportEntry + +func (re ReportEntries) HasVisibility(visibilities ...ReportEntryVisibility) bool { + for _, entry := range re { + if entry.Visibility.Is(visibilities...) { + return true + } + } + return false +} + +func (re ReportEntries) WithVisibility(visibilities ...ReportEntryVisibility) ReportEntries { + out := ReportEntries{} + + for _, entry := range re { + if entry.Visibility.Is(visibilities...) { + out = append(out, entry) + } + } + + return out +} + +// ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter +type ReportEntryVisibility uint + +const ( + // Always print out this ReportEntry + ReportEntryVisibilityAlways ReportEntryVisibility = iota + // Only print out this ReportEntry if the spec fails or if the test is run with -v + ReportEntryVisibilityFailureOrVerbose + // Never print out this ReportEntry (note that ReportEntrys are always encoded in machine readable reports (e.g. JSON, JUnit, etc.)) + ReportEntryVisibilityNever +) + +var revEnumSupport = NewEnumSupport(map[uint]string{ + uint(ReportEntryVisibilityAlways): "always", + uint(ReportEntryVisibilityFailureOrVerbose): "failure-or-verbose", + uint(ReportEntryVisibilityNever): "never", +}) + +func (rev ReportEntryVisibility) String() string { + return revEnumSupport.String(uint(rev)) +} +func (rev *ReportEntryVisibility) UnmarshalJSON(b []byte) error { + out, err := revEnumSupport.UnmarshJSON(b) + *rev = ReportEntryVisibility(out) + return err +} +func (rev ReportEntryVisibility) MarshalJSON() ([]byte, error) { + return revEnumSupport.MarshJSON(uint(rev)) +} + +func (v ReportEntryVisibility) Is(visibilities ...ReportEntryVisibility) bool { + for _, visibility := range visibilities { + if v == visibility { + return true + } + } + + return false +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/types.go b/vendor/github.com/onsi/ginkgo/v2/types/types.go new file mode 100644 index 000000000..aae69b04c --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/types.go @@ -0,0 +1,914 @@ +package types + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + "time" +) + +const GINKGO_FOCUS_EXIT_CODE = 197 +const GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999" + +// Report captures information about a Ginkgo test run +type Report struct { + //SuitePath captures the absolute path to the test suite + SuitePath string + + //SuiteDescription captures the description string passed to the DSL's RunSpecs() function + SuiteDescription string + + //SuiteLabels captures any labels attached to the suite by the DSL's RunSpecs() function + SuiteLabels []string + + //SuiteSucceeded captures the success or failure status of the test run + //If true, the test run is considered successful. + //If false, the test run is considered unsuccessful + SuiteSucceeded bool + + //SuiteHasProgrammaticFocus captures whether the test suite has a test or set of tests that are programmatically focused + //(i.e an `FIt` or an `FDescribe` + SuiteHasProgrammaticFocus bool + + //SpecialSuiteFailureReasons may contain special failure reasons + //For example, a test suite might be considered "failed" even if none of the individual specs + //have a failure state. For example, if the user has configured --fail-on-pending the test suite + //will have failed if there are pending tests even though all non-pending tests may have passed. In such + //cases, Ginkgo populates SpecialSuiteFailureReasons with a clear message indicating the reason for the failure. + //SpecialSuiteFailureReasons is also populated if the test suite is interrupted by the user. + //Since multiple special failure reasons can occur, this field is a slice. + SpecialSuiteFailureReasons []string + + //PreRunStats contains a set of stats captured before the test run begins. This is primarily used + //by Ginkgo's reporter to tell the user how many specs are in the current suite (PreRunStats.TotalSpecs) + //and how many it intends to run (PreRunStats.SpecsThatWillRun) after applying any relevant focus or skip filters. + PreRunStats PreRunStats + + //StartTime and EndTime capture the start and end time of the test run + StartTime time.Time + EndTime time.Time + + //RunTime captures the duration of the test run + RunTime time.Duration + + //SuiteConfig captures the Ginkgo configuration governing this test run + //SuiteConfig includes information necessary for reproducing an identical test run, + //such as the random seed and any filters applied during the test run + SuiteConfig SuiteConfig + + //SpecReports is a list of all SpecReports generated by this test run + //It is empty when the SuiteReport is provided to ReportBeforeSuite + SpecReports SpecReports +} + +// PreRunStats contains a set of stats captured before the test run begins. This is primarily used +// by Ginkgo's reporter to tell the user how many specs are in the current suite (PreRunStats.TotalSpecs) +// and how many it intends to run (PreRunStats.SpecsThatWillRun) after applying any relevant focus or skip filters. +type PreRunStats struct { + TotalSpecs int + SpecsThatWillRun int +} + +// Add is used by Ginkgo's parallel aggregation mechanisms to combine test run reports form individual parallel processes +// to form a complete final report. +func (report Report) Add(other Report) Report { + report.SuiteSucceeded = report.SuiteSucceeded && other.SuiteSucceeded + + if other.StartTime.Before(report.StartTime) { + report.StartTime = other.StartTime + } + + if other.EndTime.After(report.EndTime) { + report.EndTime = other.EndTime + } + + specialSuiteFailureReasons := []string{} + reasonsLookup := map[string]bool{} + for _, reasons := range [][]string{report.SpecialSuiteFailureReasons, other.SpecialSuiteFailureReasons} { + for _, reason := range reasons { + if !reasonsLookup[reason] { + reasonsLookup[reason] = true + specialSuiteFailureReasons = append(specialSuiteFailureReasons, reason) + } + } + } + report.SpecialSuiteFailureReasons = specialSuiteFailureReasons + report.RunTime = report.EndTime.Sub(report.StartTime) + + reports := make(SpecReports, len(report.SpecReports)+len(other.SpecReports)) + copy(reports, report.SpecReports) + offset := len(report.SpecReports) + for i := range other.SpecReports { + reports[i+offset] = other.SpecReports[i] + } + + report.SpecReports = reports + return report +} + +// SpecReport captures information about a Ginkgo spec. +type SpecReport struct { + // ContainerHierarchyTexts is a slice containing the text strings of + // all Describe/Context/When containers in this spec's hierarchy. + ContainerHierarchyTexts []string + + // ContainerHierarchyLocations is a slice containing the CodeLocations of + // all Describe/Context/When containers in this spec's hierarchy. + ContainerHierarchyLocations []CodeLocation + + // ContainerHierarchyLabels is a slice containing the labels of + // all Describe/Context/When containers in this spec's hierarchy + ContainerHierarchyLabels [][]string + + // LeafNodeType, LeadNodeLocation, LeafNodeLabels and LeafNodeText capture the NodeType, CodeLocation, and text + // of the Ginkgo node being tested (typically an NodeTypeIt node, though this can also be + // one of the NodeTypesForSuiteLevelNodes node types) + LeafNodeType NodeType + LeafNodeLocation CodeLocation + LeafNodeLabels []string + LeafNodeText string + + // State captures whether the spec has passed, failed, etc. + State SpecState + + // IsSerial captures whether the spec has the Serial decorator + IsSerial bool + + // IsInOrderedContainer captures whether the spec appears in an Ordered container + IsInOrderedContainer bool + + // StartTime and EndTime capture the start and end time of the spec + StartTime time.Time + EndTime time.Time + + // RunTime captures the duration of the spec + RunTime time.Duration + + // ParallelProcess captures the parallel process that this spec ran on + ParallelProcess int + + // RunningInParallel captures whether this spec is part of a suite that ran in parallel + RunningInParallel bool + + //Failure is populated if a spec has failed, panicked, been interrupted, or skipped by the user (e.g. calling Skip()) + //It includes detailed information about the Failure + Failure Failure + + // NumAttempts captures the number of times this Spec was run. + // Flakey specs can be retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator. + // Repeated specs can be retried with the use of the MustPassRepeatedly decorator + NumAttempts int + + // MaxFlakeAttempts captures whether the spec has been retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator. + MaxFlakeAttempts int + + // MaxMustPassRepeatedly captures whether the spec has the MustPassRepeatedly decorator + MaxMustPassRepeatedly int + + // CapturedGinkgoWriterOutput contains text printed to the GinkgoWriter + CapturedGinkgoWriterOutput string + + // CapturedStdOutErr contains text printed to stdout/stderr (when running in parallel) + // This is always empty when running in series or calling CurrentSpecReport() + // It is used internally by Ginkgo's reporter + CapturedStdOutErr string + + // ReportEntries contains any reports added via `AddReportEntry` + ReportEntries ReportEntries + + // ProgressReports contains any progress reports generated during this spec. These can either be manually triggered, or automatically generated by Ginkgo via the PollProgressAfter() decorator + ProgressReports []ProgressReport + + // AdditionalFailures contains any failures that occurred after the initial spec failure. These typically occur in cleanup nodes after the initial failure and are only emitted when running in verbose mode. + AdditionalFailures []AdditionalFailure + + // SpecEvents capture additional events that occur during the spec run + SpecEvents SpecEvents +} + +func (report SpecReport) MarshalJSON() ([]byte, error) { + //All this to avoid emitting an empty Failure struct in the JSON + out := struct { + ContainerHierarchyTexts []string + ContainerHierarchyLocations []CodeLocation + ContainerHierarchyLabels [][]string + LeafNodeType NodeType + LeafNodeLocation CodeLocation + LeafNodeLabels []string + LeafNodeText string + State SpecState + StartTime time.Time + EndTime time.Time + RunTime time.Duration + ParallelProcess int + Failure *Failure `json:",omitempty"` + NumAttempts int + MaxFlakeAttempts int + MaxMustPassRepeatedly int + CapturedGinkgoWriterOutput string `json:",omitempty"` + CapturedStdOutErr string `json:",omitempty"` + ReportEntries ReportEntries `json:",omitempty"` + ProgressReports []ProgressReport `json:",omitempty"` + AdditionalFailures []AdditionalFailure `json:",omitempty"` + SpecEvents SpecEvents `json:",omitempty"` + }{ + ContainerHierarchyTexts: report.ContainerHierarchyTexts, + ContainerHierarchyLocations: report.ContainerHierarchyLocations, + ContainerHierarchyLabels: report.ContainerHierarchyLabels, + LeafNodeType: report.LeafNodeType, + LeafNodeLocation: report.LeafNodeLocation, + LeafNodeLabels: report.LeafNodeLabels, + LeafNodeText: report.LeafNodeText, + State: report.State, + StartTime: report.StartTime, + EndTime: report.EndTime, + RunTime: report.RunTime, + ParallelProcess: report.ParallelProcess, + Failure: nil, + ReportEntries: nil, + NumAttempts: report.NumAttempts, + MaxFlakeAttempts: report.MaxFlakeAttempts, + MaxMustPassRepeatedly: report.MaxMustPassRepeatedly, + CapturedGinkgoWriterOutput: report.CapturedGinkgoWriterOutput, + CapturedStdOutErr: report.CapturedStdOutErr, + } + + if !report.Failure.IsZero() { + out.Failure = &(report.Failure) + } + if len(report.ReportEntries) > 0 { + out.ReportEntries = report.ReportEntries + } + if len(report.ProgressReports) > 0 { + out.ProgressReports = report.ProgressReports + } + if len(report.AdditionalFailures) > 0 { + out.AdditionalFailures = report.AdditionalFailures + } + if len(report.SpecEvents) > 0 { + out.SpecEvents = report.SpecEvents + } + + return json.Marshal(out) +} + +// CombinedOutput returns a single string representation of both CapturedStdOutErr and CapturedGinkgoWriterOutput +// Note that both are empty when using CurrentSpecReport() so CurrentSpecReport().CombinedOutput() will always be empty. +// CombinedOutput() is used internally by Ginkgo's reporter. +func (report SpecReport) CombinedOutput() string { + if report.CapturedStdOutErr == "" { + return report.CapturedGinkgoWriterOutput + } + if report.CapturedGinkgoWriterOutput == "" { + return report.CapturedStdOutErr + } + return report.CapturedStdOutErr + "\n" + report.CapturedGinkgoWriterOutput +} + +// Failed returns true if report.State is one of the SpecStateFailureStates +// (SpecStateFailed, SpecStatePanicked, SpecStateinterrupted, SpecStateAborted) +func (report SpecReport) Failed() bool { + return report.State.Is(SpecStateFailureStates) +} + +// FullText returns a concatenation of all the report.ContainerHierarchyTexts and report.LeafNodeText +func (report SpecReport) FullText() string { + texts := []string{} + texts = append(texts, report.ContainerHierarchyTexts...) + if report.LeafNodeText != "" { + texts = append(texts, report.LeafNodeText) + } + return strings.Join(texts, " ") +} + +// Labels returns a deduped set of all the spec's Labels. +func (report SpecReport) Labels() []string { + out := []string{} + seen := map[string]bool{} + for _, labels := range report.ContainerHierarchyLabels { + for _, label := range labels { + if !seen[label] { + seen[label] = true + out = append(out, label) + } + } + } + for _, label := range report.LeafNodeLabels { + if !seen[label] { + seen[label] = true + out = append(out, label) + } + } + + return out +} + +// MatchesLabelFilter returns true if the spec satisfies the passed in label filter query +func (report SpecReport) MatchesLabelFilter(query string) (bool, error) { + filter, err := ParseLabelFilter(query) + if err != nil { + return false, err + } + return filter(report.Labels()), nil +} + +// FileName() returns the name of the file containing the spec +func (report SpecReport) FileName() string { + return report.LeafNodeLocation.FileName +} + +// LineNumber() returns the line number of the leaf node +func (report SpecReport) LineNumber() int { + return report.LeafNodeLocation.LineNumber +} + +// FailureMessage() returns the failure message (or empty string if the test hasn't failed) +func (report SpecReport) FailureMessage() string { + return report.Failure.Message +} + +// FailureLocation() returns the location of the failure (or an empty CodeLocation if the test hasn't failed) +func (report SpecReport) FailureLocation() CodeLocation { + return report.Failure.Location +} + +// Timeline() returns a timeline view of the report +func (report SpecReport) Timeline() Timeline { + timeline := Timeline{} + if !report.Failure.IsZero() { + timeline = append(timeline, report.Failure) + if report.Failure.AdditionalFailure != nil { + timeline = append(timeline, *(report.Failure.AdditionalFailure)) + } + } + for _, additionalFailure := range report.AdditionalFailures { + timeline = append(timeline, additionalFailure) + } + for _, reportEntry := range report.ReportEntries { + timeline = append(timeline, reportEntry) + } + for _, progressReport := range report.ProgressReports { + timeline = append(timeline, progressReport) + } + for _, specEvent := range report.SpecEvents { + timeline = append(timeline, specEvent) + } + sort.Sort(timeline) + return timeline +} + +type SpecReports []SpecReport + +// WithLeafNodeType returns the subset of SpecReports with LeafNodeType matching one of the requested NodeTypes +func (reports SpecReports) WithLeafNodeType(nodeTypes NodeType) SpecReports { + count := 0 + for i := range reports { + if reports[i].LeafNodeType.Is(nodeTypes) { + count++ + } + } + + out := make(SpecReports, count) + j := 0 + for i := range reports { + if reports[i].LeafNodeType.Is(nodeTypes) { + out[j] = reports[i] + j++ + } + } + return out +} + +// WithState returns the subset of SpecReports with State matching one of the requested SpecStates +func (reports SpecReports) WithState(states SpecState) SpecReports { + count := 0 + for i := range reports { + if reports[i].State.Is(states) { + count++ + } + } + + out, j := make(SpecReports, count), 0 + for i := range reports { + if reports[i].State.Is(states) { + out[j] = reports[i] + j++ + } + } + return out +} + +// CountWithState returns the number of SpecReports with State matching one of the requested SpecStates +func (reports SpecReports) CountWithState(states SpecState) int { + n := 0 + for i := range reports { + if reports[i].State.Is(states) { + n += 1 + } + } + return n +} + +// If the Spec passes, CountOfFlakedSpecs returns the number of SpecReports that failed after multiple attempts. +func (reports SpecReports) CountOfFlakedSpecs() int { + n := 0 + for i := range reports { + if reports[i].MaxFlakeAttempts > 1 && reports[i].State.Is(SpecStatePassed) && reports[i].NumAttempts > 1 { + n += 1 + } + } + return n +} + +// If the Spec fails, CountOfRepeatedSpecs returns the number of SpecReports that passed after multiple attempts +func (reports SpecReports) CountOfRepeatedSpecs() int { + n := 0 + for i := range reports { + if reports[i].MaxMustPassRepeatedly > 1 && reports[i].State.Is(SpecStateFailureStates) && reports[i].NumAttempts > 1 { + n += 1 + } + } + return n +} + +// TimelineLocation captures the location of an event in the spec's timeline +type TimelineLocation struct { + //Offset is the offset (in bytes) of the event relative to the GinkgoWriter stream + Offset int `json:",omitempty"` + + //Order is the order of the event with respect to other events. The absolute value of Order + //is irrelevant. All that matters is that an event with a lower Order occurs before ane vent with a higher Order + Order int `json:",omitempty"` + + Time time.Time +} + +// TimelineEvent represent an event on the timeline +// consumers of Timeline will need to check the concrete type of each entry to determine how to handle it +type TimelineEvent interface { + GetTimelineLocation() TimelineLocation +} + +type Timeline []TimelineEvent + +func (t Timeline) Len() int { return len(t) } +func (t Timeline) Less(i, j int) bool { + return t[i].GetTimelineLocation().Order < t[j].GetTimelineLocation().Order +} +func (t Timeline) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t Timeline) WithoutHiddenReportEntries() Timeline { + out := Timeline{} + for _, event := range t { + if reportEntry, isReportEntry := event.(ReportEntry); isReportEntry && reportEntry.Visibility == ReportEntryVisibilityNever { + continue + } + out = append(out, event) + } + return out +} + +func (t Timeline) WithoutVeryVerboseSpecEvents() Timeline { + out := Timeline{} + for _, event := range t { + if specEvent, isSpecEvent := event.(SpecEvent); isSpecEvent && specEvent.IsOnlyVisibleAtVeryVerbose() { + continue + } + out = append(out, event) + } + return out +} + +// Failure captures failure information for an individual test +type Failure struct { + // Message - the failure message passed into Fail(...). When using a matcher library + // like Gomega, this will contain the failure message generated by Gomega. + // + // Message is also populated if the user has called Skip(...). + Message string + + // Location - the CodeLocation where the failure occurred + // This CodeLocation will include a fully-populated StackTrace + Location CodeLocation + + TimelineLocation TimelineLocation + + // ForwardedPanic - if the failure represents a captured panic (i.e. Summary.State == SpecStatePanicked) + // then ForwardedPanic will be populated with a string representation of the captured panic. + ForwardedPanic string `json:",omitempty"` + + // FailureNodeContext - one of three contexts describing the node in which the failure occurred: + // FailureNodeIsLeafNode means the failure occurred in the leaf node of the associated SpecReport. None of the other FailureNode fields will be populated + // FailureNodeAtTopLevel means the failure occurred in a non-leaf node that is defined at the top-level of the spec (i.e. not in a container). FailureNodeType and FailureNodeLocation will be populated. + // FailureNodeInContainer means the failure occurred in a non-leaf node that is defined within a container. FailureNodeType, FailureNodeLocation, and FailureNodeContainerIndex will be populated. + // + // FailureNodeType will contain the NodeType of the node in which the failure occurred. + // FailureNodeLocation will contain the CodeLocation of the node in which the failure occurred. + // If populated, FailureNodeContainerIndex will be the index into SpecReport.ContainerHierarchyTexts and SpecReport.ContainerHierarchyLocations that represents the parent container of the node in which the failure occurred. + FailureNodeContext FailureNodeContext `json:",omitempty"` + + FailureNodeType NodeType `json:",omitempty"` + + FailureNodeLocation CodeLocation `json:",omitempty"` + + FailureNodeContainerIndex int `json:",omitempty"` + + //ProgressReport is populated if the spec was interrupted or timed out + ProgressReport ProgressReport `json:",omitempty"` + + //AdditionalFailure is non-nil if a follow-on failure occurred within the same node after the primary failure. This only happens when a node has timed out or been interrupted. In such cases the AdditionalFailure can include information about where/why the spec was stuck. + AdditionalFailure *AdditionalFailure `json:",omitempty"` +} + +func (f Failure) IsZero() bool { + return f.Message == "" && (f.Location == CodeLocation{}) +} + +func (f Failure) GetTimelineLocation() TimelineLocation { + return f.TimelineLocation +} + +// FailureNodeContext captures the location context for the node containing the failing line of code +type FailureNodeContext uint + +const ( + FailureNodeContextInvalid FailureNodeContext = iota + + FailureNodeIsLeafNode + FailureNodeAtTopLevel + FailureNodeInContainer +) + +var fncEnumSupport = NewEnumSupport(map[uint]string{ + uint(FailureNodeContextInvalid): "INVALID FAILURE NODE CONTEXT", + uint(FailureNodeIsLeafNode): "leaf-node", + uint(FailureNodeAtTopLevel): "top-level", + uint(FailureNodeInContainer): "in-container", +}) + +func (fnc FailureNodeContext) String() string { + return fncEnumSupport.String(uint(fnc)) +} +func (fnc *FailureNodeContext) UnmarshalJSON(b []byte) error { + out, err := fncEnumSupport.UnmarshJSON(b) + *fnc = FailureNodeContext(out) + return err +} +func (fnc FailureNodeContext) MarshalJSON() ([]byte, error) { + return fncEnumSupport.MarshJSON(uint(fnc)) +} + +// AdditionalFailure capturs any additional failures that occur after the initial failure of a psec +// these typically occur in clean up nodes after the spec has failed. +// We can't simply use Failure as we want to track the SpecState to know what kind of failure this is +type AdditionalFailure struct { + State SpecState + Failure Failure +} + +func (f AdditionalFailure) GetTimelineLocation() TimelineLocation { + return f.Failure.TimelineLocation +} + +// SpecState captures the state of a spec +// To determine if a given `state` represents a failure state, use `state.Is(SpecStateFailureStates)` +type SpecState uint + +const ( + SpecStateInvalid SpecState = 0 + + SpecStatePending SpecState = 1 << iota + SpecStateSkipped + SpecStatePassed + SpecStateFailed + SpecStateAborted + SpecStatePanicked + SpecStateInterrupted + SpecStateTimedout +) + +var ssEnumSupport = NewEnumSupport(map[uint]string{ + uint(SpecStateInvalid): "INVALID SPEC STATE", + uint(SpecStatePending): "pending", + uint(SpecStateSkipped): "skipped", + uint(SpecStatePassed): "passed", + uint(SpecStateFailed): "failed", + uint(SpecStateAborted): "aborted", + uint(SpecStatePanicked): "panicked", + uint(SpecStateInterrupted): "interrupted", + uint(SpecStateTimedout): "timedout", +}) + +func (ss SpecState) String() string { + return ssEnumSupport.String(uint(ss)) +} +func (ss SpecState) GomegaString() string { + return ssEnumSupport.String(uint(ss)) +} +func (ss *SpecState) UnmarshalJSON(b []byte) error { + out, err := ssEnumSupport.UnmarshJSON(b) + *ss = SpecState(out) + return err +} +func (ss SpecState) MarshalJSON() ([]byte, error) { + return ssEnumSupport.MarshJSON(uint(ss)) +} + +var SpecStateFailureStates = SpecStateFailed | SpecStateTimedout | SpecStateAborted | SpecStatePanicked | SpecStateInterrupted + +func (ss SpecState) Is(states SpecState) bool { + return ss&states != 0 +} + +// ProgressReport captures the progress of the current spec. It is, effectively, a structured Ginkgo-aware stack trace +type ProgressReport struct { + Message string `json:",omitempty"` + ParallelProcess int `json:",omitempty"` + RunningInParallel bool `json:",omitempty"` + + ContainerHierarchyTexts []string `json:",omitempty"` + LeafNodeText string `json:",omitempty"` + LeafNodeLocation CodeLocation `json:",omitempty"` + SpecStartTime time.Time `json:",omitempty"` + + CurrentNodeType NodeType `json:",omitempty"` + CurrentNodeText string `json:",omitempty"` + CurrentNodeLocation CodeLocation `json:",omitempty"` + CurrentNodeStartTime time.Time `json:",omitempty"` + + CurrentStepText string `json:",omitempty"` + CurrentStepLocation CodeLocation `json:",omitempty"` + CurrentStepStartTime time.Time `json:",omitempty"` + + AdditionalReports []string `json:",omitempty"` + + CapturedGinkgoWriterOutput string `json:",omitempty"` + TimelineLocation TimelineLocation `json:",omitempty"` + + Goroutines []Goroutine `json:",omitempty"` +} + +func (pr ProgressReport) IsZero() bool { + return pr.CurrentNodeType == NodeTypeInvalid +} + +func (pr ProgressReport) Time() time.Time { + return pr.TimelineLocation.Time +} + +func (pr ProgressReport) SpecGoroutine() Goroutine { + for _, goroutine := range pr.Goroutines { + if goroutine.IsSpecGoroutine { + return goroutine + } + } + return Goroutine{} +} + +func (pr ProgressReport) HighlightedGoroutines() []Goroutine { + out := []Goroutine{} + for _, goroutine := range pr.Goroutines { + if goroutine.IsSpecGoroutine || !goroutine.HasHighlights() { + continue + } + out = append(out, goroutine) + } + return out +} + +func (pr ProgressReport) OtherGoroutines() []Goroutine { + out := []Goroutine{} + for _, goroutine := range pr.Goroutines { + if goroutine.IsSpecGoroutine || goroutine.HasHighlights() { + continue + } + out = append(out, goroutine) + } + return out +} + +func (pr ProgressReport) WithoutCapturedGinkgoWriterOutput() ProgressReport { + out := pr + out.CapturedGinkgoWriterOutput = "" + return out +} + +func (pr ProgressReport) WithoutOtherGoroutines() ProgressReport { + out := pr + filteredGoroutines := []Goroutine{} + for _, goroutine := range pr.Goroutines { + if goroutine.IsSpecGoroutine || goroutine.HasHighlights() { + filteredGoroutines = append(filteredGoroutines, goroutine) + } + } + out.Goroutines = filteredGoroutines + return out +} + +func (pr ProgressReport) GetTimelineLocation() TimelineLocation { + return pr.TimelineLocation +} + +type Goroutine struct { + ID uint64 + State string + Stack []FunctionCall + IsSpecGoroutine bool +} + +func (g Goroutine) IsZero() bool { + return g.ID == 0 +} + +func (g Goroutine) HasHighlights() bool { + for _, fc := range g.Stack { + if fc.Highlight { + return true + } + } + + return false +} + +type FunctionCall struct { + Function string + Filename string + Line int + Highlight bool `json:",omitempty"` + Source []string `json:",omitempty"` + SourceHighlight int `json:",omitempty"` +} + +// NodeType captures the type of a given Ginkgo Node +type NodeType uint + +const ( + NodeTypeInvalid NodeType = 0 + + NodeTypeContainer NodeType = 1 << iota + NodeTypeIt + + NodeTypeBeforeEach + NodeTypeJustBeforeEach + NodeTypeAfterEach + NodeTypeJustAfterEach + + NodeTypeBeforeAll + NodeTypeAfterAll + + NodeTypeBeforeSuite + NodeTypeSynchronizedBeforeSuite + NodeTypeAfterSuite + NodeTypeSynchronizedAfterSuite + + NodeTypeReportBeforeEach + NodeTypeReportAfterEach + NodeTypeReportBeforeSuite + NodeTypeReportAfterSuite + + NodeTypeCleanupInvalid + NodeTypeCleanupAfterEach + NodeTypeCleanupAfterAll + NodeTypeCleanupAfterSuite +) + +var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt +var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite +var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite +var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite + +var ntEnumSupport = NewEnumSupport(map[uint]string{ + uint(NodeTypeInvalid): "INVALID NODE TYPE", + uint(NodeTypeContainer): "Container", + uint(NodeTypeIt): "It", + uint(NodeTypeBeforeEach): "BeforeEach", + uint(NodeTypeJustBeforeEach): "JustBeforeEach", + uint(NodeTypeAfterEach): "AfterEach", + uint(NodeTypeJustAfterEach): "JustAfterEach", + uint(NodeTypeBeforeAll): "BeforeAll", + uint(NodeTypeAfterAll): "AfterAll", + uint(NodeTypeBeforeSuite): "BeforeSuite", + uint(NodeTypeSynchronizedBeforeSuite): "SynchronizedBeforeSuite", + uint(NodeTypeAfterSuite): "AfterSuite", + uint(NodeTypeSynchronizedAfterSuite): "SynchronizedAfterSuite", + uint(NodeTypeReportBeforeEach): "ReportBeforeEach", + uint(NodeTypeReportAfterEach): "ReportAfterEach", + uint(NodeTypeReportBeforeSuite): "ReportBeforeSuite", + uint(NodeTypeReportAfterSuite): "ReportAfterSuite", + uint(NodeTypeCleanupInvalid): "DeferCleanup", + uint(NodeTypeCleanupAfterEach): "DeferCleanup (Each)", + uint(NodeTypeCleanupAfterAll): "DeferCleanup (All)", + uint(NodeTypeCleanupAfterSuite): "DeferCleanup (Suite)", +}) + +func (nt NodeType) String() string { + return ntEnumSupport.String(uint(nt)) +} +func (nt *NodeType) UnmarshalJSON(b []byte) error { + out, err := ntEnumSupport.UnmarshJSON(b) + *nt = NodeType(out) + return err +} +func (nt NodeType) MarshalJSON() ([]byte, error) { + return ntEnumSupport.MarshJSON(uint(nt)) +} + +func (nt NodeType) Is(nodeTypes NodeType) bool { + return nt&nodeTypes != 0 +} + +/* +SpecEvent captures a vareity of events that can occur when specs run. See SpecEventType for the list of available events. +*/ +type SpecEvent struct { + SpecEventType SpecEventType + + CodeLocation CodeLocation + TimelineLocation TimelineLocation + + Message string `json:",omitempty"` + Duration time.Duration `json:",omitempty"` + NodeType NodeType `json:",omitempty"` + Attempt int `json:",omitempty"` +} + +func (se SpecEvent) GetTimelineLocation() TimelineLocation { + return se.TimelineLocation +} + +func (se SpecEvent) IsOnlyVisibleAtVeryVerbose() bool { + return se.SpecEventType.Is(SpecEventByEnd | SpecEventNodeStart | SpecEventNodeEnd) +} + +func (se SpecEvent) GomegaString() string { + out := &strings.Builder{} + out.WriteString("[" + se.SpecEventType.String() + " SpecEvent] ") + if se.Message != "" { + out.WriteString("Message=") + out.WriteString(`"` + se.Message + `",`) + } + if se.Duration != 0 { + out.WriteString("Duration=" + se.Duration.String() + ",") + } + if se.NodeType != NodeTypeInvalid { + out.WriteString("NodeType=" + se.NodeType.String() + ",") + } + if se.Attempt != 0 { + out.WriteString(fmt.Sprintf("Attempt=%d", se.Attempt) + ",") + } + out.WriteString("CL=" + se.CodeLocation.String() + ",") + out.WriteString(fmt.Sprintf("TL.Offset=%d", se.TimelineLocation.Offset)) + + return out.String() +} + +type SpecEvents []SpecEvent + +func (se SpecEvents) WithType(seType SpecEventType) SpecEvents { + out := SpecEvents{} + for _, event := range se { + if event.SpecEventType.Is(seType) { + out = append(out, event) + } + } + return out +} + +type SpecEventType uint + +const ( + SpecEventInvalid SpecEventType = 0 + + SpecEventByStart SpecEventType = 1 << iota + SpecEventByEnd + SpecEventNodeStart + SpecEventNodeEnd + SpecEventSpecRepeat + SpecEventSpecRetry +) + +var seEnumSupport = NewEnumSupport(map[uint]string{ + uint(SpecEventInvalid): "INVALID SPEC EVENT", + uint(SpecEventByStart): "By", + uint(SpecEventByEnd): "By (End)", + uint(SpecEventNodeStart): "Node", + uint(SpecEventNodeEnd): "Node (End)", + uint(SpecEventSpecRepeat): "Repeat", + uint(SpecEventSpecRetry): "Retry", +}) + +func (se SpecEventType) String() string { + return seEnumSupport.String(uint(se)) +} +func (se *SpecEventType) UnmarshalJSON(b []byte) error { + out, err := seEnumSupport.UnmarshJSON(b) + *se = SpecEventType(out) + return err +} +func (se SpecEventType) MarshalJSON() ([]byte, error) { + return seEnumSupport.MarshJSON(uint(se)) +} + +func (se SpecEventType) Is(specEventTypes SpecEventType) bool { + return se&specEventTypes != 0 +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go new file mode 100644 index 000000000..a37f30828 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go @@ -0,0 +1,3 @@ +package types + +const VERSION = "2.13.0" diff --git a/vendor/github.com/onsi/gomega/.gitignore b/vendor/github.com/onsi/gomega/.gitignore index 5f12ff053..425d0a509 100644 --- a/vendor/github.com/onsi/gomega/.gitignore +++ b/vendor/github.com/onsi/gomega/.gitignore @@ -3,4 +3,5 @@ . .idea gomega.iml -TODO.md \ No newline at end of file +TODO +.vscode \ No newline at end of file diff --git a/vendor/github.com/onsi/gomega/CHANGELOG.md b/vendor/github.com/onsi/gomega/CHANGELOG.md index e088dc078..1526497b9 100644 --- a/vendor/github.com/onsi/gomega/CHANGELOG.md +++ b/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -1,3 +1,167 @@ +## 1.27.10 + +### Fixes +- fix: go 1.21 adding goroutine ID to creator+location (#685) [bdc7803] + +## 1.27.9 + +### Fixes +- Prevent nil-dereference in format.Object for boxed nil error (#681) [3b31fc3] + +### Maintenance +- Bump golang.org/x/net from 0.11.0 to 0.12.0 (#679) [360849b] +- chore: use String() instead of fmt.Sprintf (#678) [86f3659] +- Bump golang.org/x/net from 0.10.0 to 0.11.0 (#674) [642ead0] +- chore: unnecessary use of fmt.Sprintf (#677) [ceb9ca6] +- Bump github.com/onsi/ginkgo/v2 from 2.10.0 to 2.11.0 (#675) [a2087d8] +- docs: fix ContainSubstring references (#673) [fc9a89f] +- Bump github.com/onsi/ginkgo/v2 from 2.9.7 to 2.10.0 (#671) [9076019] + +## 1.27.8 + +### Fixes +- HaveExactElement should not call FailureMessage if a submatcher returned an error [096f392] + +### Maintenance +- Bump github.com/onsi/ginkgo/v2 from 2.9.5 to 2.9.7 (#669) [8884bee] + +## 1.27.7 + +### Fixes +- fix: gcustom.MakeMatcher accepts nil as actual value (#666) [57054d5] + +### Maintenance +- update gitignore [05c1bc6] +- Bump github.com/onsi/ginkgo/v2 from 2.9.4 to 2.9.5 (#663) [7cadcf6] +- Bump golang.org/x/net from 0.9.0 to 0.10.0 (#662) [b524839] +- Bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.9.4 (#661) [5f44694] +- Bump commonmarker from 0.23.8 to 0.23.9 in /docs (#657) [05dc99a] +- Bump nokogiri from 1.14.1 to 1.14.3 in /docs (#658) [3a033d1] +- Replace deprecated NewGomegaWithT with NewWithT (#659) [a19238f] +- Bump golang.org/x/net from 0.8.0 to 0.9.0 (#656) [29ed041] +- Bump actions/setup-go from 3 to 4 (#651) [11b2080] + +## 1.27.6 + +### Fixes +- Allow collections matchers to work correctly when expected has nil elements [60e7cf3] + +### Maintenance +- updates MatchError godoc comment to also accept a Gomega matcher (#654) [67b869d] + +## 1.27.5 + +### Maintenance +- Bump github.com/onsi/ginkgo/v2 from 2.9.1 to 2.9.2 (#653) [a215021] +- Bump github.com/go-task/slim-sprig (#652) [a26fed8] + +## 1.27.4 + +### Fixes +- improve error formatting and remove duplication of error message in Eventually/Consistently [854f075] + +### Maintenance +- Bump github.com/onsi/ginkgo/v2 from 2.9.0 to 2.9.1 (#650) [ccebd9b] + +## 1.27.3 + +### Fixes +- format.Object now always includes err.Error() when passed an error [86d97ef] +- Fix HaveExactElements to work inside ContainElement or other collection matchers (#648) [636757e] + +### Maintenance +- Bump github.com/golang/protobuf from 1.5.2 to 1.5.3 (#649) [cc16689] +- Bump github.com/onsi/ginkgo/v2 from 2.8.4 to 2.9.0 (#646) [e783366] + +## 1.27.2 + +### Fixes +- improve poll progress message when polling a consistently that has been passing [28a319b] + +### Maintenance +- bump ginkgo +- remove tools.go hack as Ginkgo 2.8.2 automatically pulls in the cli dependencies [81443b3] + +## 1.27.1 + +### Maintenance + +- Bump golang.org/x/net from 0.6.0 to 0.7.0 (#640) [bc686cd] + +## 1.27.0 + +### Features +- Add HaveExactElements matcher (#634) [9d50783] +- update Gomega docs to discuss GinkgoHelper() [be32774] + +### Maintenance +- Bump github.com/onsi/ginkgo/v2 from 2.8.0 to 2.8.1 (#639) [296a68b] +- Bump golang.org/x/net from 0.5.0 to 0.6.0 (#638) [c2b098b] +- Bump github-pages from 227 to 228 in /docs (#636) [a9069ab] +- test: update matrix for Go 1.20 (#635) [6bd25c8] +- Bump github.com/onsi/ginkgo/v2 from 2.7.0 to 2.8.0 (#631) [5445f8b] +- Bump webrick from 1.7.0 to 1.8.1 in /docs (#630) [03e93bb] +- codeql: add ruby language (#626) [63c7d21] +- dependabot: add bundler package-ecosystem for docs (#625) [d92f963] + +## 1.26.0 + +### Features +- When a polled function returns an error, keep track of the actual and report on the matcher state of the last non-errored actual [21f3090] +- improve eventually failure message output [c530fb3] + +### Fixes +- fix several documentation spelling issues [e2eff1f] + + +## 1.25.0 + +### Features +- add `MustPassRepeatedly(int)` to asyncAssertion (#619) [4509f72] +- compare unwrapped errors using DeepEqual (#617) [aaeaa5d] + +### Maintenance +- Bump golang.org/x/net from 0.4.0 to 0.5.0 (#614) [c7cfea4] +- Bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0 (#615) [71b8adb] +- Docs: Fix typo "MUltiple" -> "Multiple" (#616) [9351dda] +- clean up go.sum [cd1dc1d] + +## 1.24.2 + +### Fixes +- Correctly handle assertion failure panics for eventually/consistnetly "g Gomega"s in a goroutine [78f1660] +- docs:Fix typo "you an" -> "you can" (#607) [3187c1f] +- fixes issue #600 (#606) [808d192] + +### Maintenance +- Bump golang.org/x/net from 0.2.0 to 0.4.0 (#611) [6ebc0bf] +- Bump nokogiri from 1.13.9 to 1.13.10 in /docs (#612) [258cfc8] +- Bump github.com/onsi/ginkgo/v2 from 2.5.0 to 2.5.1 (#609) [e6c3eb9] + +## 1.24.1 + +### Fixes +- maintain backward compatibility for Eventually and Consisntetly's signatures [4c7df5e] +- fix small typo (#601) [ea0ebe6] + +### Maintenance +- Bump golang.org/x/net from 0.1.0 to 0.2.0 (#603) [1ba8372] +- Bump github.com/onsi/ginkgo/v2 from 2.4.0 to 2.5.0 (#602) [f9426cb] +- fix label-filter in test.yml [d795db6] +- stop running flakey tests and rely on external network dependencies in CI [7133290] + +## 1.24.0 + +### Features + +Introducting [gcustom](https://onsi.github.io/gomega/#gcustom-a-convenient-mechanism-for-buildling-custom-matchers) - a convenient mechanism for building custom matchers. + +This is an RC release for `gcustom`. The external API may be tweaked in response to feedback however it is expected to remain mostly stable. + +### Maintenance + +- Update BeComparableTo documentation [756eaa0] + ## 1.23.0 ### Features diff --git a/vendor/github.com/onsi/gomega/RELEASING.md b/vendor/github.com/onsi/gomega/RELEASING.md index 7153b9b94..9973fff49 100644 --- a/vendor/github.com/onsi/gomega/RELEASING.md +++ b/vendor/github.com/onsi/gomega/RELEASING.md @@ -5,7 +5,7 @@ A Gomega release is a tagged sha and a GitHub release. To cut a release: ```bash LAST_VERSION=$(git tag --sort=version:refname | tail -n1) CHANGES=$(git log --pretty=format:'- %s [%h]' HEAD...$LAST_VERSION) - echo -e "## NEXT\n\n$CHANGES\n\n### Features\n\n## Fixes\n\n## Maintenance\n\n$(cat CHANGELOG.md)" > CHANGELOG.md + echo -e "## NEXT\n\n$CHANGES\n\n### Features\n\n### Fixes\n\n### Maintenance\n\n$(cat CHANGELOG.md)" > CHANGELOG.md ``` to update the changelog - Categorize the changes into diff --git a/vendor/github.com/onsi/gomega/format/format.go b/vendor/github.com/onsi/gomega/format/format.go index 1a2ed877a..6c1680638 100644 --- a/vendor/github.com/onsi/gomega/format/format.go +++ b/vendor/github.com/onsi/gomega/format/format.go @@ -52,7 +52,7 @@ var CharactersAroundMismatchToInclude uint = 5 var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() var timeType = reflect.TypeOf(time.Time{}) -//The default indentation string emitted by the format package +// The default indentation string emitted by the format package var Indent = " " var longFormThreshold = 20 @@ -258,7 +258,11 @@ Set PrintContextObjects to true to print the content of objects implementing con func Object(object interface{}, indentation uint) string { indent := strings.Repeat(Indent, int(indentation)) value := reflect.ValueOf(object) - return fmt.Sprintf("%s<%s>: %s", indent, formatType(value), formatValue(value, indentation)) + commonRepresentation := "" + if err, ok := object.(error); ok && !isNilValue(value) { // isNilValue check needed here to avoid nil deref due to boxed nil + commonRepresentation += "\n" + IndentString(err.Error(), indentation) + "\n" + indent + } + return fmt.Sprintf("%s<%s>: %s%s", indent, formatType(value), commonRepresentation, formatValue(value, indentation)) } /* @@ -298,7 +302,7 @@ func formatType(v reflect.Value) string { case reflect.Map: return fmt.Sprintf("%s | len:%d", v.Type(), v.Len()) default: - return fmt.Sprintf("%s", v.Type()) + return v.Type().String() } } diff --git a/vendor/github.com/onsi/gomega/gomega_dsl.go b/vendor/github.com/onsi/gomega/gomega_dsl.go index e236a40f4..1fd1803ac 100644 --- a/vendor/github.com/onsi/gomega/gomega_dsl.go +++ b/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -22,7 +22,7 @@ import ( "github.com/onsi/gomega/types" ) -const GOMEGA_VERSION = "1.23.0" +const GOMEGA_VERSION = "1.27.10" const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). @@ -204,7 +204,7 @@ func Ω(actual interface{}, extra ...interface{}) Assertion { // All subsequent arguments will be required to be nil/zero. // // This is convenient if you want to make an assertion on a method/function that returns -// a value and an error - a common patter in Go. +// a value and an error - a common pattern in Go. // // For example, given a function with signature: // @@ -360,6 +360,16 @@ You can also pass additional arugments to functions that take a Gomega. The onl g.Expect(elements).To(ConsistOf(expected)) }).WithContext(ctx).WithArguments("/names", "Joe", "Jane", "Sam").Should(Succeed()) +You can ensure that you get a number of consecutive successful tries before succeeding using `MustPassRepeatedly(int)`. For Example: + + int count := 0 + Eventually(func() bool { + count++ + return count > 2 + }).MustPassRepeatedly(2).Should(BeTrue()) + // Because we had to wait for 2 calls that returned true + Expect(count).To(Equal(3)) + Finally, in addition to passing timeouts and a context to Eventually you can be more explicit with Eventually's chaining configuration methods: Eventually(..., "1s", "2s", ctx).Should(...) @@ -368,9 +378,9 @@ is equivalent to Eventually(...).WithTimeout(time.Second).WithPolling(2*time.Second).WithContext(ctx).Should(...) */ -func Eventually(args ...interface{}) AsyncAssertion { +func Eventually(actualOrCtx interface{}, args ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() - return Default.Eventually(args...) + return Default.Eventually(actualOrCtx, args...) } // EventuallyWithOffset operates like Eventually but takes an additional @@ -382,9 +392,9 @@ func Eventually(args ...interface{}) AsyncAssertion { // `EventuallyWithOffset` specifying a timeout interval (and an optional polling interval) are // the same as `Eventually(...).WithOffset(...).WithTimeout` or // `Eventually(...).WithOffset(...).WithTimeout(...).WithPolling`. -func EventuallyWithOffset(offset int, args ...interface{}) AsyncAssertion { +func EventuallyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() - return Default.EventuallyWithOffset(offset, args...) + return Default.EventuallyWithOffset(offset, actualOrCtx, args...) } /* @@ -402,9 +412,9 @@ Consistently is useful in cases where you want to assert that something *does no This will block for 200 milliseconds and repeatedly check the channel and ensure nothing has been received. */ -func Consistently(args ...interface{}) AsyncAssertion { +func Consistently(actualOrCtx interface{}, args ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() - return Default.Consistently(args...) + return Default.Consistently(actualOrCtx, args...) } // ConsistentlyWithOffset operates like Consistently but takes an additional @@ -413,9 +423,9 @@ func Consistently(args ...interface{}) AsyncAssertion { // // `ConsistentlyWithOffset` is the same as `Consistently(...).WithOffset` and // optional `WithTimeout` and `WithPolling`. -func ConsistentlyWithOffset(offset int, args ...interface{}) AsyncAssertion { +func ConsistentlyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() - return Default.ConsistentlyWithOffset(offset, args...) + return Default.ConsistentlyWithOffset(offset, actualOrCtx, args...) } /* diff --git a/vendor/github.com/onsi/gomega/internal/async_assertion.go b/vendor/github.com/onsi/gomega/internal/async_assertion.go index c1e4a9995..1188b0bce 100644 --- a/vendor/github.com/onsi/gomega/internal/async_assertion.go +++ b/vendor/github.com/onsi/gomega/internal/async_assertion.go @@ -2,6 +2,7 @@ package internal import ( "context" + "errors" "fmt" "reflect" "runtime" @@ -16,10 +17,37 @@ var errInterface = reflect.TypeOf((*error)(nil)).Elem() var gomegaType = reflect.TypeOf((*types.Gomega)(nil)).Elem() var contextType = reflect.TypeOf(new(context.Context)).Elem() +type formattedGomegaError interface { + FormattedGomegaError() string +} + +type asyncPolledActualError struct { + message string +} + +func (err *asyncPolledActualError) Error() string { + return err.message +} + +func (err *asyncPolledActualError) FormattedGomegaError() string { + return err.message +} + type contextWithAttachProgressReporter interface { AttachProgressReporter(func() string) func() } +type asyncGomegaHaltExecutionError struct{} + +func (a asyncGomegaHaltExecutionError) GinkgoRecoverShouldIgnoreThisPanic() {} +func (a asyncGomegaHaltExecutionError) Error() string { + return `An assertion has failed in a goroutine. You should call + + defer GinkgoRecover() + +at the top of the goroutine that caused this panic. This will allow Ginkgo and Gomega to correctly capture and manage this panic.` +} + type AsyncAssertionType uint const ( @@ -44,21 +72,23 @@ type AsyncAssertion struct { actual interface{} argsToForward []interface{} - timeoutInterval time.Duration - pollingInterval time.Duration - ctx context.Context - offset int - g *Gomega + timeoutInterval time.Duration + pollingInterval time.Duration + mustPassRepeatedly int + ctx context.Context + offset int + g *Gomega } -func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, ctx context.Context, offset int) *AsyncAssertion { +func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, mustPassRepeatedly int, ctx context.Context, offset int) *AsyncAssertion { out := &AsyncAssertion{ - asyncType: asyncType, - timeoutInterval: timeoutInterval, - pollingInterval: pollingInterval, - offset: offset, - ctx: ctx, - g: g, + asyncType: asyncType, + timeoutInterval: timeoutInterval, + pollingInterval: pollingInterval, + mustPassRepeatedly: mustPassRepeatedly, + offset: offset, + ctx: ctx, + g: g, } out.actual = actualInput @@ -104,6 +134,11 @@ func (assertion *AsyncAssertion) WithArguments(argsToForward ...interface{}) typ return assertion } +func (assertion *AsyncAssertion) MustPassRepeatedly(count int) types.AsyncAssertion { + assertion.mustPassRepeatedly = count + return assertion +} + func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() vetOptionalDescription("Asynchronous assertion", optionalDescription...) @@ -130,7 +165,9 @@ func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interfa func (assertion *AsyncAssertion) processReturnValues(values []reflect.Value) (interface{}, error) { if len(values) == 0 { - return nil, fmt.Errorf("No values were returned by the function passed to Gomega") + return nil, &asyncPolledActualError{ + message: fmt.Sprintf("The function passed to %s did not return any values", assertion.asyncType), + } } actual := values[0].Interface() @@ -153,10 +190,12 @@ func (assertion *AsyncAssertion) processReturnValues(values []reflect.Value) (in continue } if i == len(values)-2 && extraType.Implements(errInterface) { - err = fmt.Errorf("function returned error: %w", extra.(error)) + err = extra.(error) } if err == nil { - err = fmt.Errorf("Unexpected non-nil/non-zero return value at index %d:\n\t<%T>: %#v", i+1, extra, extra) + err = &asyncPolledActualError{ + message: fmt.Sprintf("The function passed to %s had an unexpected non-nil/non-zero return value at index %d:\n%s", assertion.asyncType, i+1, format.Object(extra, 1)), + } } } @@ -191,6 +230,13 @@ You can learn more at https://onsi.github.io/gomega/#eventually `, assertion.asyncType, t, t.NumIn(), numProvided, have, assertion.asyncType) } +func (assertion *AsyncAssertion) invalidMustPassRepeatedlyError(reason string) error { + return fmt.Errorf(`Invalid use of MustPassRepeatedly with %s %s + +You can learn more at https://onsi.github.io/gomega/#eventually +`, assertion.asyncType, reason) +} + func (assertion *AsyncAssertion) buildActualPoller() (func() (interface{}, error), error) { if !assertion.actualIsFunc { return func() (interface{}, error) { return assertion.actual, nil }, nil @@ -228,8 +274,11 @@ func (assertion *AsyncAssertion) buildActualPoller() (func() (interface{}, error skip = callerSkip[0] } _, file, line, _ := runtime.Caller(skip + 1) - assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message) - panic("stop execution") + assertionFailure = &asyncPolledActualError{ + message: fmt.Sprintf("The function passed to %s failed at %s:%d with:\n%s", assertion.asyncType, file, line, message), + } + // we throw an asyncGomegaHaltExecutionError so that defer GinkgoRecover() can catch this error if the user makes an assertion in a goroutine + panic(asyncGomegaHaltExecutionError{}) }))) } if takesContext { @@ -245,6 +294,13 @@ func (assertion *AsyncAssertion) buildActualPoller() (func() (interface{}, error return nil, assertion.argumentMismatchError(actualType, len(inValues)) } + if assertion.mustPassRepeatedly != 1 && assertion.asyncType != AsyncAssertionTypeEventually { + return nil, assertion.invalidMustPassRepeatedlyError("it can only be used with Eventually") + } + if assertion.mustPassRepeatedly < 1 { + return nil, assertion.invalidMustPassRepeatedlyError("parameter can't be < 1") + } + return func() (actual interface{}, err error) { var values []reflect.Value assertionFailure = nil @@ -326,22 +382,39 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch timeout := assertion.afterTimeout() lock := sync.Mutex{} - var matches bool - var err error + var matches, hasLastValidActual bool + var actual, lastValidActual interface{} + var actualErr, matcherErr error var oracleMatcherSaysStop bool assertion.g.THelper() - pollActual, err := assertion.buildActualPoller() - if err != nil { - assertion.g.Fail(err.Error(), 2+assertion.offset) + pollActual, buildActualPollerErr := assertion.buildActualPoller() + if buildActualPollerErr != nil { + assertion.g.Fail(buildActualPollerErr.Error(), 2+assertion.offset) return false } - value, err := pollActual() - if err == nil { - oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, value) - matches, err = assertion.pollMatcher(matcher, value) + actual, actualErr = pollActual() + if actualErr == nil { + lastValidActual = actual + hasLastValidActual = true + oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, actual) + matches, matcherErr = assertion.pollMatcher(matcher, actual) + } + + renderError := func(preamble string, err error) string { + message := "" + if pollingSignalErr, ok := AsPollingSignalError(err); ok { + message = err.Error() + for _, attachment := range pollingSignalErr.Attachments { + message += fmt.Sprintf("\n%s:\n", attachment.Description) + message += format.Object(attachment.Object, 1) + } + } else { + message = preamble + "\n" + format.Object(err, 1) + } + return message } messageGenerator := func() string { @@ -349,23 +422,53 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch lock.Lock() defer lock.Unlock() message := "" - if err != nil { - if pollingSignalErr, ok := AsPollingSignalError(err); ok && pollingSignalErr.IsStopTrying() { - message = err.Error() - for _, attachment := range pollingSignalErr.Attachments { - message += fmt.Sprintf("\n%s:\n", attachment.Description) - message += format.Object(attachment.Object, 1) + + if actualErr == nil { + if matcherErr == nil { + if desiredMatch != matches { + if desiredMatch { + message += matcher.FailureMessage(actual) + } else { + message += matcher.NegatedFailureMessage(actual) + } + } else { + if assertion.asyncType == AsyncAssertionTypeConsistently { + message += "There is no failure as the matcher passed to Consistently has not yet failed" + } else { + message += "There is no failure as the matcher passed to Eventually succeeded on its most recent iteration" + } } } else { - message = "Error: " + err.Error() + "\n" + format.Object(err, 1) + var fgErr formattedGomegaError + if errors.As(actualErr, &fgErr) { + message += fgErr.FormattedGomegaError() + "\n" + } else { + message += renderError(fmt.Sprintf("The matcher passed to %s returned the following error:", assertion.asyncType), matcherErr) + } } } else { - if desiredMatch { - message = matcher.FailureMessage(value) + var fgErr formattedGomegaError + if errors.As(actualErr, &fgErr) { + message += fgErr.FormattedGomegaError() + "\n" } else { - message = matcher.NegatedFailureMessage(value) + message += renderError(fmt.Sprintf("The function passed to %s returned the following error:", assertion.asyncType), actualErr) + } + if hasLastValidActual { + message += fmt.Sprintf("\nAt one point, however, the function did return successfully.\nYet, %s failed because", assertion.asyncType) + _, e := matcher.Match(lastValidActual) + if e != nil { + message += renderError(" the matcher returned the following error:", e) + } else { + message += " the matcher was not satisfied:\n" + if desiredMatch { + message += matcher.FailureMessage(lastValidActual) + } else { + message += matcher.NegatedFailureMessage(lastValidActual) + } + } } } + description := assertion.buildDescription(optionalDescription...) return fmt.Sprintf("%s%s", description, message) } @@ -384,30 +487,39 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch } } + // Used to count the number of times in a row a step passed + passedRepeatedlyCount := 0 for { var nextPoll <-chan time.Time = nil var isTryAgainAfterError = false - if pollingSignalErr, ok := AsPollingSignalError(err); ok { - if pollingSignalErr.IsStopTrying() { - fail("Told to stop trying") - return false - } - if pollingSignalErr.IsTryAgainAfter() { - nextPoll = time.After(pollingSignalErr.TryAgainDuration()) - isTryAgainAfterError = true + for _, err := range []error{actualErr, matcherErr} { + if pollingSignalErr, ok := AsPollingSignalError(err); ok { + if pollingSignalErr.IsStopTrying() { + fail("Told to stop trying") + return false + } + if pollingSignalErr.IsTryAgainAfter() { + nextPoll = time.After(pollingSignalErr.TryAgainDuration()) + isTryAgainAfterError = true + } } } - if err == nil && matches == desiredMatch { + if actualErr == nil && matcherErr == nil && matches == desiredMatch { if assertion.asyncType == AsyncAssertionTypeEventually { - return true + passedRepeatedlyCount += 1 + if passedRepeatedlyCount == assertion.mustPassRepeatedly { + return true + } } } else if !isTryAgainAfterError { if assertion.asyncType == AsyncAssertionTypeConsistently { fail("Failed") return false } + // Reset the consecutive pass count + passedRepeatedlyCount = 0 } if oracleMatcherSaysStop { @@ -425,15 +537,19 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch select { case <-nextPoll: - v, e := pollActual() + a, e := pollActual() lock.Lock() - value, err = v, e + actual, actualErr = a, e lock.Unlock() - if err == nil { - oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, value) - m, e := assertion.pollMatcher(matcher, value) + if actualErr == nil { + lock.Lock() + lastValidActual = actual + hasLastValidActual = true + lock.Unlock() + oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, actual) + m, e := assertion.pollMatcher(matcher, actual) lock.Lock() - matches, err = m, e + matches, matcherErr = m, e lock.Unlock() } case <-contextDone: diff --git a/vendor/github.com/onsi/gomega/internal/gomega.go b/vendor/github.com/onsi/gomega/internal/gomega.go index e75d2626a..de1f4f336 100644 --- a/vendor/github.com/onsi/gomega/internal/gomega.go +++ b/vendor/github.com/onsi/gomega/internal/gomega.go @@ -2,7 +2,6 @@ package internal import ( "context" - "fmt" "time" "github.com/onsi/gomega/types" @@ -53,42 +52,38 @@ func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...inter return NewAssertion(actual, g, offset, extra...) } -func (g *Gomega) Eventually(args ...interface{}) types.AsyncAssertion { - return g.makeAsyncAssertion(AsyncAssertionTypeEventually, 0, args...) +func (g *Gomega) Eventually(actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion { + return g.makeAsyncAssertion(AsyncAssertionTypeEventually, 0, actualOrCtx, args...) } -func (g *Gomega) EventuallyWithOffset(offset int, args ...interface{}) types.AsyncAssertion { - return g.makeAsyncAssertion(AsyncAssertionTypeEventually, offset, args...) +func (g *Gomega) EventuallyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion { + return g.makeAsyncAssertion(AsyncAssertionTypeEventually, offset, actualOrCtx, args...) } -func (g *Gomega) Consistently(args ...interface{}) types.AsyncAssertion { - return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, 0, args...) +func (g *Gomega) Consistently(actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion { + return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, 0, actualOrCtx, args...) } -func (g *Gomega) ConsistentlyWithOffset(offset int, args ...interface{}) types.AsyncAssertion { - return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, offset, args...) +func (g *Gomega) ConsistentlyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion { + return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, offset, actualOrCtx, args...) } -func (g *Gomega) makeAsyncAssertion(asyncAssertionType AsyncAssertionType, offset int, args ...interface{}) types.AsyncAssertion { +func (g *Gomega) makeAsyncAssertion(asyncAssertionType AsyncAssertionType, offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion { baseOffset := 3 timeoutInterval := -time.Duration(1) pollingInterval := -time.Duration(1) intervals := []interface{}{} var ctx context.Context - if len(args) == 0 { - g.Fail(fmt.Sprintf("Call to %s is missing a value or function to poll", asyncAssertionType), offset+baseOffset) - return nil - } - actual := args[0] - startingIndex := 1 - if _, isCtx := args[0].(context.Context); isCtx && len(args) > 1 { + actual := actualOrCtx + startingIndex := 0 + if _, isCtx := actualOrCtx.(context.Context); isCtx && len(args) > 0 { // the first argument is a context, we should accept it as the context _only if_ it is **not** the only argumnent **and** the second argument is not a parseable duration // this is due to an unfortunate ambiguity in early version of Gomega in which multi-type durations are allowed after the actual - if _, err := toDuration(args[1]); err != nil { - ctx = args[0].(context.Context) - actual = args[1] - startingIndex = 2 + if _, err := toDuration(args[0]); err != nil { + ctx = actualOrCtx.(context.Context) + actual = args[0] + startingIndex = 1 } } @@ -114,7 +109,7 @@ func (g *Gomega) makeAsyncAssertion(asyncAssertionType AsyncAssertionType, offse } } - return NewAsyncAssertion(asyncAssertionType, actual, g, timeoutInterval, pollingInterval, ctx, offset) + return NewAsyncAssertion(asyncAssertionType, actual, g, timeoutInterval, pollingInterval, 1, ctx, offset) } func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) { diff --git a/vendor/github.com/onsi/gomega/matchers.go b/vendor/github.com/onsi/gomega/matchers.go index f9d9f2aad..bdaf62b56 100644 --- a/vendor/github.com/onsi/gomega/matchers.go +++ b/vendor/github.com/onsi/gomega/matchers.go @@ -27,7 +27,8 @@ func BeEquivalentTo(expected interface{}) types.GomegaMatcher { } } -// BeComparableTo uses gocmp.Equal to compare. You can pass cmp.Option as options. +// BeComparableTo uses gocmp.Equal from github.com/google/go-cmp (instead of reflect.DeepEqual) to perform a deep comparison. +// You can pass cmp.Option as options. // It is an error for actual and expected to be nil. Use BeNil() instead. func BeComparableTo(expected interface{}, opts ...cmp.Option) types.GomegaMatcher { return &matchers.BeComparableToMatcher{ @@ -86,14 +87,17 @@ func Succeed() types.GomegaMatcher { return &matchers.SucceedMatcher{} } -// MatchError succeeds if actual is a non-nil error that matches the passed in string/error. +// MatchError succeeds if actual is a non-nil error that matches the passed in +// string, error, or matcher. // // These are valid use-cases: // // Expect(err).Should(MatchError("an error")) //asserts that err.Error() == "an error" // Expect(err).Should(MatchError(SomeError)) //asserts that err == SomeError (via reflect.DeepEqual) +// Expect(err).Should(MatchError(ContainSubstring("sprocket not found"))) // asserts that edrr.Error() contains substring "sprocket not found" // -// It is an error for err to be nil or an object that does not implement the Error interface +// It is an error for err to be nil or an object that does not implement the +// Error interface func MatchError(expected interface{}) types.GomegaMatcher { return &matchers.MatchErrorMatcher{ Expected: expected, @@ -348,6 +352,20 @@ func ConsistOf(elements ...interface{}) types.GomegaMatcher { } } +// HaveExactElemets succeeds if actual contains elements that precisely match the elemets passed into the matcher. The ordering of the elements does matter. +// By default HaveExactElements() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: +// +// Expect([]string{"Foo", "FooBar"}).Should(HaveExactElements("Foo", "FooBar")) +// Expect([]string{"Foo", "FooBar"}).Should(HaveExactElements("Foo", ContainSubstring("Bar"))) +// Expect([]string{"Foo", "FooBar"}).Should(HaveExactElements(ContainSubstring("Foo"), ContainSubstring("Foo"))) +// +// Actual must be an array or slice. +func HaveExactElements(elements ...interface{}) types.GomegaMatcher { + return &matchers.HaveExactElementsMatcher{ + Elements: elements, + } +} + // ContainElements succeeds if actual contains the passed in elements. The ordering of the elements does not matter. // By default ContainElements() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: // diff --git a/vendor/github.com/onsi/gomega/matchers/be_a_directory.go b/vendor/github.com/onsi/gomega/matchers/be_a_directory.go index acffc8570..93d4497c7 100644 --- a/vendor/github.com/onsi/gomega/matchers/be_a_directory.go +++ b/vendor/github.com/onsi/gomega/matchers/be_a_directory.go @@ -52,5 +52,5 @@ func (matcher *BeADirectoryMatcher) FailureMessage(actual interface{}) (message } func (matcher *BeADirectoryMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not be a directory")) + return format.Message(actual, "not be a directory") } diff --git a/vendor/github.com/onsi/gomega/matchers/be_a_regular_file.go b/vendor/github.com/onsi/gomega/matchers/be_a_regular_file.go index 89441c800..8fefc4deb 100644 --- a/vendor/github.com/onsi/gomega/matchers/be_a_regular_file.go +++ b/vendor/github.com/onsi/gomega/matchers/be_a_regular_file.go @@ -52,5 +52,5 @@ func (matcher *BeARegularFileMatcher) FailureMessage(actual interface{}) (messag } func (matcher *BeARegularFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not be a regular file")) + return format.Message(actual, "not be a regular file") } diff --git a/vendor/github.com/onsi/gomega/matchers/be_an_existing_file.go b/vendor/github.com/onsi/gomega/matchers/be_an_existing_file.go index ec6506b00..e2bdd2811 100644 --- a/vendor/github.com/onsi/gomega/matchers/be_an_existing_file.go +++ b/vendor/github.com/onsi/gomega/matchers/be_an_existing_file.go @@ -32,9 +32,9 @@ func (matcher *BeAnExistingFileMatcher) Match(actual interface{}) (success bool, } func (matcher *BeAnExistingFileMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to exist")) + return format.Message(actual, "to exist") } func (matcher *BeAnExistingFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not to exist")) + return format.Message(actual, "not to exist") } diff --git a/vendor/github.com/onsi/gomega/matchers/consist_of.go b/vendor/github.com/onsi/gomega/matchers/consist_of.go index e8ef0dee1..f69037a4f 100644 --- a/vendor/github.com/onsi/gomega/matchers/consist_of.go +++ b/vendor/github.com/onsi/gomega/matchers/consist_of.go @@ -48,11 +48,13 @@ func neighbours(value, matcher interface{}) (bool, error) { func equalMatchersToElements(matchers []interface{}) (elements []interface{}) { for _, matcher := range matchers { - equalMatcher, ok := matcher.(*EqualMatcher) - if ok { - matcher = equalMatcher.Expected + if equalMatcher, ok := matcher.(*EqualMatcher); ok { + elements = append(elements, equalMatcher.Expected) + } else if _, ok := matcher.(*BeNilMatcher); ok { + elements = append(elements, nil) + } else { + elements = append(elements, matcher) } - elements = append(elements, matcher) } return } @@ -72,11 +74,13 @@ func flatten(elems []interface{}) []interface{} { func matchers(expectedElems []interface{}) (matchers []interface{}) { for _, e := range flatten(expectedElems) { - matcher, isMatcher := e.(omegaMatcher) - if !isMatcher { - matcher = &EqualMatcher{Expected: e} + if e == nil { + matchers = append(matchers, &BeNilMatcher{}) + } else if matcher, isMatcher := e.(omegaMatcher); isMatcher { + matchers = append(matchers, matcher) + } else { + matchers = append(matchers, &EqualMatcher{Expected: e}) } - matchers = append(matchers, matcher) } return } @@ -89,9 +93,14 @@ func presentable(elems []interface{}) interface{} { } sv := reflect.ValueOf(elems) - tt := sv.Index(0).Elem().Type() + firstEl := sv.Index(0) + if firstEl.IsNil() { + return elems + } + tt := firstEl.Elem().Type() for i := 1; i < sv.Len(); i++ { - if sv.Index(i).Elem().Type() != tt { + el := sv.Index(i) + if el.IsNil() || (sv.Index(i).Elem().Type() != tt) { return elems } } diff --git a/vendor/github.com/onsi/gomega/matchers/have_exact_elements.go b/vendor/github.com/onsi/gomega/matchers/have_exact_elements.go new file mode 100644 index 000000000..dca5b9446 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_exact_elements.go @@ -0,0 +1,88 @@ +package matchers + +import ( + "fmt" + + "github.com/onsi/gomega/format" +) + +type mismatchFailure struct { + failure string + index int +} + +type HaveExactElementsMatcher struct { + Elements []interface{} + mismatchFailures []mismatchFailure + missingIndex int + extraIndex int +} + +func (matcher *HaveExactElementsMatcher) Match(actual interface{}) (success bool, err error) { + matcher.resetState() + + if isMap(actual) { + return false, fmt.Errorf("error") + } + + matchers := matchers(matcher.Elements) + values := valuesOf(actual) + + lenMatchers := len(matchers) + lenValues := len(values) + + for i := 0; i < lenMatchers || i < lenValues; i++ { + if i >= lenMatchers { + matcher.extraIndex = i + continue + } + + if i >= lenValues { + matcher.missingIndex = i + return + } + + elemMatcher := matchers[i].(omegaMatcher) + match, err := elemMatcher.Match(values[i]) + if err != nil { + matcher.mismatchFailures = append(matcher.mismatchFailures, mismatchFailure{ + index: i, + failure: err.Error(), + }) + } else if !match { + matcher.mismatchFailures = append(matcher.mismatchFailures, mismatchFailure{ + index: i, + failure: elemMatcher.FailureMessage(values[i]), + }) + } + } + + return matcher.missingIndex+matcher.extraIndex+len(matcher.mismatchFailures) == 0, nil +} + +func (matcher *HaveExactElementsMatcher) FailureMessage(actual interface{}) (message string) { + message = format.Message(actual, "to have exact elements with", presentable(matcher.Elements)) + if matcher.missingIndex > 0 { + message = fmt.Sprintf("%s\nthe missing elements start from index %d", message, matcher.missingIndex) + } + if matcher.extraIndex > 0 { + message = fmt.Sprintf("%s\nthe extra elements start from index %d", message, matcher.extraIndex) + } + if len(matcher.mismatchFailures) != 0 { + message = fmt.Sprintf("%s\nthe mismatch indexes were:", message) + } + for _, mismatch := range matcher.mismatchFailures { + message = fmt.Sprintf("%s\n%d: %s", message, mismatch.index, mismatch.failure) + } + return +} + +func (matcher *HaveExactElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to contain elements", presentable(matcher.Elements)) +} + +func (matcher *HaveExactElementsMatcher) resetState() { + matcher.mismatchFailures = nil + matcher.missingIndex = 0 + matcher.extraIndex = 0 +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go index 5bcfdd2ad..22a1b6730 100644 --- a/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go +++ b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go @@ -31,5 +31,5 @@ func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message } func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Unexpected error:\n%s\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1), "occurred") + return fmt.Sprintf("Unexpected error:\n%s\n%s", format.Object(actual, 1), "occurred") } diff --git a/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go b/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go index c8993a86d..827475ea5 100644 --- a/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go +++ b/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go @@ -25,7 +25,17 @@ func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err e expected := matcher.Expected if isError(expected) { - return reflect.DeepEqual(actualErr, expected) || errors.Is(actualErr, expected.(error)), nil + // first try the built-in errors.Is + if errors.Is(actualErr, expected.(error)) { + return true, nil + } + // if not, try DeepEqual along the error chain + for unwrapped := actualErr; unwrapped != nil; unwrapped = errors.Unwrap(unwrapped) { + if reflect.DeepEqual(unwrapped, expected) { + return true, nil + } + } + return false, nil } if isString(expected) { diff --git a/vendor/github.com/onsi/gomega/matchers/succeed_matcher.go b/vendor/github.com/onsi/gomega/matchers/succeed_matcher.go index 721ed5529..327350f7b 100644 --- a/vendor/github.com/onsi/gomega/matchers/succeed_matcher.go +++ b/vendor/github.com/onsi/gomega/matchers/succeed_matcher.go @@ -1,11 +1,16 @@ package matchers import ( + "errors" "fmt" "github.com/onsi/gomega/format" ) +type formattedGomegaError interface { + FormattedGomegaError() string +} + type SucceedMatcher struct { } @@ -25,7 +30,11 @@ func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err erro } func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected success, but got an error:\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1)) + var fgErr formattedGomegaError + if errors.As(actual.(error), &fgErr) { + return fgErr.FormattedGomegaError() + } + return fmt.Sprintf("Expected success, but got an error:\n%s", format.Object(actual, 1)) } func (matcher *SucceedMatcher) NegatedFailureMessage(actual interface{}) (message string) { diff --git a/vendor/github.com/onsi/gomega/types/types.go b/vendor/github.com/onsi/gomega/types/types.go index 089505a4b..7c7adb941 100644 --- a/vendor/github.com/onsi/gomega/types/types.go +++ b/vendor/github.com/onsi/gomega/types/types.go @@ -19,11 +19,11 @@ type Gomega interface { Expect(actual interface{}, extra ...interface{}) Assertion ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion - Eventually(args ...interface{}) AsyncAssertion - EventuallyWithOffset(offset int, args ...interface{}) AsyncAssertion + Eventually(actualOrCtx interface{}, args ...interface{}) AsyncAssertion + EventuallyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) AsyncAssertion - Consistently(args ...interface{}) AsyncAssertion - ConsistentlyWithOffset(offset int, args ...interface{}) AsyncAssertion + Consistently(actualOrCtx interface{}, args ...interface{}) AsyncAssertion + ConsistentlyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) AsyncAssertion SetDefaultEventuallyTimeout(time.Duration) SetDefaultEventuallyPollingInterval(time.Duration) @@ -75,6 +75,7 @@ type AsyncAssertion interface { ProbeEvery(interval time.Duration) AsyncAssertion WithContext(ctx context.Context) AsyncAssertion WithArguments(argsToForward ...interface{}) AsyncAssertion + MustPassRepeatedly(count int) AsyncAssertion } // Assertions are returned by Ω and Expect and enable assertions against Gomega matchers diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/.travis.yml b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/.travis.yml index 15640cef3..4502e0632 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/.travis.yml +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/.travis.yml @@ -4,24 +4,15 @@ sudo: false matrix: include: - - go: "1.9.x" - - go: "1.10.x" - - go: "1.11.x" - env: - - GO111MODULE=off - - go: "1.12.x" - env: - - GO111MODULE=on - - go: "tip" - env: - - GO111MODULE=on + - go: "1.17.x" + - go: "1.18.x" + - go: "1.19.x" install: - go get -d -t ./... - - go get -u golang.org/x/lint/golint script: - - make test vet lint bench + - make test vet bench notifications: webhooks: diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/context.go b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/context.go index 4ca55b8f0..b5ef74b69 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/context.go +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/context.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OpenZipkin Authors +// Copyright 2022 The OpenZipkin Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/propagation.go b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/propagation.go index a85a65ca6..3dc7dbaae 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/propagation.go +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/propagation.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OpenZipkin Authors +// Copyright 2022 The OpenZipkin Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/span.go b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/span.go index fe7915d5b..3d202483b 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/span.go +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/span.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OpenZipkin Authors +// Copyright 2022 The OpenZipkin Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer.go b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer.go index b48d92ae3..748135396 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer.go +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OpenZipkin Authors +// Copyright 2022 The OpenZipkin Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer_options.go b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer_options.go index 190a692c4..260235ae7 100644 --- a/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer_options.go +++ b/vendor/github.com/openzipkin-contrib/zipkin-go-opentracing/tracer_options.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OpenZipkin Authors +// Copyright 2022 The OpenZipkin Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/openzipkin/zipkin-go/.golangci.yml b/vendor/github.com/openzipkin/zipkin-go/.golangci.yml index 3d615b8b0..e990f027f 100644 --- a/vendor/github.com/openzipkin/zipkin-go/.golangci.yml +++ b/vendor/github.com/openzipkin/zipkin-go/.golangci.yml @@ -16,9 +16,8 @@ linters: - lll - misspell - nakedret - - structcheck - unparam - - varcheck + - unused linters-settings: dupl: diff --git a/vendor/github.com/openzipkin/zipkin-go/README.md b/vendor/github.com/openzipkin/zipkin-go/README.md index bc91c66fd..05000f80a 100644 --- a/vendor/github.com/openzipkin/zipkin-go/README.md +++ b/vendor/github.com/openzipkin/zipkin-go/README.md @@ -17,15 +17,14 @@ limitations under the License. # Zipkin Library for Go [![GHA](https://github.com/openzipkin/zipkin-go/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/openzipkin/zipkin-go/actions/workflows/ci.yml) -[![CircleCI](https://circleci.com/gh/openzipkin/zipkin-go.svg?style=shield)](https://circleci.com/gh/openzipkin/zipkin-go) [![codecov](https://codecov.io/gh/openzipkin/zipkin-go/branch/master/graph/badge.svg?token=gXdWofFlsq)](https://codecov.io/gh/openzipkin/zipkin-go) [![Go Report Card](https://goreportcard.com/badge/github.com/openzipkin/zipkin-go)](https://goreportcard.com/report/github.com/openzipkin/zipkin-go) [![GoDoc](https://godoc.org/github.com/openzipkin/zipkin-go?status.svg)](https://godoc.org/github.com/openzipkin/zipkin-go) [![Gitter chat](https://badges.gitter.im/openzipkin/zipkin.svg)](https://gitter.im/openzipkin/zipkin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Sourcegraph](https://sourcegraph.com/github.com/openzipkin/zipkin-go/-/badge.svg)](https://sourcegraph.com/github.com/openzipkin/zipkin-go?badge) -Zipkin Go is the official Go Tracer / Tracing implementation for Zipkin, supported by the -OpenZipkin community. +Zipkin Go is the official Go Tracer / Tracing implementation for Zipkin, +supported by the OpenZipkin community. ## package organization `zipkin-go` is built with interoperability in mind within the OpenZipkin @@ -70,8 +69,8 @@ For convenience `NewClient` is provided which returns a HTTP Client which embeds calling the `DoWithAppSpan()` method. #### grpc -Easy to use grpc.StatsHandler middleware are provided for tracing gRPC server and -client requests. +Easy to use grpc.StatsHandler middleware are provided for tracing gRPC server +and client requests. For a server, pass `NewServerHandler` when calling `NewServer`, e.g., @@ -110,7 +109,7 @@ backend asynchronously. #### Kafka Reporter High performance Reporter transporting Spans to the Zipkin server using a Kafka Producer digesting JSON V2 Spans. The reporter uses the -[Sarama async producer](https://godoc.org/github.com/Shopify/sarama#AsyncProducer) +[Sarama async producer](https://pkg.go.dev/github.com/IBM/sarama#AsyncProducer) underneath. ## usage and examples diff --git a/vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go b/vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go index 80a209c2c..ac0e28dfa 100644 --- a/vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go +++ b/vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go @@ -21,8 +21,10 @@ import ( "github.com/openzipkin/zipkin-go/propagation" ) +// InjectOption provides functional option handler type. type InjectOption func(opts *InjectOptions) +// InjectOptions provides the available functional options. type InjectOptions struct { shouldInjectSingleHeader bool shouldInjectMultiHeader bool diff --git a/vendor/github.com/dgraph-io/ristretto/.deepsource.toml b/vendor/github.com/outcaste-io/ristretto/.deepsource.toml similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/.deepsource.toml rename to vendor/github.com/outcaste-io/ristretto/.deepsource.toml diff --git a/vendor/github.com/outcaste-io/ristretto/.mailmap b/vendor/github.com/outcaste-io/ristretto/.mailmap new file mode 100644 index 000000000..8ea0986d4 --- /dev/null +++ b/vendor/github.com/outcaste-io/ristretto/.mailmap @@ -0,0 +1 @@ +Manish R Jain diff --git a/vendor/github.com/dgraph-io/ristretto/CHANGELOG.md b/vendor/github.com/outcaste-io/ristretto/CHANGELOG.md similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/CHANGELOG.md rename to vendor/github.com/outcaste-io/ristretto/CHANGELOG.md diff --git a/vendor/github.com/dgraph-io/ristretto/LICENSE b/vendor/github.com/outcaste-io/ristretto/LICENSE similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/LICENSE rename to vendor/github.com/outcaste-io/ristretto/LICENSE diff --git a/vendor/github.com/dgraph-io/ristretto/README.md b/vendor/github.com/outcaste-io/ristretto/README.md similarity index 82% rename from vendor/github.com/dgraph-io/ristretto/README.md rename to vendor/github.com/outcaste-io/ristretto/README.md index f4bb28cd7..80a43ec14 100644 --- a/vendor/github.com/dgraph-io/ristretto/README.md +++ b/vendor/github.com/outcaste-io/ristretto/README.md @@ -1,17 +1,17 @@ # Ristretto -[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/dgraph-io/ristretto) -[![Go Report Card](https://img.shields.io/badge/go%20report-A%2B-brightgreen)](https://goreportcard.com/report/github.com/dgraph-io/ristretto) -[![Coverage](https://gocover.io/_badge/github.com/dgraph-io/ristretto)](https://gocover.io/github.com/dgraph-io/ristretto) -![Tests](https://github.com/dgraph-io/ristretto/workflows/tests/badge.svg) +[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/outcaste-io/ristretto) +[![Go Report Card](https://img.shields.io/badge/go%20report-A%2B-brightgreen)](https://goreportcard.com/report/github.com/outcaste-io/ristretto) +[![Coverage](https://gocover.io/_badge/github.com/outcaste-io/ristretto)](https://gocover.io/github.com/outcaste-io/ristretto) +![Tests](https://github.com/outcaste-io/ristretto/workflows/tests/badge.svg) + +**This is a fork of dgraph-io/ristretto, maintained by @manishrjain.** Ristretto is a fast, concurrent cache library built with a focus on performance and correctness. The motivation to build Ristretto comes from the need for a contention-free -cache in [Dgraph][]. - -**Use [Discuss Issues](https://discuss.dgraph.io/tags/c/issues/35/ristretto/40) for reporting issues about this repository.** +cache. -[Dgraph]: https://github.com/dgraph-io/dgraph +[issues]: https://github.com/outcaste-io/issues ## Features @@ -20,10 +20,26 @@ cache in [Dgraph][]. * **Admission: TinyLFU** - extra performance with little memory overhead (12 bits per counter). * **Fast Throughput** - we use a variety of techniques for managing contention and the result is excellent throughput. * **Cost-Based Eviction** - any large new item deemed valuable can evict multiple smaller items (cost could be anything). -* **Fully Concurrent** - you can use as many goroutines as you want with little throughput degradation. +* **Fully Concurrent** - you can use as many goroutines as you want with little throughput degradation. * **Metrics** - optional performance metrics for throughput, hit ratios, and other stats. * **Simple API** - just figure out your ideal `Config` values and you're off and running. +## Note on jemalloc + +We have been using jemalloc v5.2.1. +To use jemalloc, please configure jemalloc with these flags: + +``` +./configure --with-install-suffix='_outcaste' --with-jemalloc-prefix='je_' --with-malloc-conf='background_thread:true,metadata_thp:auto'; \ +make +make install_lib install_include # Use sudo if needed in this step. +``` + +outserv/outserv Makefile has these build steps already present. You can run +`make jemalloc` to install it. This jemalloc would not interfere with any other +jemalloc installation that might already be present on the system. + + ## Status Ristretto is production-ready. See [Projects using Ristretto](#projects-using-ristretto). @@ -70,9 +86,9 @@ func main() { // set a value with a cost of 1 cache.Set("key", "value", 1) - + // wait for value to pass through buffers - time.Sleep(10 * time.Millisecond) + cache.Wait() value, found := cache.Get("key") if !found { @@ -85,31 +101,31 @@ func main() { ### Config -The `Config` struct is passed to `NewCache` when creating Ristretto instances (see the example above). +The `Config` struct is passed to `NewCache` when creating Ristretto instances (see the example above). **NumCounters** `int64` -NumCounters is the number of 4-bit access counters to keep for admission and eviction. We've seen good performance in setting this to 10x the number of items you expect to keep in the cache when full. +NumCounters is the number of 4-bit access counters to keep for admission and eviction. We've seen good performance in setting this to 10x the number of items you expect to keep in the cache when full. -For example, if you expect each item to have a cost of 1 and MaxCost is 100, set NumCounters to 1,000. Or, if you use variable cost values but expect the cache to hold around 10,000 items when full, set NumCounters to 100,000. The important thing is the *number of unique items* in the full cache, not necessarily the MaxCost value. +For example, if you expect each item to have a cost of 1 and MaxCost is 100, set NumCounters to 1,000. Or, if you use variable cost values but expect the cache to hold around 10,000 items when full, set NumCounters to 100,000. The important thing is the *number of unique items* in the full cache, not necessarily the MaxCost value. **MaxCost** `int64` -MaxCost is how eviction decisions are made. For example, if MaxCost is 100 and a new item with a cost of 1 increases total cache cost to 101, 1 item will be evicted. +MaxCost is how eviction decisions are made. For example, if MaxCost is 100 and a new item with a cost of 1 increases total cache cost to 101, 1 item will be evicted. -MaxCost can also be used to denote the max size in bytes. For example, if MaxCost is 1,000,000 (1MB) and the cache is full with 1,000 1KB items, a new item (that's accepted) would cause 5 1KB items to be evicted. +MaxCost can also be used to denote the max size in bytes. For example, if MaxCost is 1,000,000 (1MB) and the cache is full with 1,000 1KB items, a new item (that's accepted) would cause 5 1KB items to be evicted. -MaxCost could be anything as long as it matches how you're using the cost values when calling Set. +MaxCost could be anything as long as it matches how you're using the cost values when calling Set. **BufferItems** `int64` -BufferItems is the size of the Get buffers. The best value we've found for this is 64. +BufferItems is the size of the Get buffers. The best value we've found for this is 64. If for some reason you see Get performance decreasing with lots of contention (you shouldn't), try increasing this value in increments of 64. This is a fine-tuning mechanism and you probably won't have to touch this. **Metrics** `bool` -Metrics is true when you want real-time logging of a variety of stats. The reason this is a Config flag is because there's a 10% throughput performance overhead. +Metrics is true when you want real-time logging of a variety of stats. The reason this is a Config flag is because there's a 10% throughput performance overhead. **OnEvict** `func(hashes [2]uint64, value interface{}, cost int64)` @@ -117,7 +133,7 @@ OnEvict is called for every eviction. **KeyToHash** `func(key interface{}) [2]uint64` -KeyToHash is the hashing algorithm used for every key. If this is nil, Ristretto has a variety of [defaults depending on the underlying interface type](https://github.com/dgraph-io/ristretto/blob/master/z/z.go#L19-L41). +KeyToHash is the hashing algorithm used for every key. If this is nil, Ristretto has a variety of [defaults depending on the underlying interface type](https://github.com/outcaste-io/ristretto/blob/master/z/z.go#L19-L41). Note that if you want 128bit hashes you should use the full `[2]uint64`, otherwise just fill the `uint64` at the `0` position and it will behave like @@ -205,7 +221,8 @@ Below is a list of known projects that use Ristretto: - [Badger](https://github.com/dgraph-io/badger) - Embeddable key-value DB in Go - [Dgraph](https://github.com/dgraph-io/dgraph) - Horizontally scalable and distributed GraphQL database with a graph backend -- [Vitess](https://github.com/vitessio/vitess) - database clustering system for horizontal scaling of MySQL +- [Vitess](https://github.com/vitessio/vitess) - Database clustering system for horizontal scaling of MySQL +- [SpiceDB](https://github.com/authzed/spicedb) - Horizontally scalable permissions database ## FAQ @@ -213,8 +230,8 @@ Below is a list of known projects that use Ristretto: We go into detail in the [Ristretto blog post](https://blog.dgraph.io/post/introducing-ristretto-high-perf-go-cache/), but in short: our throughput performance can be attributed to a mix of batching and eventual consistency. Our hit ratio performance is mostly due to an excellent [admission policy](https://arxiv.org/abs/1512.00727) and SampledLFU eviction policy. -As for "shortcuts," the only thing Ristretto does that could be construed as one is dropping some Set calls. That means a Set call for a new item (updates are guaranteed) isn't guaranteed to make it into the cache. The new item could be dropped at two points: when passing through the Set buffer or when passing through the admission policy. However, this doesn't affect hit ratios much at all as we expect the most popular items to be Set multiple times and eventually make it in the cache. +As for "shortcuts," the only thing Ristretto does that could be construed as one is dropping some Set calls. That means a Set call for a new item (updates are guaranteed) isn't guaranteed to make it into the cache. The new item could be dropped at two points: when passing through the Set buffer or when passing through the admission policy. However, this doesn't affect hit ratios much at all as we expect the most popular items to be Set multiple times and eventually make it in the cache. ### Is Ristretto distributed? -No, it's just like any other Go library that you can import into your project and use in a single process. +No, it's just like any other Go library that you can import into your project and use in a single process. diff --git a/vendor/github.com/dgraph-io/ristretto/cache.go b/vendor/github.com/outcaste-io/ristretto/cache.go similarity index 72% rename from vendor/github.com/dgraph-io/ristretto/cache.go rename to vendor/github.com/outcaste-io/ristretto/cache.go index 7226245bc..18e3647e8 100644 --- a/vendor/github.com/dgraph-io/ristretto/cache.go +++ b/vendor/github.com/outcaste-io/ristretto/cache.go @@ -20,15 +20,13 @@ package ristretto import ( - "bytes" "errors" - "fmt" "sync" - "sync/atomic" "time" "unsafe" - "github.com/dgraph-io/ristretto/z" + "github.com/outcaste-io/ristretto/z" + "go.uber.org/atomic" ) var ( @@ -45,9 +43,9 @@ const itemSize = int64(unsafe.Sizeof(storeItem{})) // from as many goroutines as you want. type Cache struct { // store is the central concurrent hashmap where key-value items are stored. - store store + store *shardedMap // policy determines what gets let in to the cache and what gets kicked out. - policy policy + policy *lfuPolicy // getBuf is a custom ring buffer implementation that gets pushed to when // keys are read. getBuf *ringBuffer @@ -67,7 +65,7 @@ type Cache struct { // stop is used to stop the processItems goroutine. stop chan struct{} // indicates whether cache is closed. - isClosed bool + isClosed atomic.Bool // cost calculates cost from a value. cost func(value interface{}) int64 // ignoreInternalCost dictates whether to ignore the cost of internally storing @@ -126,6 +124,8 @@ type Config struct { // Each key will be hashed using the provided function. If keyToHash value // is not set, the default keyToHash function is used. KeyToHash func(key interface{}) (uint64, uint64) + // shouldUpdate is called when a value already exists in cache and is being updated. + ShouldUpdate func(prev, cur interface{}) bool // Cost evaluates a value and outputs a corresponding cost. This function // is ran after Set is called for a new item or an item update with a cost // param of 0. @@ -168,7 +168,7 @@ func NewCache(config *Config) (*Cache, error) { } policy := newPolicy(config.NumCounters, config.MaxCost) cache := &Cache{ - store: newStore(), + store: newShardedMap(config.ShouldUpdate), policy: policy, getBuf: newRingBuffer(policy, config.BufferItems), setBuf: make(chan *Item, setBufSize), @@ -195,6 +195,12 @@ func NewCache(config *Config) (*Cache, error) { } cache.onExit(item.Value) } + cache.store.shouldUpdate = func(prev, cur interface{}) bool { + if config.ShouldUpdate != nil { + return config.ShouldUpdate(prev, cur) + } + return true + } if cache.keyToHash == nil { cache.keyToHash = z.KeyToHash } @@ -209,7 +215,7 @@ func NewCache(config *Config) (*Cache, error) { } func (c *Cache) Wait() { - if c == nil || c.isClosed { + if c == nil || c.isClosed.Load() { return } wg := &sync.WaitGroup{} @@ -222,7 +228,7 @@ func (c *Cache) Wait() { // value was found or not. The value can be nil and the boolean can be true at // the same time. func (c *Cache) Get(key interface{}) (interface{}, bool) { - if c == nil || c.isClosed || key == nil { + if c == nil || c.isClosed.Load() || key == nil { return nil, false } keyHash, conflictHash := c.keyToHash(key) @@ -254,7 +260,18 @@ func (c *Cache) Set(key, value interface{}, cost int64) bool { // expires, which is identical to calling Set. A negative value is a no-op and the value // is discarded. func (c *Cache) SetWithTTL(key, value interface{}, cost int64, ttl time.Duration) bool { - if c == nil || c.isClosed || key == nil { + return c.setInternal(key, value, cost, ttl, false) +} + +// SetIfPresent is like Set, but only updates the value of an existing key. It +// does NOT add the key to cache if it's absent. +func (c *Cache) SetIfPresent(key, value interface{}, cost int64) bool { + return c.setInternal(key, value, cost, 0*time.Second, true) +} + +func (c *Cache) setInternal(key, value interface{}, + cost int64, ttl time.Duration, onlyUpdate bool) bool { + if c == nil || c.isClosed.Load() || key == nil { return false } @@ -279,11 +296,18 @@ func (c *Cache) SetWithTTL(key, value interface{}, cost int64, ttl time.Duration Cost: cost, Expiration: expiration, } + if onlyUpdate { + i.flag = itemUpdate + } // cost is eventually updated. The expiration must also be immediately updated // to prevent items from being prematurely removed from the map. if prev, ok := c.store.Update(i); ok { c.onExit(prev) i.flag = itemUpdate + } else if onlyUpdate { + // The instruction was to update the key, but store.Update failed. So, + // this is a NOOP. + return false } // Attempt to send item to policy. select { @@ -303,7 +327,7 @@ func (c *Cache) SetWithTTL(key, value interface{}, cost int64, ttl time.Duration // Del deletes the key-value item from the cache if it exists. func (c *Cache) Del(key interface{}) { - if c == nil || c.isClosed || key == nil { + if c == nil || c.isClosed.Load() || key == nil { return } keyHash, conflictHash := c.keyToHash(key) @@ -350,7 +374,7 @@ func (c *Cache) GetTTL(key interface{}) (time.Duration, bool) { // Close stops all goroutines and closes all channels. func (c *Cache) Close() { - if c == nil || c.isClosed { + if c == nil || c.isClosed.Load() { return } c.Clear() @@ -360,14 +384,14 @@ func (c *Cache) Close() { close(c.stop) close(c.setBuf) c.policy.Close() - c.isClosed = true + c.isClosed.Store(true) } // Clear empties the hashmap and zeroes all policy counters. Note that this is // not an atomic operation (but that shouldn't be a problem as it's assumed that // Set/Get calls won't be occurring until after this). func (c *Cache) Clear() { - if c == nil || c.isClosed { + if c == nil || c.isClosed.Load() { return } // Block until processItems goroutine is returned. @@ -494,226 +518,3 @@ func (c *Cache) processItems() { } } } - -// collectMetrics just creates a new *Metrics instance and adds the pointers -// to the cache and policy instances. -func (c *Cache) collectMetrics() { - c.Metrics = newMetrics() - c.policy.CollectMetrics(c.Metrics) -} - -type metricType int - -const ( - // The following 2 keep track of hits and misses. - hit = iota - miss - // The following 3 keep track of number of keys added, updated and evicted. - keyAdd - keyUpdate - keyEvict - // The following 2 keep track of cost of keys added and evicted. - costAdd - costEvict - // The following keep track of how many sets were dropped or rejected later. - dropSets - rejectSets - // The following 2 keep track of how many gets were kept and dropped on the - // floor. - dropGets - keepGets - // This should be the final enum. Other enums should be set before this. - doNotUse -) - -func stringFor(t metricType) string { - switch t { - case hit: - return "hit" - case miss: - return "miss" - case keyAdd: - return "keys-added" - case keyUpdate: - return "keys-updated" - case keyEvict: - return "keys-evicted" - case costAdd: - return "cost-added" - case costEvict: - return "cost-evicted" - case dropSets: - return "sets-dropped" - case rejectSets: - return "sets-rejected" // by policy. - case dropGets: - return "gets-dropped" - case keepGets: - return "gets-kept" - default: - return "unidentified" - } -} - -// Metrics is a snapshot of performance statistics for the lifetime of a cache instance. -type Metrics struct { - all [doNotUse][]*uint64 - - mu sync.RWMutex - life *z.HistogramData // Tracks the life expectancy of a key. -} - -func newMetrics() *Metrics { - s := &Metrics{ - life: z.NewHistogramData(z.HistogramBounds(1, 16)), - } - for i := 0; i < doNotUse; i++ { - s.all[i] = make([]*uint64, 256) - slice := s.all[i] - for j := range slice { - slice[j] = new(uint64) - } - } - return s -} - -func (p *Metrics) add(t metricType, hash, delta uint64) { - if p == nil { - return - } - valp := p.all[t] - // Avoid false sharing by padding at least 64 bytes of space between two - // atomic counters which would be incremented. - idx := (hash % 25) * 10 - atomic.AddUint64(valp[idx], delta) -} - -func (p *Metrics) get(t metricType) uint64 { - if p == nil { - return 0 - } - valp := p.all[t] - var total uint64 - for i := range valp { - total += atomic.LoadUint64(valp[i]) - } - return total -} - -// Hits is the number of Get calls where a value was found for the corresponding key. -func (p *Metrics) Hits() uint64 { - return p.get(hit) -} - -// Misses is the number of Get calls where a value was not found for the corresponding key. -func (p *Metrics) Misses() uint64 { - return p.get(miss) -} - -// KeysAdded is the total number of Set calls where a new key-value item was added. -func (p *Metrics) KeysAdded() uint64 { - return p.get(keyAdd) -} - -// KeysUpdated is the total number of Set calls where the value was updated. -func (p *Metrics) KeysUpdated() uint64 { - return p.get(keyUpdate) -} - -// KeysEvicted is the total number of keys evicted. -func (p *Metrics) KeysEvicted() uint64 { - return p.get(keyEvict) -} - -// CostAdded is the sum of costs that have been added (successful Set calls). -func (p *Metrics) CostAdded() uint64 { - return p.get(costAdd) -} - -// CostEvicted is the sum of all costs that have been evicted. -func (p *Metrics) CostEvicted() uint64 { - return p.get(costEvict) -} - -// SetsDropped is the number of Set calls that don't make it into internal -// buffers (due to contention or some other reason). -func (p *Metrics) SetsDropped() uint64 { - return p.get(dropSets) -} - -// SetsRejected is the number of Set calls rejected by the policy (TinyLFU). -func (p *Metrics) SetsRejected() uint64 { - return p.get(rejectSets) -} - -// GetsDropped is the number of Get counter increments that are dropped -// internally. -func (p *Metrics) GetsDropped() uint64 { - return p.get(dropGets) -} - -// GetsKept is the number of Get counter increments that are kept. -func (p *Metrics) GetsKept() uint64 { - return p.get(keepGets) -} - -// Ratio is the number of Hits over all accesses (Hits + Misses). This is the -// percentage of successful Get calls. -func (p *Metrics) Ratio() float64 { - if p == nil { - return 0.0 - } - hits, misses := p.get(hit), p.get(miss) - if hits == 0 && misses == 0 { - return 0.0 - } - return float64(hits) / float64(hits+misses) -} - -func (p *Metrics) trackEviction(numSeconds int64) { - if p == nil { - return - } - p.mu.Lock() - defer p.mu.Unlock() - p.life.Update(numSeconds) -} - -func (p *Metrics) LifeExpectancySeconds() *z.HistogramData { - if p == nil { - return nil - } - p.mu.RLock() - defer p.mu.RUnlock() - return p.life.Copy() -} - -// Clear resets all the metrics. -func (p *Metrics) Clear() { - if p == nil { - return - } - for i := 0; i < doNotUse; i++ { - for j := range p.all[i] { - atomic.StoreUint64(p.all[i][j], 0) - } - } - p.mu.Lock() - p.life = z.NewHistogramData(z.HistogramBounds(1, 16)) - p.mu.Unlock() -} - -// String returns a string representation of the metrics. -func (p *Metrics) String() string { - if p == nil { - return "" - } - var buf bytes.Buffer - for i := 0; i < doNotUse; i++ { - t := metricType(i) - fmt.Fprintf(&buf, "%s: %d ", stringFor(t), p.get(t)) - } - fmt.Fprintf(&buf, "gets-total: %d ", p.get(hit)+p.get(miss)) - fmt.Fprintf(&buf, "hit-ratio: %.2f", p.Ratio()) - return buf.String() -} diff --git a/vendor/github.com/outcaste-io/ristretto/metrics.go b/vendor/github.com/outcaste-io/ristretto/metrics.go new file mode 100644 index 000000000..c2db77fad --- /dev/null +++ b/vendor/github.com/outcaste-io/ristretto/metrics.go @@ -0,0 +1,249 @@ +/* + * Copyright 2021 Dgraph Labs, Inc. and Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ristretto + +import ( + "bytes" + "fmt" + "sync" + "sync/atomic" + + "github.com/outcaste-io/ristretto/z" +) + +type metricType int + +const ( + // The following 2 keep track of hits and misses. + hit = iota + miss + // The following 3 keep track of number of keys added, updated and evicted. + keyAdd + keyUpdate + keyEvict + // The following 2 keep track of cost of keys added and evicted. + costAdd + costEvict + // The following keep track of how many sets were dropped or rejected later. + dropSets + rejectSets + // The following 2 keep track of how many gets were kept and dropped on the + // floor. + dropGets + keepGets + // This should be the final enum. Other enums should be set before this. + doNotUse +) + +func stringFor(t metricType) string { + switch t { + case hit: + return "hit" + case miss: + return "miss" + case keyAdd: + return "keys-added" + case keyUpdate: + return "keys-updated" + case keyEvict: + return "keys-evicted" + case costAdd: + return "cost-added" + case costEvict: + return "cost-evicted" + case dropSets: + return "sets-dropped" + case rejectSets: + return "sets-rejected" // by policy. + case dropGets: + return "gets-dropped" + case keepGets: + return "gets-kept" + default: + return "unidentified" + } +} + +// Metrics is a snapshot of performance statistics for the lifetime of a cache instance. +type Metrics struct { + all [doNotUse][]*uint64 + + mu sync.RWMutex + life *z.HistogramData // Tracks the life expectancy of a key. +} + +// collectMetrics just creates a new *Metrics instance and adds the pointers +// to the cache and policy instances. +func (c *Cache) collectMetrics() { + c.Metrics = newMetrics() + c.policy.CollectMetrics(c.Metrics) +} + +func newMetrics() *Metrics { + s := &Metrics{ + life: z.NewHistogramData(z.HistogramBounds(1, 16)), + } + for i := 0; i < doNotUse; i++ { + s.all[i] = make([]*uint64, 256) + slice := s.all[i] + for j := range slice { + slice[j] = new(uint64) + } + } + return s +} + +func (p *Metrics) add(t metricType, hash, delta uint64) { + if p == nil { + return + } + valp := p.all[t] + // Avoid false sharing by padding at least 64 bytes of space between two + // atomic counters which would be incremented. + idx := (hash % 25) * 10 + atomic.AddUint64(valp[idx], delta) +} + +func (p *Metrics) get(t metricType) uint64 { + if p == nil { + return 0 + } + valp := p.all[t] + var total uint64 + for i := range valp { + total += atomic.LoadUint64(valp[i]) + } + return total +} + +// Hits is the number of Get calls where a value was found for the corresponding key. +func (p *Metrics) Hits() uint64 { + return p.get(hit) +} + +// Misses is the number of Get calls where a value was not found for the corresponding key. +func (p *Metrics) Misses() uint64 { + return p.get(miss) +} + +// KeysAdded is the total number of Set calls where a new key-value item was added. +func (p *Metrics) KeysAdded() uint64 { + return p.get(keyAdd) +} + +// KeysUpdated is the total number of Set calls where the value was updated. +func (p *Metrics) KeysUpdated() uint64 { + return p.get(keyUpdate) +} + +// KeysEvicted is the total number of keys evicted. +func (p *Metrics) KeysEvicted() uint64 { + return p.get(keyEvict) +} + +// CostAdded is the sum of costs that have been added (successful Set calls). +func (p *Metrics) CostAdded() uint64 { + return p.get(costAdd) +} + +// CostEvicted is the sum of all costs that have been evicted. +func (p *Metrics) CostEvicted() uint64 { + return p.get(costEvict) +} + +// SetsDropped is the number of Set calls that don't make it into internal +// buffers (due to contention or some other reason). +func (p *Metrics) SetsDropped() uint64 { + return p.get(dropSets) +} + +// SetsRejected is the number of Set calls rejected by the policy (TinyLFU). +func (p *Metrics) SetsRejected() uint64 { + return p.get(rejectSets) +} + +// GetsDropped is the number of Get counter increments that are dropped +// internally. +func (p *Metrics) GetsDropped() uint64 { + return p.get(dropGets) +} + +// GetsKept is the number of Get counter increments that are kept. +func (p *Metrics) GetsKept() uint64 { + return p.get(keepGets) +} + +// Ratio is the number of Hits over all accesses (Hits + Misses). This is the +// percentage of successful Get calls. +func (p *Metrics) Ratio() float64 { + if p == nil { + return 0.0 + } + hits, misses := p.get(hit), p.get(miss) + if hits == 0 && misses == 0 { + return 0.0 + } + return float64(hits) / float64(hits+misses) +} + +func (p *Metrics) trackEviction(numSeconds int64) { + if p == nil { + return + } + p.mu.Lock() + defer p.mu.Unlock() + p.life.Update(numSeconds) +} + +func (p *Metrics) LifeExpectancySeconds() *z.HistogramData { + if p == nil { + return nil + } + p.mu.RLock() + defer p.mu.RUnlock() + return p.life.Copy() +} + +// Clear resets all the metrics. +func (p *Metrics) Clear() { + if p == nil { + return + } + for i := 0; i < doNotUse; i++ { + for j := range p.all[i] { + atomic.StoreUint64(p.all[i][j], 0) + } + } + p.mu.Lock() + p.life = z.NewHistogramData(z.HistogramBounds(1, 16)) + p.mu.Unlock() +} + +// String returns a string representation of the metrics. +func (p *Metrics) String() string { + if p == nil { + return "" + } + var buf bytes.Buffer + for i := 0; i < doNotUse; i++ { + t := metricType(i) + fmt.Fprintf(&buf, "%s: %d ", stringFor(t), p.get(t)) + } + fmt.Fprintf(&buf, "gets-total: %d ", p.get(hit)+p.get(miss)) + fmt.Fprintf(&buf, "hit-ratio: %.2f", p.Ratio()) + return buf.String() +} diff --git a/vendor/github.com/dgraph-io/ristretto/policy.go b/vendor/github.com/outcaste-io/ristretto/policy.go similarity index 67% rename from vendor/github.com/dgraph-io/ristretto/policy.go rename to vendor/github.com/outcaste-io/ristretto/policy.go index bf23f91fd..58a65f993 100644 --- a/vendor/github.com/dgraph-io/ristretto/policy.go +++ b/vendor/github.com/outcaste-io/ristretto/policy.go @@ -21,7 +21,7 @@ import ( "sync" "sync/atomic" - "github.com/dgraph-io/ristretto/z" + "github.com/outcaste-io/ristretto/z" ) const ( @@ -30,56 +30,21 @@ const ( lfuSample = 5 ) -// policy is the interface encapsulating eviction/admission behavior. -// -// TODO: remove this interface and just rename defaultPolicy to policy, as we -// are probably only going to use/implement/maintain one policy. -type policy interface { - ringConsumer - // Add attempts to Add the key-cost pair to the Policy. It returns a slice - // of evicted keys and a bool denoting whether or not the key-cost pair - // was added. If it returns true, the key should be stored in cache. - Add(uint64, int64) ([]*Item, bool) - // Has returns true if the key exists in the Policy. - Has(uint64) bool - // Del deletes the key from the Policy. - Del(uint64) - // Cap returns the available capacity. - Cap() int64 - // Close stops all goroutines and closes all channels. - Close() - // Update updates the cost value for the key. - Update(uint64, int64) - // Cost returns the cost value of a key or -1 if missing. - Cost(uint64) int64 - // Optionally, set stats object to track how policy is performing. - CollectMetrics(*Metrics) - // Clear zeroes out all counters and clears hashmaps. - Clear() - // MaxCost returns the current max cost of the cache policy. - MaxCost() int64 - // UpdateMaxCost updates the max cost of the cache policy. - UpdateMaxCost(int64) -} - -func newPolicy(numCounters, maxCost int64) policy { - return newDefaultPolicy(numCounters, maxCost) -} - -type defaultPolicy struct { +// lfuPolicy encapsulates eviction/admission behavior. +type lfuPolicy struct { sync.Mutex admit *tinyLFU - evict *sampledLFU + costs *keyCosts itemsCh chan []uint64 stop chan struct{} isClosed bool metrics *Metrics } -func newDefaultPolicy(numCounters, maxCost int64) *defaultPolicy { - p := &defaultPolicy{ +func newPolicy(numCounters, maxCost int64) *lfuPolicy { + p := &lfuPolicy{ admit: newTinyLFU(numCounters), - evict: newSampledLFU(maxCost), + costs: newSampledLFU(maxCost), itemsCh: make(chan []uint64, 3), stop: make(chan struct{}), } @@ -87,9 +52,9 @@ func newDefaultPolicy(numCounters, maxCost int64) *defaultPolicy { return p } -func (p *defaultPolicy) CollectMetrics(metrics *Metrics) { +func (p *lfuPolicy) CollectMetrics(metrics *Metrics) { p.metrics = metrics - p.evict.metrics = metrics + p.costs.metrics = metrics } type policyPair struct { @@ -97,7 +62,7 @@ type policyPair struct { cost int64 } -func (p *defaultPolicy) processItems() { +func (p *lfuPolicy) processItems() { for { select { case items := <-p.itemsCh: @@ -110,7 +75,7 @@ func (p *defaultPolicy) processItems() { } } -func (p *defaultPolicy) Push(keys []uint64) bool { +func (p *lfuPolicy) Push(keys []uint64) bool { if p.isClosed { return false } @@ -132,28 +97,28 @@ func (p *defaultPolicy) Push(keys []uint64) bool { // Add decides whether the item with the given key and cost should be accepted by // the policy. It returns the list of victims that have been evicted and a boolean // indicating whether the incoming item should be accepted. -func (p *defaultPolicy) Add(key uint64, cost int64) ([]*Item, bool) { +func (p *lfuPolicy) Add(key uint64, cost int64) ([]*Item, bool) { p.Lock() defer p.Unlock() // Cannot add an item bigger than entire cache. - if cost > p.evict.getMaxCost() { + if cost > p.costs.getMaxCost() { return nil, false } // No need to go any further if the item is already in the cache. - if has := p.evict.updateIfHas(key, cost); has { + if has := p.costs.updateIfHas(key, cost); has { // An update does not count as an addition, so return false. return nil, false } // If the execution reaches this point, the key doesn't exist in the cache. // Calculate the remaining room in the cache (usually bytes). - room := p.evict.roomLeft(cost) + room := p.costs.roomLeft(cost) if room >= 0 { // There's enough room in the cache to store the new item without // overflowing. Do that now and stop here. - p.evict.add(key, cost) + p.costs.add(key, cost) p.metrics.add(costAdd, key, uint64(cost)) return nil, true } @@ -170,9 +135,9 @@ func (p *defaultPolicy) Add(key uint64, cost int64) ([]*Item, bool) { // Delete victims until there's enough space or a minKey is found that has // more hits than incoming item. - for ; room < 0; room = p.evict.roomLeft(cost) { + for ; room < 0; room = p.costs.roomLeft(cost) { // Fill up empty slots in sample. - sample = p.evict.fillSample(sample) + sample = p.costs.fillSample(sample) // Find minimally used item in sample. minKey, minHits, minId, minCost := uint64(0), int64(math.MaxInt64), 0, int64(0) @@ -190,7 +155,7 @@ func (p *defaultPolicy) Add(key uint64, cost int64) ([]*Item, bool) { } // Delete the victim from metadata. - p.evict.del(minKey) + p.costs.del(minKey) // Delete the victim from sample. sample[minId] = sample[len(sample)-1] @@ -203,40 +168,40 @@ func (p *defaultPolicy) Add(key uint64, cost int64) ([]*Item, bool) { }) } - p.evict.add(key, cost) + p.costs.add(key, cost) p.metrics.add(costAdd, key, uint64(cost)) return victims, true } -func (p *defaultPolicy) Has(key uint64) bool { +func (p *lfuPolicy) Has(key uint64) bool { p.Lock() - _, exists := p.evict.keyCosts[key] + _, exists := p.costs.keyCosts[key] p.Unlock() return exists } -func (p *defaultPolicy) Del(key uint64) { +func (p *lfuPolicy) Del(key uint64) { p.Lock() - p.evict.del(key) + p.costs.del(key) p.Unlock() } -func (p *defaultPolicy) Cap() int64 { +func (p *lfuPolicy) Cap() int64 { p.Lock() - capacity := int64(p.evict.getMaxCost() - p.evict.used) + capacity := int64(p.costs.getMaxCost() - p.costs.used) p.Unlock() return capacity } -func (p *defaultPolicy) Update(key uint64, cost int64) { +func (p *lfuPolicy) Update(key uint64, cost int64) { p.Lock() - p.evict.updateIfHas(key, cost) + p.costs.updateIfHas(key, cost) p.Unlock() } -func (p *defaultPolicy) Cost(key uint64) int64 { +func (p *lfuPolicy) Cost(key uint64) int64 { p.Lock() - if cost, found := p.evict.keyCosts[key]; found { + if cost, found := p.costs.keyCosts[key]; found { p.Unlock() return cost } @@ -244,14 +209,14 @@ func (p *defaultPolicy) Cost(key uint64) int64 { return -1 } -func (p *defaultPolicy) Clear() { +func (p *lfuPolicy) Clear() { p.Lock() p.admit.clear() - p.evict.clear() + p.costs.clear() p.Unlock() } -func (p *defaultPolicy) Close() { +func (p *lfuPolicy) Close() { if p.isClosed { return } @@ -263,22 +228,22 @@ func (p *defaultPolicy) Close() { p.isClosed = true } -func (p *defaultPolicy) MaxCost() int64 { - if p == nil || p.evict == nil { +func (p *lfuPolicy) MaxCost() int64 { + if p == nil || p.costs == nil { return 0 } - return p.evict.getMaxCost() + return p.costs.getMaxCost() } -func (p *defaultPolicy) UpdateMaxCost(maxCost int64) { - if p == nil || p.evict == nil { +func (p *lfuPolicy) UpdateMaxCost(maxCost int64) { + if p == nil || p.costs == nil { return } - p.evict.updateMaxCost(maxCost) + p.costs.updateMaxCost(maxCost) } -// sampledLFU is an eviction helper storing key-cost pairs. -type sampledLFU struct { +// keyCosts stores key-cost pairs. +type keyCosts struct { // NOTE: align maxCost to 64-bit boundary for use with atomic. // As per https://golang.org/pkg/sync/atomic/: "On ARM, x86-32, // and 32-bit MIPS, it is the caller’s responsibility to arrange @@ -291,26 +256,26 @@ type sampledLFU struct { keyCosts map[uint64]int64 } -func newSampledLFU(maxCost int64) *sampledLFU { - return &sampledLFU{ +func newSampledLFU(maxCost int64) *keyCosts { + return &keyCosts{ keyCosts: make(map[uint64]int64), maxCost: maxCost, } } -func (p *sampledLFU) getMaxCost() int64 { +func (p *keyCosts) getMaxCost() int64 { return atomic.LoadInt64(&p.maxCost) } -func (p *sampledLFU) updateMaxCost(maxCost int64) { +func (p *keyCosts) updateMaxCost(maxCost int64) { atomic.StoreInt64(&p.maxCost, maxCost) } -func (p *sampledLFU) roomLeft(cost int64) int64 { +func (p *keyCosts) roomLeft(cost int64) int64 { return p.getMaxCost() - (p.used + cost) } -func (p *sampledLFU) fillSample(in []*policyPair) []*policyPair { +func (p *keyCosts) fillSample(in []*policyPair) []*policyPair { if len(in) >= lfuSample { return in } @@ -323,7 +288,7 @@ func (p *sampledLFU) fillSample(in []*policyPair) []*policyPair { return in } -func (p *sampledLFU) del(key uint64) { +func (p *keyCosts) del(key uint64) { cost, ok := p.keyCosts[key] if !ok { return @@ -334,12 +299,12 @@ func (p *sampledLFU) del(key uint64) { p.metrics.add(keyEvict, key, 1) } -func (p *sampledLFU) add(key uint64, cost int64) { +func (p *keyCosts) add(key uint64, cost int64) { p.keyCosts[key] = cost p.used += cost } -func (p *sampledLFU) updateIfHas(key uint64, cost int64) bool { +func (p *keyCosts) updateIfHas(key uint64, cost int64) bool { if prev, found := p.keyCosts[key]; found { // Update the cost of an existing key, but don't worry about evicting. // Evictions will be handled the next time a new item is added. @@ -358,7 +323,7 @@ func (p *sampledLFU) updateIfHas(key uint64, cost int64) bool { return false } -func (p *sampledLFU) clear() { +func (p *keyCosts) clear() { p.used = 0 p.keyCosts = make(map[uint64]int64) } diff --git a/vendor/github.com/dgraph-io/ristretto/ring.go b/vendor/github.com/outcaste-io/ristretto/ring.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/ring.go rename to vendor/github.com/outcaste-io/ristretto/ring.go diff --git a/vendor/github.com/dgraph-io/ristretto/sketch.go b/vendor/github.com/outcaste-io/ristretto/sketch.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/sketch.go rename to vendor/github.com/outcaste-io/ristretto/sketch.go diff --git a/vendor/github.com/dgraph-io/ristretto/store.go b/vendor/github.com/outcaste-io/ristretto/store.go similarity index 73% rename from vendor/github.com/dgraph-io/ristretto/store.go rename to vendor/github.com/outcaste-io/ristretto/store.go index e42a98b78..5d5395c8d 100644 --- a/vendor/github.com/dgraph-io/ristretto/store.go +++ b/vendor/github.com/outcaste-io/ristretto/store.go @@ -29,51 +29,28 @@ type storeItem struct { expiration time.Time } -// store is the interface fulfilled by all hash map implementations in this -// file. Some hash map implementations are better suited for certain data -// distributions than others, so this allows us to abstract that out for use -// in Ristretto. -// -// Every store is safe for concurrent usage. -type store interface { - // Get returns the value associated with the key parameter. - Get(uint64, uint64) (interface{}, bool) - // Expiration returns the expiration time for this key. - Expiration(uint64) time.Time - // Set adds the key-value pair to the Map or updates the value if it's - // already present. The key-value pair is passed as a pointer to an - // item object. - Set(*Item) - // Del deletes the key-value pair from the Map. - Del(uint64, uint64) (uint64, interface{}) - // Update attempts to update the key with a new value and returns true if - // successful. - Update(*Item) (interface{}, bool) - // Cleanup removes items that have an expired TTL. - Cleanup(policy policy, onEvict itemCallback) - // Clear clears all contents of the store. - Clear(onEvict itemCallback) -} - -// newStore returns the default store implementation. -func newStore() store { - return newShardedMap() -} - const numShards uint64 = 256 +type updateFn func(prev, cur interface{}) bool type shardedMap struct { - shards []*lockedMap - expiryMap *expirationMap + shards []*lockedMap + expiryMap *expirationMap + shouldUpdate func(prev, cur interface{}) bool } -func newShardedMap() *shardedMap { +// newShardedMap is safe for concurrent usage. +func newShardedMap(fn updateFn) *shardedMap { sm := &shardedMap{ shards: make([]*lockedMap, int(numShards)), expiryMap: newExpirationMap(), } + if fn == nil { + fn = func(prev, cur interface{}) bool { + return true + } + } for i := range sm.shards { - sm.shards[i] = newLockedMap(sm.expiryMap) + sm.shards[i] = newLockedMap(fn, sm.expiryMap) } return sm } @@ -103,7 +80,7 @@ func (sm *shardedMap) Update(newItem *Item) (interface{}, bool) { return sm.shards[newItem.Key%numShards].Update(newItem) } -func (sm *shardedMap) Cleanup(policy policy, onEvict itemCallback) { +func (sm *shardedMap) Cleanup(policy *lfuPolicy, onEvict itemCallback) { sm.expiryMap.cleanup(sm, policy, onEvict) } @@ -115,14 +92,16 @@ func (sm *shardedMap) Clear(onEvict itemCallback) { type lockedMap struct { sync.RWMutex - data map[uint64]storeItem - em *expirationMap + data map[uint64]storeItem + em *expirationMap + shouldUpdate updateFn } -func newLockedMap(em *expirationMap) *lockedMap { +func newLockedMap(fn updateFn, em *expirationMap) *lockedMap { return &lockedMap{ - data: make(map[uint64]storeItem), - em: em, + data: make(map[uint64]storeItem), + em: em, + shouldUpdate: fn, } } @@ -166,6 +145,9 @@ func (m *lockedMap) Set(i *Item) { if i.Conflict != 0 && (i.Conflict != item.conflict) { return } + if !m.shouldUpdate(item.value, i.Value) { + return + } m.em.update(i.Key, i.Conflict, item.expiration, i.Expiration) } else { // The value is not in the map already. There's no need to return anything. @@ -204,15 +186,18 @@ func (m *lockedMap) Del(key, conflict uint64) (uint64, interface{}) { func (m *lockedMap) Update(newItem *Item) (interface{}, bool) { m.Lock() + defer m.Unlock() + item, ok := m.data[newItem.Key] if !ok { - m.Unlock() return nil, false } if newItem.Conflict != 0 && (newItem.Conflict != item.conflict) { - m.Unlock() return nil, false } + if !m.shouldUpdate(item.value, newItem.Value) { + return item.value, false + } m.em.update(newItem.Key, newItem.Conflict, item.expiration, newItem.Expiration) m.data[newItem.Key] = storeItem{ @@ -221,8 +206,6 @@ func (m *lockedMap) Update(newItem *Item) (interface{}, bool) { value: newItem.Value, expiration: newItem.Expiration, } - - m.Unlock() return item.value, true } diff --git a/vendor/github.com/dgraph-io/ristretto/test.sh b/vendor/github.com/outcaste-io/ristretto/test.sh similarity index 77% rename from vendor/github.com/dgraph-io/ristretto/test.sh rename to vendor/github.com/outcaste-io/ristretto/test.sh index d53b32d42..99fdc99a3 100644 --- a/vendor/github.com/dgraph-io/ristretto/test.sh +++ b/vendor/github.com/outcaste-io/ristretto/test.sh @@ -9,10 +9,10 @@ if [ -z "${TEAMCITY_VERSION}" ]; then # running locally, so start test in a container # TEAMCITY_VERSION=local will avoid recursive calls, when it would be running in container docker run --rm --name ristretto-test -ti \ - -v `pwd`:/go/src/github.com/dgraph-io/ristretto \ - --workdir /go/src/github.com/dgraph-io/ristretto \ + -v `pwd`:/go/src/github.com/outcaste-io/ristretto \ + --workdir /go/src/github.com/outcaste-io/ristretto \ --env TEAMCITY_VERSION=local \ - golang:1.13 \ + golang:1.16 \ sh test.sh else # running in teamcity, since teamcity itself run this in container, let's simply run this diff --git a/vendor/github.com/dgraph-io/ristretto/ttl.go b/vendor/github.com/outcaste-io/ristretto/ttl.go similarity index 93% rename from vendor/github.com/dgraph-io/ristretto/ttl.go rename to vendor/github.com/outcaste-io/ristretto/ttl.go index 337976ad4..6e4bf38bf 100644 --- a/vendor/github.com/dgraph-io/ristretto/ttl.go +++ b/vendor/github.com/outcaste-io/ristretto/ttl.go @@ -77,17 +77,25 @@ func (m *expirationMap) update(key, conflict uint64, oldExpTime, newExpTime time if m == nil { return } + if oldExpTime.IsZero() && newExpTime.IsZero() { + return + } m.Lock() defer m.Unlock() oldBucketNum := storageBucket(oldExpTime) + newBucketNum := storageBucket(newExpTime) + if oldBucketNum == newBucketNum { + // No change. + return + } + oldBucket, ok := m.buckets[oldBucketNum] if ok { delete(oldBucket, key) } - newBucketNum := storageBucket(newExpTime) newBucket, ok := m.buckets[newBucketNum] if !ok { newBucket = make(bucket) @@ -114,7 +122,7 @@ func (m *expirationMap) del(key uint64, expiration time.Time) { // cleanup removes all the items in the bucket that was just completed. It deletes // those items from the store, and calls the onEvict function on those items. // This function is meant to be called periodically. -func (m *expirationMap) cleanup(store store, policy policy, onEvict itemCallback) { +func (m *expirationMap) cleanup(store *shardedMap, policy *lfuPolicy, onEvict itemCallback) { if m == nil { return } diff --git a/vendor/github.com/dgraph-io/ristretto/z/LICENSE b/vendor/github.com/outcaste-io/ristretto/z/LICENSE similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/LICENSE rename to vendor/github.com/outcaste-io/ristretto/z/LICENSE diff --git a/vendor/github.com/dgraph-io/ristretto/z/README.md b/vendor/github.com/outcaste-io/ristretto/z/README.md similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/README.md rename to vendor/github.com/outcaste-io/ristretto/z/README.md diff --git a/vendor/github.com/dgraph-io/ristretto/z/allocator.go b/vendor/github.com/outcaste-io/ristretto/z/allocator.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/allocator.go rename to vendor/github.com/outcaste-io/ristretto/z/allocator.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/bbloom.go b/vendor/github.com/outcaste-io/ristretto/z/bbloom.go similarity index 97% rename from vendor/github.com/dgraph-io/ristretto/z/bbloom.go rename to vendor/github.com/outcaste-io/ristretto/z/bbloom.go index 37135b012..4d657e4e1 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/bbloom.go +++ b/vendor/github.com/outcaste-io/ristretto/z/bbloom.go @@ -25,8 +25,6 @@ import ( "encoding/json" "math" "unsafe" - - "github.com/golang/glog" ) // helper @@ -60,7 +58,7 @@ func NewBloomFilter(params ...float64) (bloomfilter *Bloom) { entries, locs = uint64(params[0]), uint64(params[1]) } } else { - glog.Fatal("usage: New(float64(number_of_entries), float64(number_of_hashlocations))" + + fatal("usage: New(float64(number_of_entries), float64(number_of_hashlocations))" + " i.e. New(float64(1000), float64(3)) or New(float64(number_of_entries)," + " float64(number_of_hashlocations)) i.e. New(float64(1000), float64(0.03))") } @@ -205,7 +203,7 @@ func (bl Bloom) JSONMarshal() []byte { } data, err := json.Marshal(bloomImEx) if err != nil { - glog.Fatal("json.Marshal failed: ", err) + fatal("json.Marshal failed: ", err) } return data } diff --git a/vendor/github.com/dgraph-io/ristretto/z/btree.go b/vendor/github.com/outcaste-io/ristretto/z/btree.go similarity index 99% rename from vendor/github.com/dgraph-io/ristretto/z/btree.go rename to vendor/github.com/outcaste-io/ristretto/z/btree.go index 12b735bb0..0b28ae5b8 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/btree.go +++ b/vendor/github.com/outcaste-io/ristretto/z/btree.go @@ -24,7 +24,7 @@ import ( "strings" "unsafe" - "github.com/dgraph-io/ristretto/z/simd" + "github.com/outcaste-io/ristretto/z/simd" ) var ( diff --git a/vendor/github.com/dgraph-io/ristretto/z/buffer.go b/vendor/github.com/outcaste-io/ristretto/z/buffer.go similarity index 99% rename from vendor/github.com/dgraph-io/ristretto/z/buffer.go rename to vendor/github.com/outcaste-io/ristretto/z/buffer.go index 5a22de8c7..8f760c7d3 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/buffer.go +++ b/vendor/github.com/outcaste-io/ristretto/z/buffer.go @@ -24,7 +24,6 @@ import ( "sort" "sync/atomic" - "github.com/golang/glog" "github.com/pkg/errors" ) @@ -346,12 +345,12 @@ func (s *sortHelper) sortSmall(start, end int) { func assert(b bool) { if !b { - glog.Fatalf("%+v", errors.Errorf("Assertion failure")) + fatalf("%+v", errors.Errorf("Assertion failure")) } } func check(err error) { if err != nil { - glog.Fatalf("%+v", err) + fatalf("%+v", err) } } func check2(_ interface{}, err error) { diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc.go b/vendor/github.com/outcaste-io/ristretto/z/calloc.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/calloc.go rename to vendor/github.com/outcaste-io/ristretto/z/calloc.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go b/vendor/github.com/outcaste-io/ristretto/z/calloc_32bit.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go rename to vendor/github.com/outcaste-io/ristretto/z/calloc_32bit.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go b/vendor/github.com/outcaste-io/ristretto/z/calloc_64bit.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go rename to vendor/github.com/outcaste-io/ristretto/z/calloc_64bit.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go b/vendor/github.com/outcaste-io/ristretto/z/calloc_jemalloc.go similarity index 94% rename from vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go rename to vendor/github.com/outcaste-io/ristretto/z/calloc_jemalloc.go index 904d73ac5..88a5acedb 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go +++ b/vendor/github.com/outcaste-io/ristretto/z/calloc_jemalloc.go @@ -2,14 +2,15 @@ // of this source code is governed by a BSD-style license that can be found in // the LICENSE file. +//go:build jemalloc // +build jemalloc package z /* -#cgo LDFLAGS: /usr/local/lib/libjemalloc.a -L/usr/local/lib -Wl,-rpath,/usr/local/lib -ljemalloc -lm -lstdc++ -pthread -ldl +#cgo LDFLAGS: /usr/local/lib/libjemalloc_outcaste.a -L/usr/local/lib -Wl,-rpath,/usr/local/lib -ljemalloc_outcaste -lm -lstdc++ -pthread -ldl #include -#include +#include */ import "C" import ( @@ -161,7 +162,7 @@ func fetchStat(s string) uint64 { unsafe.Pointer(&out), // Variable to store the output. (*C.size_t)(unsafe.Pointer(&sz)), // Size of the output variable. nil, // Input variable used to set a value. - 0) // Size of the input variable. + 0) // Size of the input variable. return out } diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go b/vendor/github.com/outcaste-io/ristretto/z/calloc_nojemalloc.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go rename to vendor/github.com/outcaste-io/ristretto/z/calloc_nojemalloc.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/file.go b/vendor/github.com/outcaste-io/ristretto/z/file.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/file.go rename to vendor/github.com/outcaste-io/ristretto/z/file.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/file_default.go b/vendor/github.com/outcaste-io/ristretto/z/file_default.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/file_default.go rename to vendor/github.com/outcaste-io/ristretto/z/file_default.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/file_linux.go b/vendor/github.com/outcaste-io/ristretto/z/file_linux.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/file_linux.go rename to vendor/github.com/outcaste-io/ristretto/z/file_linux.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/flags.go b/vendor/github.com/outcaste-io/ristretto/z/flags.go similarity index 92% rename from vendor/github.com/dgraph-io/ristretto/z/flags.go rename to vendor/github.com/outcaste-io/ristretto/z/flags.go index a55c474ab..4aca58f30 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/flags.go +++ b/vendor/github.com/outcaste-io/ristretto/z/flags.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/golang/glog" "github.com/pkg/errors" ) @@ -109,7 +108,7 @@ type SuperFlag struct { func NewSuperFlag(flag string) *SuperFlag { sf, err := newSuperFlagImpl(flag) if err != nil { - glog.Fatal(err) + fatal(err) } return sf } @@ -134,14 +133,28 @@ func (sf *SuperFlag) String() string { } func (sf *SuperFlag) MergeAndCheckDefault(flag string) *SuperFlag { - sf, err := sf.mergeAndCheckDefaultImpl(flag) + sf, err := sf.MergeWithDefault(flag) if err != nil { - glog.Fatal(err) + fatal(err) } return sf } -func (sf *SuperFlag) mergeAndCheckDefaultImpl(flag string) (*SuperFlag, error) { +func (sf *SuperFlag) Merge(flag string) *SuperFlag { + src, err := parseFlag(flag) + if err != nil { + fatal(err) + } + for k, v := range src { + if _, ok := sf.m[k]; !ok { + fatal("Unable to find the flag in SuperFlag") + } + sf.m[k] = v + } + return sf +} + +func (sf *SuperFlag) MergeWithDefault(flag string) (*SuperFlag, error) { if sf == nil { m, err := parseFlag(flag) if err != nil { @@ -207,7 +220,7 @@ func (sf *SuperFlag) GetBool(opt string) bool { err = errors.Wrapf(err, "Unable to parse %s as bool for key: %s. Options: %s\n", val, opt, sf) - glog.Fatalf("%+v", err) + fatalf("%+v", err) } return b } @@ -222,7 +235,7 @@ func (sf *SuperFlag) GetFloat64(opt string) float64 { err = errors.Wrapf(err, "Unable to parse %s as float64 for key: %s. Options: %s\n", val, opt, sf) - glog.Fatalf("%+v", err) + fatalf("%+v", err) } return f } @@ -237,7 +250,7 @@ func (sf *SuperFlag) GetInt64(opt string) int64 { err = errors.Wrapf(err, "Unable to parse %s as int64 for key: %s. Options: %s\n", val, opt, sf) - glog.Fatalf("%+v", err) + fatalf("%+v", err) } return i } @@ -252,7 +265,7 @@ func (sf *SuperFlag) GetUint64(opt string) uint64 { err = errors.Wrapf(err, "Unable to parse %s as uint64 for key: %s. Options: %s\n", val, opt, sf) - glog.Fatalf("%+v", err) + fatalf("%+v", err) } return u } @@ -267,7 +280,7 @@ func (sf *SuperFlag) GetUint32(opt string) uint32 { err = errors.Wrapf(err, "Unable to parse %s as uint32 for key: %s. Options: %s\n", val, opt, sf) - glog.Fatalf("%+v", err) + fatalf("%+v", err) } return uint32(u) } @@ -283,7 +296,7 @@ func (sf *SuperFlag) GetPath(opt string) string { p := sf.GetString(opt) path, err := expandPath(p) if err != nil { - glog.Fatalf("Failed to get path: %+v", err) + fatalf("Failed to get path: %+v", err) } return path } diff --git a/vendor/github.com/dgraph-io/ristretto/z/histogram.go b/vendor/github.com/outcaste-io/ristretto/z/histogram.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/histogram.go rename to vendor/github.com/outcaste-io/ristretto/z/histogram.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap.go b/vendor/github.com/outcaste-io/ristretto/z/mmap.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/mmap.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_darwin.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_darwin.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_linux.go similarity index 93% rename from vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_linux.go index 9cc3497a1..8843e4243 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go +++ b/vendor/github.com/outcaste-io/ristretto/z/mmap_linux.go @@ -17,7 +17,6 @@ package z import ( - "fmt" "os" "reflect" "unsafe" @@ -41,7 +40,7 @@ func mremap(data []byte, size int) ([]byte, error) { const MREMAP_MAYMOVE = 0x1 header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - mmapAddr, mmapSize, errno := unix.Syscall6( + mmapAddr, _, errno := unix.Syscall6( unix.SYS_MREMAP, header.Data, uintptr(header.Len), @@ -53,9 +52,6 @@ func mremap(data []byte, size int) ([]byte, error) { if errno != 0 { return nil, errno } - if mmapSize != uintptr(size) { - return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize) - } header.Data = mmapAddr header.Cap = size diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_plan9.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_plan9.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_unix.go similarity index 93% rename from vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_unix.go index e8b2699cf..629449f9d 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go +++ b/vendor/github.com/outcaste-io/ristretto/z/mmap_unix.go @@ -1,4 +1,5 @@ -// +build !windows,!darwin,!plan9,!linux +//go:build !windows && !darwin && !plan9 && !linux && !wasip1 +// +build !windows,!darwin,!plan9,!linux,!wasip1 /* * Copyright 2019 Dgraph Labs, Inc. and Contributors diff --git a/vendor/google.golang.org/grpc/internal/channelz/util_linux.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_wasip1.go similarity index 61% rename from vendor/google.golang.org/grpc/internal/channelz/util_linux.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_wasip1.go index 8d194e44e..94f714845 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/util_linux.go +++ b/vendor/github.com/outcaste-io/ristretto/z/mmap_wasip1.go @@ -1,6 +1,7 @@ +//go:build wasip1 + /* - * - * Copyright 2018 gRPC authors. + * Copyright 2023 Dgraph Labs, Inc. and Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +14,27 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ -package channelz +package z import ( + "os" "syscall" ) -// GetSocketOption gets the socket option info of the conn. -func GetSocketOption(socket interface{}) *SocketOptionData { - c, ok := socket.(syscall.Conn) - if !ok { - return nil - } - data := &SocketOptionData{} - if rawConn, err := c.SyscallConn(); err == nil { - rawConn.Control(data.Getsockopt) - return data - } - return nil +func mmap(fd *os.File, writeable bool, size int64) ([]byte, error) { + return nil, syscall.ENOSYS +} + +func munmap(b []byte) error { + return syscall.ENOSYS +} + +func madvise(b []byte, readahead bool) error { + return syscall.ENOSYS +} + +func msync(b []byte) error { + return syscall.ENOSYS } diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go b/vendor/github.com/outcaste-io/ristretto/z/mmap_windows.go similarity index 96% rename from vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go rename to vendor/github.com/outcaste-io/ristretto/z/mmap_windows.go index 171176e9f..0ea6e9448 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go +++ b/vendor/github.com/outcaste-io/ristretto/z/mmap_windows.go @@ -91,6 +91,5 @@ func madvise(b []byte, readahead bool) error { } func msync(b []byte) error { - // TODO: Figure out how to do msync on Windows. - return nil + return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))) } diff --git a/vendor/github.com/dgraph-io/ristretto/z/rtutil.go b/vendor/github.com/outcaste-io/ristretto/z/rtutil.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/rtutil.go rename to vendor/github.com/outcaste-io/ristretto/z/rtutil.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/rtutil.s b/vendor/github.com/outcaste-io/ristretto/z/rtutil.s similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/rtutil.s rename to vendor/github.com/outcaste-io/ristretto/z/rtutil.s diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go b/vendor/github.com/outcaste-io/ristretto/z/simd/baseline.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go rename to vendor/github.com/outcaste-io/ristretto/z/simd/baseline.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/search.go b/vendor/github.com/outcaste-io/ristretto/z/simd/search.go similarity index 96% rename from vendor/github.com/dgraph-io/ristretto/z/simd/search.go rename to vendor/github.com/outcaste-io/ristretto/z/simd/search.go index 0d001ee0c..b1e639225 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/simd/search.go +++ b/vendor/github.com/outcaste-io/ristretto/z/simd/search.go @@ -20,7 +20,7 @@ package simd // Search uses the Clever search to find the correct key. func Search(xs []uint64, k uint64) int16 { - if len(xs) < 8 { + if len(xs) < 8 || (len(xs) % 8 != 0) { return Naive(xs, k) } var twos, pk [4]uint64 diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s b/vendor/github.com/outcaste-io/ristretto/z/simd/search_amd64.s similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s rename to vendor/github.com/outcaste-io/ristretto/z/simd/search_amd64.s diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go b/vendor/github.com/outcaste-io/ristretto/z/simd/stub_search_amd64.go similarity index 100% rename from vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go rename to vendor/github.com/outcaste-io/ristretto/z/simd/stub_search_amd64.go diff --git a/vendor/github.com/dgraph-io/ristretto/z/z.go b/vendor/github.com/outcaste-io/ristretto/z/z.go similarity index 94% rename from vendor/github.com/dgraph-io/ristretto/z/z.go rename to vendor/github.com/outcaste-io/ristretto/z/z.go index 97455586a..45c15cdcd 100644 --- a/vendor/github.com/dgraph-io/ristretto/z/z.go +++ b/vendor/github.com/outcaste-io/ristretto/z/z.go @@ -18,6 +18,8 @@ package z import ( "context" + "fmt" + "os" "sync" "github.com/cespare/xxhash/v2" @@ -149,3 +151,13 @@ func ZeroOut(dst []byte, start, end int) { // b[i] = 0x0 // } } + +func fatal(args ...interface{}) { + defer os.Exit(1) + panic(fmt.Sprint(args...)) +} + +func fatalf(format string, args ...interface{}) { + defer os.Exit(1) + panic(fmt.Sprintf(format, args...)) +} diff --git a/vendor/github.com/philhofer/fwd/README.md b/vendor/github.com/philhofer/fwd/README.md index 38349af34..62bd5c6d0 100644 --- a/vendor/github.com/philhofer/fwd/README.md +++ b/vendor/github.com/philhofer/fwd/README.md @@ -1,17 +1,25 @@ # fwd - import "github.com/philhofer/fwd" -The `fwd` package provides a buffered reader +[![Go Reference](https://pkg.go.dev/badge/github.com/philhofer/fwd.svg)](https://pkg.go.dev/github.com/philhofer/fwd) + + +`import "github.com/philhofer/fwd"` + +* [Overview](#pkg-overview) +* [Index](#pkg-index) + +## Overview +Package fwd provides a buffered reader and writer. Each has methods that help improve the encoding/decoding performance of some binary protocols. -The `fwd.Writer` and `fwd.Reader` type provide similar +The `Writer` and `Reader` type provide similar functionality to their counterparts in `bufio`, plus a few extra utility methods that simplify read-ahead and write-ahead. I wrote this package to improve serialization -performance for http://github.com/tinylib/msgp, +performance for [github.com/tinylib/msgp](https://github.com/tinylib/msgp), where it provided about a 2x speedup over `bufio` for certain workloads. However, care must be taken to understand the semantics of the extra methods provided by this package, as they allow @@ -39,7 +47,37 @@ to write directly to the end of the buffer. -## Constants +## Index +* [Constants](#pkg-constants) +* [type Reader](#Reader) + * [func NewReader(r io.Reader) *Reader](#NewReader) + * [func NewReaderBuf(r io.Reader, buf []byte) *Reader](#NewReaderBuf) + * [func NewReaderSize(r io.Reader, n int) *Reader](#NewReaderSize) + * [func (r *Reader) BufferSize() int](#Reader.BufferSize) + * [func (r *Reader) Buffered() int](#Reader.Buffered) + * [func (r *Reader) Next(n int) ([]byte, error)](#Reader.Next) + * [func (r *Reader) Peek(n int) ([]byte, error)](#Reader.Peek) + * [func (r *Reader) Read(b []byte) (int, error)](#Reader.Read) + * [func (r *Reader) ReadByte() (byte, error)](#Reader.ReadByte) + * [func (r *Reader) ReadFull(b []byte) (int, error)](#Reader.ReadFull) + * [func (r *Reader) Reset(rd io.Reader)](#Reader.Reset) + * [func (r *Reader) Skip(n int) (int, error)](#Reader.Skip) + * [func (r *Reader) WriteTo(w io.Writer) (int64, error)](#Reader.WriteTo) +* [type Writer](#Writer) + * [func NewWriter(w io.Writer) *Writer](#NewWriter) + * [func NewWriterBuf(w io.Writer, buf []byte) *Writer](#NewWriterBuf) + * [func NewWriterSize(w io.Writer, n int) *Writer](#NewWriterSize) + * [func (w *Writer) BufferSize() int](#Writer.BufferSize) + * [func (w *Writer) Buffered() int](#Writer.Buffered) + * [func (w *Writer) Flush() error](#Writer.Flush) + * [func (w *Writer) Next(n int) ([]byte, error)](#Writer.Next) + * [func (w *Writer) ReadFrom(r io.Reader) (int64, error)](#Writer.ReadFrom) + * [func (w *Writer) Write(p []byte) (int, error)](#Writer.Write) + * [func (w *Writer) WriteByte(b byte) error](#Writer.WriteByte) + * [func (w *Writer) WriteString(s string) (int, error)](#Writer.WriteString) + + +## Constants ``` go const ( // DefaultReaderSize is the default size of the read buffer @@ -121,7 +159,7 @@ and the reader position will not be incremented. -### func (\*Reader) Peek +### func (\*Reader) Peek ``` go func (r *Reader) Peek(n int) ([]byte, error) ``` @@ -134,23 +172,23 @@ io.ErrUnexpectedEOF. -### func (\*Reader) Read +### func (\*Reader) Read ``` go func (r *Reader) Read(b []byte) (int, error) ``` -Read implements `io.Reader` +Read implements `io.Reader`. -### func (\*Reader) ReadByte +### func (\*Reader) ReadByte ``` go func (r *Reader) ReadByte() (byte, error) ``` -ReadByte implements `io.ByteReader` +ReadByte implements `io.ByteReader`. -### func (\*Reader) ReadFull +### func (\*Reader) ReadFull ``` go func (r *Reader) ReadFull(b []byte) (int, error) ``` @@ -161,7 +199,7 @@ EOF is considered an unexpected error. -### func (\*Reader) Reset +### func (\*Reader) Reset ``` go func (r *Reader) Reset(rd io.Reader) ``` @@ -170,7 +208,7 @@ and the read buffer. -### func (\*Reader) Skip +### func (\*Reader) Skip ``` go func (r *Reader) Skip(n int) (int, error) ``` @@ -182,27 +220,30 @@ that method will be used to skip forward. If the reader encounters an EOF before skipping 'n' bytes, it -returns io.ErrUnexpectedEOF. If the -underlying reader implements io.Seeker, then +returns `io.ErrUnexpectedEOF`. If the +underlying reader implements `io.Seeker`, then those rules apply instead. (Many implementations will not return `io.EOF` until the next call -to Read.) +to Read). -### func (\*Reader) WriteTo + +### func (\*Reader) WriteTo ``` go func (r *Reader) WriteTo(w io.Writer) (int64, error) ``` -WriteTo implements `io.WriterTo` +WriteTo implements `io.WriterTo`. + -## type Writer +## type Writer ``` go type Writer struct { // contains filtered or unexported fields } + ``` Writer is a buffered writer @@ -212,9 +253,7 @@ Writer is a buffered writer - - -### func NewWriter +### func NewWriter ``` go func NewWriter(w io.Writer) *Writer ``` @@ -223,18 +262,24 @@ that writes to 'w' and has a buffer that is `DefaultWriterSize` bytes. -### func NewWriterSize +### func NewWriterBuf ``` go -func NewWriterSize(w io.Writer, size int) *Writer +func NewWriterBuf(w io.Writer, buf []byte) *Writer ``` -NewWriterSize returns a new writer -that writes to 'w' and has a buffer -that is 'size' bytes. - +NewWriterBuf returns a new writer +that writes to 'w' and has 'buf' as a buffer. +'buf' is not used when has smaller capacity than 18, +custom buffer is allocated instead. +### func NewWriterSize +``` go +func NewWriterSize(w io.Writer, n int) *Writer +``` +NewWriterSize returns a new writer that +writes to 'w' and has a buffer size 'n'. -### func (\*Writer) BufferSize +### func (\*Writer) BufferSize ``` go func (w *Writer) BufferSize() int ``` @@ -242,7 +287,7 @@ BufferSize returns the maximum size of the buffer. -### func (\*Writer) Buffered +### func (\*Writer) Buffered ``` go func (w *Writer) Buffered() int ``` @@ -251,7 +296,7 @@ in the reader. -### func (\*Writer) Flush +### func (\*Writer) Flush ``` go func (w *Writer) Flush() error ``` @@ -260,7 +305,7 @@ to the underlying writer. -### func (\*Writer) Next +### func (\*Writer) Next ``` go func (w *Writer) Next(n int) ([]byte, error) ``` @@ -273,7 +318,7 @@ the size of the returned buffer. -### func (\*Writer) ReadFrom +### func (\*Writer) ReadFrom ``` go func (w *Writer) ReadFrom(r io.Reader) (int64, error) ``` @@ -281,7 +326,7 @@ ReadFrom implements `io.ReaderFrom` -### func (\*Writer) Write +### func (\*Writer) Write ``` go func (w *Writer) Write(p []byte) (int, error) ``` @@ -289,7 +334,7 @@ Write implements `io.Writer` -### func (\*Writer) WriteByte +### func (\*Writer) WriteByte ``` go func (w *Writer) WriteByte(b byte) error ``` @@ -297,7 +342,7 @@ WriteByte implements `io.ByteWriter` -### func (\*Writer) WriteString +### func (\*Writer) WriteString ``` go func (w *Writer) WriteString(s string) (int, error) ``` @@ -310,6 +355,5 @@ WriteString is analogous to Write, but it takes a string. - - - - -Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) \ No newline at end of file +Generated by [godoc2md](https://github.com/davecheney/godoc2md) diff --git a/vendor/github.com/philhofer/fwd/reader.go b/vendor/github.com/philhofer/fwd/reader.go index 72cc112ba..7c21f8fb4 100644 --- a/vendor/github.com/philhofer/fwd/reader.go +++ b/vendor/github.com/philhofer/fwd/reader.go @@ -1,10 +1,10 @@ -// The `fwd` package provides a buffered reader +// Package fwd provides a buffered reader // and writer. Each has methods that help improve // the encoding/decoding performance of some binary // protocols. // -// The `fwd.Writer` and `fwd.Reader` type provide similar -// functionality to their counterparts in `bufio`, plus +// The [Writer] and [Reader] type provide similar +// functionality to their counterparts in [bufio], plus // a few extra utility methods that simplify read-ahead // and write-ahead. I wrote this package to improve serialization // performance for http://github.com/tinylib/msgp, @@ -14,24 +14,23 @@ // the user to access and manipulate the buffer memory // directly. // -// The extra methods for `fwd.Reader` are `Peek`, `Skip` -// and `Next`. `(*fwd.Reader).Peek`, unlike `(*bufio.Reader).Peek`, +// The extra methods for [Reader] are [Reader.Peek], [Reader.Skip] +// and [Reader.Next]. (*fwd.Reader).Peek, unlike (*bufio.Reader).Peek, // will re-allocate the read buffer in order to accommodate arbitrarily -// large read-ahead. `(*fwd.Reader).Skip` skips the next `n` bytes -// in the stream, and uses the `io.Seeker` interface if the underlying -// stream implements it. `(*fwd.Reader).Next` returns a slice pointing -// to the next `n` bytes in the read buffer (like `Peek`), but also +// large read-ahead. (*fwd.Reader).Skip skips the next 'n' bytes +// in the stream, and uses the [io.Seeker] interface if the underlying +// stream implements it. (*fwd.Reader).Next returns a slice pointing +// to the next 'n' bytes in the read buffer (like Reader.Peek), but also // increments the read position. This allows users to process streams // in arbitrary block sizes without having to manage appropriately-sized // slices. Additionally, obviating the need to copy the data from the // buffer to another location in memory can improve performance dramatically // in CPU-bound applications. // -// `fwd.Writer` only has one extra method, which is `(*fwd.Writer).Next`, which -// returns a slice pointing to the next `n` bytes of the writer, and increments +// [Writer] only has one extra method, which is (*fwd.Writer).Next, which +// returns a slice pointing to the next 'n' bytes of the writer, and increments // the write position by the length of the returned slice. This allows users // to write directly to the end of the buffer. -// package fwd import ( @@ -129,6 +128,8 @@ func (r *Reader) more() { // discard the io.EOF if we read more than 0 bytes. // the next call to Read should return io.EOF again. r.state = nil + } else if r.state != nil { + return } r.data = r.data[:len(r.data)+a] } @@ -211,11 +212,11 @@ func (r *Reader) discard(n int) int { // // If the reader encounters // an EOF before skipping 'n' bytes, it -// returns io.ErrUnexpectedEOF. If the -// underlying reader implements io.Seeker, then +// returns [io.ErrUnexpectedEOF]. If the +// underlying reader implements [io.Seeker], then // those rules apply instead. (Many implementations -// will not return `io.EOF` until the next call -// to Read.) +// will not return [io.EOF] until the next call +// to Read). func (r *Reader) Skip(n int) (int, error) { if n < 0 { return 0, os.ErrInvalid @@ -248,7 +249,6 @@ func (r *Reader) Skip(n int) (int, error) { // length asked for, an error will be returned, // and the reader position will not be incremented. func (r *Reader) Next(n int) ([]byte, error) { - // in case the buffer is too small if cap(r.data) < n { old := r.data[r.n:] @@ -270,7 +270,7 @@ func (r *Reader) Next(n int) ([]byte, error) { return out, nil } -// Read implements `io.Reader` +// Read implements [io.Reader]. func (r *Reader) Read(b []byte) (int, error) { // if we have data in the buffer, just // return that. @@ -325,7 +325,7 @@ func (r *Reader) ReadFull(b []byte) (int, error) { return n, nil } -// ReadByte implements `io.ByteReader` +// ReadByte implements [io.ByteReader]. func (r *Reader) ReadByte() (byte, error) { for r.buffered() < 1 && r.state == nil { r.more() @@ -338,7 +338,7 @@ func (r *Reader) ReadByte() (byte, error) { return b, nil } -// WriteTo implements `io.WriterTo` +// WriteTo implements [io.WriterTo]. func (r *Reader) WriteTo(w io.Writer) (int64, error) { var ( i int64 diff --git a/vendor/github.com/philhofer/fwd/writer_appengine.go b/vendor/github.com/philhofer/fwd/writer_appengine.go index e367f3931..a978e3b6a 100644 --- a/vendor/github.com/philhofer/fwd/writer_appengine.go +++ b/vendor/github.com/philhofer/fwd/writer_appengine.go @@ -1,3 +1,4 @@ +//go:build appengine // +build appengine package fwd diff --git a/vendor/github.com/philhofer/fwd/writer_tinygo.go b/vendor/github.com/philhofer/fwd/writer_tinygo.go new file mode 100644 index 000000000..b060faf7a --- /dev/null +++ b/vendor/github.com/philhofer/fwd/writer_tinygo.go @@ -0,0 +1,19 @@ +//go:build tinygo +// +build tinygo + +package fwd + +import ( + "reflect" + "unsafe" +) + +// unsafe cast string as []byte +func unsafestr(b string) []byte { + l := uintptr(len(b)) + return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Len: l, + Cap: l, + Data: (*reflect.StringHeader)(unsafe.Pointer(&b)).Data, + })) +} diff --git a/vendor/github.com/philhofer/fwd/writer_unsafe.go b/vendor/github.com/philhofer/fwd/writer_unsafe.go index a0bf453b3..e4cb4a830 100644 --- a/vendor/github.com/philhofer/fwd/writer_unsafe.go +++ b/vendor/github.com/philhofer/fwd/writer_unsafe.go @@ -1,4 +1,5 @@ -// +build !appengine +//go:build !appengine && !tinygo +// +build !appengine,!tinygo package fwd @@ -8,11 +9,12 @@ import ( ) // unsafe cast string as []byte -func unsafestr(b string) []byte { - l := len(b) - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Len: l, - Cap: l, - Data: (*reflect.StringHeader)(unsafe.Pointer(&b)).Data, - })) +func unsafestr(s string) []byte { + var b []byte + sHdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) + bHdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bHdr.Data = sHdr.Data + bHdr.Len = sHdr.Len + bHdr.Cap = sHdr.Len + return b } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/collectors.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/collectors.go new file mode 100644 index 000000000..f4c92913a --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/collectors.go @@ -0,0 +1,40 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package collectors provides implementations of prometheus.Collector to +// conveniently collect process and Go-related metrics. +package collectors + +import "github.com/prometheus/client_golang/prometheus" + +// NewBuildInfoCollector returns a collector collecting a single metric +// "go_build_info" with the constant value 1 and three labels "path", "version", +// and "checksum". Their label values contain the main module path, version, and +// checksum, respectively. The labels will only have meaningful values if the +// binary is built with Go module support and from source code retrieved from +// the source repository (rather than the local file system). This is usually +// accomplished by building from outside of GOPATH, specifying the full address +// of the main package, e.g. "GO111MODULE=on go run +// github.com/prometheus/client_golang/examples/random". If built without Go +// module support, all label values will be "unknown". If built with Go module +// support but using the source code from the local file system, the "path" will +// be set appropriately, but "checksum" will be empty and "version" will be +// "(devel)". +// +// This collector uses only the build information for the main module. See +// https://github.com/povilasv/prommod for an example of a collector for the +// module dependencies. +func NewBuildInfoCollector() prometheus.Collector { + //nolint:staticcheck // Ignore SA1019 until v2. + return prometheus.NewBuildInfoCollector() +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/dbstats_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/dbstats_collector.go new file mode 100644 index 000000000..d5a7279fb --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/dbstats_collector.go @@ -0,0 +1,119 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import ( + "database/sql" + + "github.com/prometheus/client_golang/prometheus" +) + +type dbStatsCollector struct { + db *sql.DB + + maxOpenConnections *prometheus.Desc + + openConnections *prometheus.Desc + inUseConnections *prometheus.Desc + idleConnections *prometheus.Desc + + waitCount *prometheus.Desc + waitDuration *prometheus.Desc + maxIdleClosed *prometheus.Desc + maxIdleTimeClosed *prometheus.Desc + maxLifetimeClosed *prometheus.Desc +} + +// NewDBStatsCollector returns a collector that exports metrics about the given *sql.DB. +// See https://golang.org/pkg/database/sql/#DBStats for more information on stats. +func NewDBStatsCollector(db *sql.DB, dbName string) prometheus.Collector { + fqName := func(name string) string { + return "go_sql_" + name + } + return &dbStatsCollector{ + db: db, + maxOpenConnections: prometheus.NewDesc( + fqName("max_open_connections"), + "Maximum number of open connections to the database.", + nil, prometheus.Labels{"db_name": dbName}, + ), + openConnections: prometheus.NewDesc( + fqName("open_connections"), + "The number of established connections both in use and idle.", + nil, prometheus.Labels{"db_name": dbName}, + ), + inUseConnections: prometheus.NewDesc( + fqName("in_use_connections"), + "The number of connections currently in use.", + nil, prometheus.Labels{"db_name": dbName}, + ), + idleConnections: prometheus.NewDesc( + fqName("idle_connections"), + "The number of idle connections.", + nil, prometheus.Labels{"db_name": dbName}, + ), + waitCount: prometheus.NewDesc( + fqName("wait_count_total"), + "The total number of connections waited for.", + nil, prometheus.Labels{"db_name": dbName}, + ), + waitDuration: prometheus.NewDesc( + fqName("wait_duration_seconds_total"), + "The total time blocked waiting for a new connection.", + nil, prometheus.Labels{"db_name": dbName}, + ), + maxIdleClosed: prometheus.NewDesc( + fqName("max_idle_closed_total"), + "The total number of connections closed due to SetMaxIdleConns.", + nil, prometheus.Labels{"db_name": dbName}, + ), + maxIdleTimeClosed: prometheus.NewDesc( + fqName("max_idle_time_closed_total"), + "The total number of connections closed due to SetConnMaxIdleTime.", + nil, prometheus.Labels{"db_name": dbName}, + ), + maxLifetimeClosed: prometheus.NewDesc( + fqName("max_lifetime_closed_total"), + "The total number of connections closed due to SetConnMaxLifetime.", + nil, prometheus.Labels{"db_name": dbName}, + ), + } +} + +// Describe implements Collector. +func (c *dbStatsCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.maxOpenConnections + ch <- c.openConnections + ch <- c.inUseConnections + ch <- c.idleConnections + ch <- c.waitCount + ch <- c.waitDuration + ch <- c.maxIdleClosed + ch <- c.maxLifetimeClosed + ch <- c.maxIdleTimeClosed +} + +// Collect implements Collector. +func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.db.Stats() + ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) + ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) + ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse)) + ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle)) + ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) + ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) + ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) + ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) + ch <- prometheus.MustNewConstMetric(c.maxIdleTimeClosed, prometheus.CounterValue, float64(stats.MaxIdleTimeClosed)) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/expvar_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/expvar_collector.go new file mode 100644 index 000000000..b22d862fb --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/expvar_collector.go @@ -0,0 +1,57 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import "github.com/prometheus/client_golang/prometheus" + +// NewExpvarCollector returns a newly allocated expvar Collector. +// +// An expvar Collector collects metrics from the expvar interface. It provides a +// quick way to expose numeric values that are already exported via expvar as +// Prometheus metrics. Note that the data models of expvar and Prometheus are +// fundamentally different, and that the expvar Collector is inherently slower +// than native Prometheus metrics. Thus, the expvar Collector is probably great +// for experiments and prototyping, but you should seriously consider a more +// direct implementation of Prometheus metrics for monitoring production +// systems. +// +// The exports map has the following meaning: +// +// The keys in the map correspond to expvar keys, i.e. for every expvar key you +// want to export as Prometheus metric, you need an entry in the exports +// map. The descriptor mapped to each key describes how to export the expvar +// value. It defines the name and the help string of the Prometheus metric +// proxying the expvar value. The type will always be Untyped. +// +// For descriptors without variable labels, the expvar value must be a number or +// a bool. The number is then directly exported as the Prometheus sample +// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values +// that are not numbers or bools are silently ignored. +// +// If the descriptor has one variable label, the expvar value must be an expvar +// map. The keys in the expvar map become the various values of the one +// Prometheus label. The values in the expvar map must be numbers or bools again +// as above. +// +// For descriptors with more than one variable label, the expvar must be a +// nested expvar map, i.e. where the values of the topmost map are maps again +// etc. until a depth is reached that corresponds to the number of labels. The +// leaves of that structure must be numbers or bools as above to serve as the +// sample values. +// +// Anything that does not fit into the scheme above is silently ignored. +func NewExpvarCollector(exports map[string]*prometheus.Desc) prometheus.Collector { + //nolint:staticcheck // Ignore SA1019 until v2. + return prometheus.NewExpvarCollector(exports) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_go116.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_go116.go new file mode 100644 index 000000000..effc57840 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_go116.go @@ -0,0 +1,49 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !go1.17 +// +build !go1.17 + +package collectors + +import "github.com/prometheus/client_golang/prometheus" + +// NewGoCollector returns a collector that exports metrics about the current Go +// process. This includes memory stats. To collect those, runtime.ReadMemStats +// is called. This requires to “stop the worldâ€, which usually only happens for +// garbage collection (GC). Take the following implications into account when +// deciding whether to use the Go collector: +// +// 1. The performance impact of stopping the world is the more relevant the more +// frequently metrics are collected. However, with Go1.9 or later the +// stop-the-world time per metrics collection is very short (~25µs) so that the +// performance impact will only matter in rare cases. However, with older Go +// versions, the stop-the-world duration depends on the heap size and can be +// quite significant (~1.7 ms/GiB as per +// https://go-review.googlesource.com/c/go/+/34937). +// +// 2. During an ongoing GC, nothing else can stop the world. Therefore, if the +// metrics collection happens to coincide with GC, it will only complete after +// GC has finished. Usually, GC is fast enough to not cause problems. However, +// with a very large heap, GC might take multiple seconds, which is enough to +// cause scrape timeouts in common setups. To avoid this problem, the Go +// collector will use the memstats from a previous collection if +// runtime.ReadMemStats takes more than 1s. However, if there are no previously +// collected memstats, or their collection is more than 5m ago, the collection +// will block until runtime.ReadMemStats succeeds. +// +// NOTE: The problem is solved in Go 1.15, see +// https://github.com/golang/go/issues/19812 for the related Go issue. +func NewGoCollector() prometheus.Collector { + return prometheus.NewGoCollector() +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_latest.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_latest.go new file mode 100644 index 000000000..bcfa4fa10 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/go_collector_latest.go @@ -0,0 +1,165 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build go1.17 +// +build go1.17 + +package collectors + +import ( + "regexp" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/internal" +) + +var ( + // MetricsAll allows all the metrics to be collected from Go runtime. + MetricsAll = GoRuntimeMetricsRule{regexp.MustCompile("/.*")} + // MetricsGC allows only GC metrics to be collected from Go runtime. + // e.g. go_gc_cycles_automatic_gc_cycles_total + // NOTE: This does not include new class of "/cpu/classes/gc/..." metrics. + // Use custom metric rule to access those. + MetricsGC = GoRuntimeMetricsRule{regexp.MustCompile(`^/gc/.*`)} + // MetricsMemory allows only memory metrics to be collected from Go runtime. + // e.g. go_memory_classes_heap_free_bytes + MetricsMemory = GoRuntimeMetricsRule{regexp.MustCompile(`^/memory/.*`)} + // MetricsScheduler allows only scheduler metrics to be collected from Go runtime. + // e.g. go_sched_goroutines_goroutines + MetricsScheduler = GoRuntimeMetricsRule{regexp.MustCompile(`^/sched/.*`)} +) + +// WithGoCollectorMemStatsMetricsDisabled disables metrics that is gathered in runtime.MemStats structure such as: +// +// go_memstats_alloc_bytes +// go_memstats_alloc_bytes_total +// go_memstats_sys_bytes +// go_memstats_lookups_total +// go_memstats_mallocs_total +// go_memstats_frees_total +// go_memstats_heap_alloc_bytes +// go_memstats_heap_sys_bytes +// go_memstats_heap_idle_bytes +// go_memstats_heap_inuse_bytes +// go_memstats_heap_released_bytes +// go_memstats_heap_objects +// go_memstats_stack_inuse_bytes +// go_memstats_stack_sys_bytes +// go_memstats_mspan_inuse_bytes +// go_memstats_mspan_sys_bytes +// go_memstats_mcache_inuse_bytes +// go_memstats_mcache_sys_bytes +// go_memstats_buck_hash_sys_bytes +// go_memstats_gc_sys_bytes +// go_memstats_other_sys_bytes +// go_memstats_next_gc_bytes +// +// so the metrics known from pre client_golang v1.12.0, +// +// NOTE(bwplotka): The above represents runtime.MemStats statistics, but they are +// actually implemented using new runtime/metrics package. (except skipped go_memstats_gc_cpu_fraction +// -- see https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 for explanation). +// +// Some users might want to disable this on collector level (although you can use scrape relabelling on Prometheus), +// because similar metrics can be now obtained using WithGoCollectorRuntimeMetrics. Note that the semantics of new +// metrics might be different, plus the names can be change over time with different Go version. +// +// NOTE(bwplotka): Changing metric names can be tedious at times as the alerts, recording rules and dashboards have to be adjusted. +// The old metrics are also very useful, with many guides and books written about how to interpret them. +// +// As a result our recommendation would be to stick with MemStats like metrics and enable other runtime/metrics if you are interested +// in advanced insights Go provides. See ExampleGoCollector_WithAdvancedGoMetrics. +func WithGoCollectorMemStatsMetricsDisabled() func(options *internal.GoCollectorOptions) { + return func(o *internal.GoCollectorOptions) { + o.DisableMemStatsLikeMetrics = true + } +} + +// GoRuntimeMetricsRule allow enabling and configuring particular group of runtime/metrics. +// TODO(bwplotka): Consider adding ability to adjust buckets. +type GoRuntimeMetricsRule struct { + // Matcher represents RE2 expression will match the runtime/metrics from https://golang.bg/src/runtime/metrics/description.go + // Use `regexp.MustCompile` or `regexp.Compile` to create this field. + Matcher *regexp.Regexp +} + +// WithGoCollectorRuntimeMetrics allows enabling and configuring particular group of runtime/metrics. +// See the list of metrics https://golang.bg/src/runtime/metrics/description.go (pick the Go version you use there!). +// You can use this option in repeated manner, which will add new rules. The order of rules is important, the last rule +// that matches particular metrics is applied. +func WithGoCollectorRuntimeMetrics(rules ...GoRuntimeMetricsRule) func(options *internal.GoCollectorOptions) { + rs := make([]internal.GoCollectorRule, len(rules)) + for i, r := range rules { + rs[i] = internal.GoCollectorRule{ + Matcher: r.Matcher, + } + } + + return func(o *internal.GoCollectorOptions) { + o.RuntimeMetricRules = append(o.RuntimeMetricRules, rs...) + } +} + +// WithoutGoCollectorRuntimeMetrics allows disabling group of runtime/metrics that you might have added in WithGoCollectorRuntimeMetrics. +// It behaves similarly to WithGoCollectorRuntimeMetrics just with deny-list semantics. +func WithoutGoCollectorRuntimeMetrics(matchers ...*regexp.Regexp) func(options *internal.GoCollectorOptions) { + rs := make([]internal.GoCollectorRule, len(matchers)) + for i, m := range matchers { + rs[i] = internal.GoCollectorRule{ + Matcher: m, + Deny: true, + } + } + + return func(o *internal.GoCollectorOptions) { + o.RuntimeMetricRules = append(o.RuntimeMetricRules, rs...) + } +} + +// GoCollectionOption represents Go collection option flag. +// Deprecated. +type GoCollectionOption uint32 + +const ( + // GoRuntimeMemStatsCollection represents the metrics represented by runtime.MemStats structure. + // + // Deprecated: Use WithGoCollectorMemStatsMetricsDisabled() function to disable those metrics in the collector. + GoRuntimeMemStatsCollection GoCollectionOption = 1 << iota + // GoRuntimeMetricsCollection is the new set of metrics represented by runtime/metrics package. + // + // Deprecated: Use WithGoCollectorRuntimeMetrics(GoRuntimeMetricsRule{Matcher: regexp.MustCompile("/.*")}) + // function to enable those metrics in the collector. + GoRuntimeMetricsCollection +) + +// WithGoCollections allows enabling different collections for Go collector on top of base metrics. +// +// Deprecated: Use WithGoCollectorRuntimeMetrics() and WithGoCollectorMemStatsMetricsDisabled() instead to control metrics. +func WithGoCollections(flags GoCollectionOption) func(options *internal.GoCollectorOptions) { + return func(options *internal.GoCollectorOptions) { + if flags&GoRuntimeMemStatsCollection == 0 { + WithGoCollectorMemStatsMetricsDisabled()(options) + } + + if flags&GoRuntimeMetricsCollection != 0 { + WithGoCollectorRuntimeMetrics(GoRuntimeMetricsRule{Matcher: regexp.MustCompile("/.*")})(options) + } + } +} + +// NewGoCollector returns a collector that exports metrics about the current Go +// process using debug.GCStats (base metrics) and runtime/metrics (both in MemStats style and new ones). +func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) prometheus.Collector { + //nolint:staticcheck // Ignore SA1019 until v2. + return prometheus.NewGoCollector(opts...) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/process_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/process_collector.go new file mode 100644 index 000000000..24558f50a --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/process_collector.go @@ -0,0 +1,56 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import "github.com/prometheus/client_golang/prometheus" + +// ProcessCollectorOpts defines the behavior of a process metrics collector +// created with NewProcessCollector. +type ProcessCollectorOpts struct { + // PidFn returns the PID of the process the collector collects metrics + // for. It is called upon each collection. By default, the PID of the + // current process is used, as determined on construction time by + // calling os.Getpid(). + PidFn func() (int, error) + // If non-empty, each of the collected metrics is prefixed by the + // provided string and an underscore ("_"). + Namespace string + // If true, any error encountered during collection is reported as an + // invalid metric (see NewInvalidMetric). Otherwise, errors are ignored + // and the collected metrics will be incomplete. (Possibly, no metrics + // will be collected at all.) While that's usually not desired, it is + // appropriate for the common "mix-in" of process metrics, where process + // metrics are nice to have, but failing to collect them should not + // disrupt the collection of the remaining metrics. + ReportErrors bool +} + +// NewProcessCollector returns a collector which exports the current state of +// process metrics including CPU, memory and file descriptor usage as well as +// the process start time. The detailed behavior is defined by the provided +// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a +// collector for the current process with an empty namespace string and no error +// reporting. +// +// The collector only works on operating systems with a Linux-style proc +// filesystem and on Microsoft Windows. On other operating systems, it will not +// collect any metrics. +func NewProcessCollector(opts ProcessCollectorOpts) prometheus.Collector { + //nolint:staticcheck // Ignore SA1019 until v2. + return prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{ + PidFn: opts.PidFn, + Namespace: opts.Namespace, + ReportErrors: opts.ReportErrors, + }) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index a912b75a0..4ce84e7a8 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -20,6 +20,7 @@ import ( "time" dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/types/known/timestamppb" ) // Counter is a Metric that represents a single numerical value that only ever @@ -59,6 +60,18 @@ type ExemplarAdder interface { // CounterOpts is an alias for Opts. See there for doc comments. type CounterOpts Opts +// CounterVecOpts bundles the options to create a CounterVec metric. +// It is mandatory to set CounterOpts, see there for mandatory fields. VariableLabels +// is optional and can safely be left to its default value. +type CounterVecOpts struct { + CounterOpts + + // VariableLabels are used to partition the metric vector by the given set + // of labels. Each label value will be constrained with the optional Constraint + // function, if provided. + VariableLabels ConstrainableLabels +} + // NewCounter creates a new Counter based on the provided CounterOpts. // // The returned implementation also implements ExemplarAdder. It is safe to @@ -78,8 +91,12 @@ func NewCounter(opts CounterOpts) Counter { nil, opts.ConstLabels, ) - result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now} + if opts.now == nil { + opts.now = time.Now + } + result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: opts.now} result.init(result) // Init self-collection. + result.createdTs = timestamppb.New(opts.now()) return result } @@ -94,10 +111,12 @@ type counter struct { selfCollector desc *Desc + createdTs *timestamppb.Timestamp labelPairs []*dto.LabelPair exemplar atomic.Value // Containing nil or a *dto.Exemplar. - now func() time.Time // To mock out time.Now() for testing. + // now is for testing purposes, by default it's time.Now. + now func() time.Time } func (c *counter) Desc() *Desc { @@ -147,8 +166,7 @@ func (c *counter) Write(out *dto.Metric) error { exemplar = e.(*dto.Exemplar) } val := c.get() - - return populateMetric(CounterValue, val, c.labelPairs, exemplar, out) + return populateMetric(CounterValue, val, c.labelPairs, exemplar, out, c.createdTs) } func (c *counter) updateExemplar(v float64, l Labels) { @@ -174,19 +192,31 @@ type CounterVec struct { // NewCounterVec creates a new CounterVec based on the provided CounterOpts and // partitioned by the given label names. func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { - desc := NewDesc( + return V2.NewCounterVec(CounterVecOpts{ + CounterOpts: opts, + VariableLabels: UnconstrainedLabels(labelNames), + }) +} + +// NewCounterVec creates a new CounterVec based on the provided CounterVecOpts. +func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - labelNames, + opts.VariableLabels, opts.ConstLabels, ) + if opts.now == nil { + opts.now = time.Now + } return &CounterVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { - if len(lvs) != len(desc.variableLabels) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) + if len(lvs) != len(desc.variableLabels.names) { + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs)) } - result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now} + result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: opts.now} result.init(result) // Init self-collection. + result.createdTs = timestamppb.New(opts.now()) return result }), } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 8bc5e44e2..68ffe3c24 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -14,20 +14,16 @@ package prometheus import ( - "errors" "fmt" "sort" "strings" "github.com/cespare/xxhash/v2" - - "github.com/prometheus/client_golang/prometheus/internal" - - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" + dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/model" + "google.golang.org/protobuf/proto" - dto "github.com/prometheus/client_model/go" + "github.com/prometheus/client_golang/prometheus/internal" ) // Desc is the descriptor used by every Prometheus Metric. It is essentially @@ -54,9 +50,9 @@ type Desc struct { // constLabelPairs contains precalculated DTO label pairs based on // the constant labels. constLabelPairs []*dto.LabelPair - // variableLabels contains names of labels for which the metric - // maintains variable values. - variableLabels []string + // variableLabels contains names of labels and normalization function for + // which the metric maintains variable values. + variableLabels *compiledLabels // id is a hash of the values of the ConstLabels and fqName. This // must be unique among all registered descriptors and can therefore be // used as an identifier of the descriptor. @@ -80,10 +76,24 @@ type Desc struct { // For constLabels, the label values are constant. Therefore, they are fully // specified in the Desc. See the Collector example for a usage pattern. func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc { + return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels) +} + +// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc +// and will be reported on registration time. variableLabels and constLabels can +// be nil if no such labels should be set. fqName must not be empty. +// +// variableLabels only contain the label names and normalization functions. Their +// label values are variable and therefore not part of the Desc. (They are managed +// within the Metric.) +// +// For constLabels, the label values are constant. Therefore, they are fully +// specified in the Desc. See the Collector example for a usage pattern. +func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc { d := &Desc{ fqName: fqName, help: help, - variableLabels: variableLabels, + variableLabels: variableLabels.compile(), } if !model.IsValidMetricName(model.LabelValue(fqName)) { d.err = fmt.Errorf("%q is not a valid metric name", fqName) @@ -93,7 +103,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // their sorted label names) plus the fqName (at position 0). labelValues := make([]string, 1, len(constLabels)+1) labelValues[0] = fqName - labelNames := make([]string, 0, len(constLabels)+len(variableLabels)) + labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names)) labelNameSet := map[string]struct{}{} // First add only the const label names and sort them... for labelName := range constLabels { @@ -118,16 +128,16 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // Now add the variable label names, but prefix them with something that // cannot be in a regular label name. That prevents matching the label // dimension with a different mix between preset and variable labels. - for _, labelName := range variableLabels { - if !checkLabelName(labelName) { - d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) + for _, label := range d.variableLabels.names { + if !checkLabelName(label) { + d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName) return d } - labelNames = append(labelNames, "$"+labelName) - labelNameSet[labelName] = struct{}{} + labelNames = append(labelNames, "$"+label) + labelNameSet[label] = struct{}{} } if len(labelNames) != len(labelNameSet) { - d.err = errors.New("duplicate label names") + d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName) return d } @@ -179,11 +189,19 @@ func (d *Desc) String() string { fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()), ) } + vlStrings := make([]string, 0, len(d.variableLabels.names)) + for _, vl := range d.variableLabels.names { + if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil { + vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl)) + } else { + vlStrings = append(vlStrings, vl) + } + } return fmt.Sprintf( - "Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}", + "Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}", d.fqName, d.help, strings.Join(lpStrings, ","), - d.variableLabels, + strings.Join(vlStrings, ","), ) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/doc.go b/vendor/github.com/prometheus/client_golang/prometheus/doc.go index 811072cbd..962608f02 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/doc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/doc.go @@ -37,35 +37,35 @@ // // type metrics struct { // cpuTemp prometheus.Gauge -// hdFailures *prometheus.CounterVec +// hdFailures *prometheus.CounterVec // } // // func NewMetrics(reg prometheus.Registerer) *metrics { -// m := &metrics{ -// cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{ -// Name: "cpu_temperature_celsius", -// Help: "Current temperature of the CPU.", -// }), -// hdFailures: prometheus.NewCounterVec( -// prometheus.CounterOpts{ -// Name: "hd_errors_total", -// Help: "Number of hard-disk errors.", -// }, -// []string{"device"}, -// ), -// } -// reg.MustRegister(m.cpuTemp) -// reg.MustRegister(m.hdFailures) -// return m +// m := &metrics{ +// cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{ +// Name: "cpu_temperature_celsius", +// Help: "Current temperature of the CPU.", +// }), +// hdFailures: prometheus.NewCounterVec( +// prometheus.CounterOpts{ +// Name: "hd_errors_total", +// Help: "Number of hard-disk errors.", +// }, +// []string{"device"}, +// ), +// } +// reg.MustRegister(m.cpuTemp) +// reg.MustRegister(m.hdFailures) +// return m // } // // func main() { -// // Create a non-global registry. -// reg := prometheus.NewRegistry() +// // Create a non-global registry. +// reg := prometheus.NewRegistry() // -// // Create new metrics and register them using the custom registry. -// m := NewMetrics(reg) -// // Set values for the new created metrics. +// // Create new metrics and register them using the custom registry. +// m := NewMetrics(reg) +// // Set values for the new created metrics. // m.cpuTemp.Set(65.3) // m.hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc() // diff --git a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go index c41ab37f3..de5a85629 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go @@ -48,7 +48,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) { continue } var v interface{} - labels := make([]string, len(desc.variableLabels)) + labels := make([]string, len(desc.variableLabels.names)) if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil { ch <- NewInvalidMetric(desc, err) continue diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go index 21271a5bb..dd2eac940 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go @@ -55,6 +55,18 @@ type Gauge interface { // GaugeOpts is an alias for Opts. See there for doc comments. type GaugeOpts Opts +// GaugeVecOpts bundles the options to create a GaugeVec metric. +// It is mandatory to set GaugeOpts, see there for mandatory fields. VariableLabels +// is optional and can safely be left to its default value. +type GaugeVecOpts struct { + GaugeOpts + + // VariableLabels are used to partition the metric vector by the given set + // of labels. Each label value will be constrained with the optional Constraint + // function, if provided. + VariableLabels ConstrainableLabels +} + // NewGauge creates a new Gauge based on the provided GaugeOpts. // // The returned implementation is optimized for a fast Set method. If you have a @@ -123,7 +135,7 @@ func (g *gauge) Sub(val float64) { func (g *gauge) Write(out *dto.Metric) error { val := math.Float64frombits(atomic.LoadUint64(&g.valBits)) - return populateMetric(GaugeValue, val, g.labelPairs, nil, out) + return populateMetric(GaugeValue, val, g.labelPairs, nil, out, nil) } // GaugeVec is a Collector that bundles a set of Gauges that all share the same @@ -138,16 +150,24 @@ type GaugeVec struct { // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and // partitioned by the given label names. func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { - desc := NewDesc( + return V2.NewGaugeVec(GaugeVecOpts{ + GaugeOpts: opts, + VariableLabels: UnconstrainedLabels(labelNames), + }) +} + +// NewGaugeVec creates a new GaugeVec based on the provided GaugeVecOpts. +func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - labelNames, + opts.VariableLabels, opts.ConstLabels, ) return &GaugeVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { - if len(lvs) != len(desc.variableLabels) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) + if len(lvs) != len(desc.variableLabels.names) { + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs)) } result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)} result.init(result) // Init self-collection. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go index 3a2d55e84..2d8d9f64f 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go @@ -23,11 +23,10 @@ import ( "strings" "sync" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" - "github.com/prometheus/client_golang/prometheus/internal" + + dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" ) const ( diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index 4c873a01c..b5c8bcb39 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -22,10 +22,10 @@ import ( "sync/atomic" "time" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) // nativeHistogramBounds for the frac of observed values. Only relevant for @@ -392,7 +392,7 @@ type HistogramOpts struct { // zero, it is replaced by default buckets. The default buckets are // DefBuckets if no buckets for a native histogram (see below) are used, // otherwise the default is no buckets. (In other words, if you want to - // use both reguler buckets and buckets for a native histogram, you have + // use both regular buckets and buckets for a native histogram, you have // to define the regular buckets here explicitly.) Buckets []float64 @@ -402,7 +402,7 @@ type HistogramOpts struct { // Histogram by a Prometheus server with that feature enabled (requires // Prometheus v2.40+). Sparse buckets are exponential buckets covering // the whole float64 range (with the exception of the “zero†bucket, see - // SparseBucketsZeroThreshold below). From any one bucket to the next, + // NativeHistogramZeroThreshold below). From any one bucket to the next, // the width of the bucket grows by a constant // factor. NativeHistogramBucketFactor provides an upper bound for this // factor (exception see below). The smaller @@ -414,8 +414,8 @@ type HistogramOpts struct { // and 2, same as between 2 and 4, and 4 and 8, etc.). // // Details about the actually used factor: The factor is calculated as - // 2^(2^n), where n is an integer number between (and including) -8 and - // 4. n is chosen so that the resulting factor is the largest that is + // 2^(2^-n), where n is an integer number between (and including) -4 and + // 8. n is chosen so that the resulting factor is the largest that is // still smaller or equal to NativeHistogramBucketFactor. Note that the // smallest possible factor is therefore approx. 1.00271 (i.e. 2^(2^-8) // ). If NativeHistogramBucketFactor is greater than 1 but smaller than @@ -429,12 +429,12 @@ type HistogramOpts struct { // a major version bump. NativeHistogramBucketFactor float64 // All observations with an absolute value of less or equal - // NativeHistogramZeroThreshold are accumulated into a “zero†- // bucket. For best results, this should be close to a bucket - // boundary. This is usually the case if picking a power of two. If + // NativeHistogramZeroThreshold are accumulated into a “zero†bucket. + // For best results, this should be close to a bucket boundary. This is + // usually the case if picking a power of two. If // NativeHistogramZeroThreshold is left at zero, - // DefSparseBucketsZeroThreshold is used as the threshold. To configure - // a zero bucket with an actual threshold of zero (i.e. only + // DefNativeHistogramZeroThreshold is used as the threshold. To + // configure a zero bucket with an actual threshold of zero (i.e. only // observations of precisely zero will go into the zero bucket), set // NativeHistogramZeroThreshold to the NativeHistogramZeroThresholdZero // constant (or any negative float value). @@ -447,26 +447,49 @@ type HistogramOpts struct { // Histogram are sufficiently wide-spread. In particular, this could be // used as a DoS attack vector. Where the observed values depend on // external inputs, it is highly recommended to set a - // NativeHistogramMaxBucketNumber.) Once the set + // NativeHistogramMaxBucketNumber.) Once the set // NativeHistogramMaxBucketNumber is exceeded, the following strategy is - // enacted: First, if the last reset (or the creation) of the histogram - // is at least NativeHistogramMinResetDuration ago, then the whole - // histogram is reset to its initial state (including regular - // buckets). If less time has passed, or if - // NativeHistogramMinResetDuration is zero, no reset is - // performed. Instead, the zero threshold is increased sufficiently to - // reduce the number of buckets to or below - // NativeHistogramMaxBucketNumber, but not to more than - // NativeHistogramMaxZeroThreshold. Thus, if - // NativeHistogramMaxZeroThreshold is already at or below the current - // zero threshold, nothing happens at this step. After that, if the - // number of buckets still exceeds NativeHistogramMaxBucketNumber, the - // resolution of the histogram is reduced by doubling the width of the - // sparse buckets (up to a growth factor between one bucket to the next - // of 2^(2^4) = 65536, see above). + // enacted: + // - First, if the last reset (or the creation) of the histogram is at + // least NativeHistogramMinResetDuration ago, then the whole + // histogram is reset to its initial state (including regular + // buckets). + // - If less time has passed, or if NativeHistogramMinResetDuration is + // zero, no reset is performed. Instead, the zero threshold is + // increased sufficiently to reduce the number of buckets to or below + // NativeHistogramMaxBucketNumber, but not to more than + // NativeHistogramMaxZeroThreshold. Thus, if + // NativeHistogramMaxZeroThreshold is already at or below the current + // zero threshold, nothing happens at this step. + // - After that, if the number of buckets still exceeds + // NativeHistogramMaxBucketNumber, the resolution of the histogram is + // reduced by doubling the width of the sparse buckets (up to a + // growth factor between one bucket to the next of 2^(2^4) = 65536, + // see above). + // - Any increased zero threshold or reduced resolution is reset back + // to their original values once NativeHistogramMinResetDuration has + // passed (since the last reset or the creation of the histogram). NativeHistogramMaxBucketNumber uint32 NativeHistogramMinResetDuration time.Duration NativeHistogramMaxZeroThreshold float64 + + // now is for testing purposes, by default it's time.Now. + now func() time.Time + + // afterFunc is for testing purposes, by default it's time.AfterFunc. + afterFunc func(time.Duration, func()) *time.Timer +} + +// HistogramVecOpts bundles the options to create a HistogramVec metric. +// It is mandatory to set HistogramOpts, see there for mandatory fields. VariableLabels +// is optional and can safely be left to its default value. +type HistogramVecOpts struct { + HistogramOpts + + // VariableLabels are used to partition the metric vector by the given set + // of labels. Each label value will be constrained with the optional Constraint + // function, if provided. + VariableLabels ConstrainableLabels } // NewHistogram creates a new Histogram based on the provided HistogramOpts. It @@ -488,11 +511,11 @@ func NewHistogram(opts HistogramOpts) Histogram { } func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram { - if len(desc.variableLabels) != len(labelValues) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) + if len(desc.variableLabels.names) != len(labelValues) { + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues)) } - for _, n := range desc.variableLabels { + for _, n := range desc.variableLabels.names { if n == bucketLabel { panic(errBucketLabelNotAllowed) } @@ -503,6 +526,12 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr } } + if opts.now == nil { + opts.now = time.Now + } + if opts.afterFunc == nil { + opts.afterFunc = time.AfterFunc + } h := &histogram{ desc: desc, upperBounds: opts.Buckets, @@ -510,8 +539,9 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr nativeHistogramMaxBuckets: opts.NativeHistogramMaxBucketNumber, nativeHistogramMaxZeroThreshold: opts.NativeHistogramMaxZeroThreshold, nativeHistogramMinResetDuration: opts.NativeHistogramMinResetDuration, - lastResetTime: time.Now(), - now: time.Now, + lastResetTime: opts.now(), + now: opts.now, + afterFunc: opts.afterFunc, } if len(h.upperBounds) == 0 && opts.NativeHistogramBucketFactor <= 1 { h.upperBounds = DefBuckets @@ -544,16 +574,12 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr } // Finally we know the final length of h.upperBounds and can make buckets // for both counts as well as exemplars: - h.counts[0] = &histogramCounts{ - buckets: make([]uint64, len(h.upperBounds)), - nativeHistogramZeroThresholdBits: math.Float64bits(h.nativeHistogramZeroThreshold), - nativeHistogramSchema: h.nativeHistogramSchema, - } - h.counts[1] = &histogramCounts{ - buckets: make([]uint64, len(h.upperBounds)), - nativeHistogramZeroThresholdBits: math.Float64bits(h.nativeHistogramZeroThreshold), - nativeHistogramSchema: h.nativeHistogramSchema, - } + h.counts[0] = &histogramCounts{buckets: make([]uint64, len(h.upperBounds))} + atomic.StoreUint64(&h.counts[0].nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold)) + atomic.StoreInt32(&h.counts[0].nativeHistogramSchema, h.nativeHistogramSchema) + h.counts[1] = &histogramCounts{buckets: make([]uint64, len(h.upperBounds))} + atomic.StoreUint64(&h.counts[1].nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold)) + atomic.StoreInt32(&h.counts[1].nativeHistogramSchema, h.nativeHistogramSchema) h.exemplars = make([]atomic.Value, len(h.upperBounds)+1) h.init(h) // Init self-collection. @@ -632,8 +658,8 @@ func (hc *histogramCounts) observe(v float64, bucket int, doSparse bool) { if frac == 0.5 { key-- } - div := 1 << -schema - key = (key + div - 1) / div + offset := (1 << -schema) - 1 + key = (key + offset) >> -schema } if isInf { key++ @@ -694,9 +720,18 @@ type histogram struct { nativeHistogramMaxZeroThreshold float64 nativeHistogramMaxBuckets uint32 nativeHistogramMinResetDuration time.Duration - lastResetTime time.Time // Protected by mtx. - - now func() time.Time // To mock out time.Now() for testing. + // lastResetTime is protected by mtx. It is also used as created timestamp. + lastResetTime time.Time + // resetScheduled is protected by mtx. It is true if a reset is + // scheduled for a later time (when nativeHistogramMinResetDuration has + // passed). + resetScheduled bool + + // now is for testing purposes, by default it's time.Now. + now func() time.Time + + // afterFunc is for testing purposes, by default it's time.AfterFunc. + afterFunc func(time.Duration, func()) *time.Timer } func (h *histogram) Desc() *Desc { @@ -735,9 +770,10 @@ func (h *histogram) Write(out *dto.Metric) error { waitForCooldown(count, coldCounts) his := &dto.Histogram{ - Bucket: make([]*dto.Bucket, len(h.upperBounds)), - SampleCount: proto.Uint64(count), - SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))), + Bucket: make([]*dto.Bucket, len(h.upperBounds)), + SampleCount: proto.Uint64(count), + SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))), + CreatedTimestamp: timestamppb.New(h.lastResetTime), } out.Histogram = his out.Label = h.labelPairs @@ -775,6 +811,16 @@ func (h *histogram) Write(out *dto.Metric) error { his.ZeroCount = proto.Uint64(zeroBucket) his.NegativeSpan, his.NegativeDelta = makeBuckets(&coldCounts.nativeHistogramBucketsNegative) his.PositiveSpan, his.PositiveDelta = makeBuckets(&coldCounts.nativeHistogramBucketsPositive) + + // Add a no-op span to a histogram without observations and with + // a zero threshold of zero. Otherwise, a native histogram would + // look like a classic histogram to scrapers. + if *his.ZeroThreshold == 0 && *his.ZeroCount == 0 && len(his.PositiveSpan) == 0 && len(his.NegativeSpan) == 0 { + his.PositiveSpan = []*dto.BucketSpan{{ + Offset: proto.Int32(0), + Length: proto.Uint32(0), + }} + } } addAndResetCounts(hotCounts, coldCounts) return nil @@ -810,7 +856,7 @@ func (h *histogram) observe(v float64, bucket int) { } } -// limitSparsebuckets applies a strategy to limit the number of populated sparse +// limitBuckets applies a strategy to limit the number of populated sparse // buckets. It's generally best effort, and there are situations where the // number can go higher (if even the lowest resolution isn't enough to reduce // the number sufficiently, or if the provided counts aren't fully updated yet @@ -841,26 +887,39 @@ func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket if h.maybeReset(hotCounts, coldCounts, coldIdx, value, bucket) { return } + // One of the other strategies will happen. To undo what they will do as + // soon as enough time has passed to satisfy + // h.nativeHistogramMinResetDuration, schedule a reset at the right time + // if we haven't done so already. + if h.nativeHistogramMinResetDuration > 0 && !h.resetScheduled { + h.resetScheduled = true + h.afterFunc(h.nativeHistogramMinResetDuration-h.now().Sub(h.lastResetTime), h.reset) + } + if h.maybeWidenZeroBucket(hotCounts, coldCounts) { return } h.doubleBucketWidth(hotCounts, coldCounts) } -// maybeReset resests the whole histogram if at least h.nativeHistogramMinResetDuration -// has been passed. It returns true if the histogram has been reset. The caller -// must have locked h.mtx. -func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int) bool { +// maybeReset resets the whole histogram if at least +// h.nativeHistogramMinResetDuration has been passed. It returns true if the +// histogram has been reset. The caller must have locked h.mtx. +func (h *histogram) maybeReset( + hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int, +) bool { // We are using the possibly mocked h.now() rather than // time.Since(h.lastResetTime) to enable testing. - if h.nativeHistogramMinResetDuration == 0 || h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration { + if h.nativeHistogramMinResetDuration == 0 || // No reset configured. + h.resetScheduled || // Do not interefere if a reset is already scheduled. + h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration { return false } // Completely reset coldCounts. h.resetCounts(cold) // Repeat the latest observation to not lose it completely. cold.observe(value, bucket, true) - // Make coldCounts the new hot counts while ressetting countAndHotIdx. + // Make coldCounts the new hot counts while resetting countAndHotIdx. n := atomic.SwapUint64(&h.countAndHotIdx, (coldIdx<<63)+1) count := n & ((1 << 63) - 1) waitForCooldown(count, hot) @@ -870,6 +929,29 @@ func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value return true } +// reset resets the whole histogram. It locks h.mtx itself, i.e. it has to be +// called without having locked h.mtx. +func (h *histogram) reset() { + h.mtx.Lock() + defer h.mtx.Unlock() + + n := atomic.LoadUint64(&h.countAndHotIdx) + hotIdx := n >> 63 + coldIdx := (^n) >> 63 + hot := h.counts[hotIdx] + cold := h.counts[coldIdx] + // Completely reset coldCounts. + h.resetCounts(cold) + // Make coldCounts the new hot counts while resetting countAndHotIdx. + n = atomic.SwapUint64(&h.countAndHotIdx, coldIdx<<63) + count := n & ((1 << 63) - 1) + waitForCooldown(count, hot) + // Finally, reset the formerly hot counts, too. + h.resetCounts(hot) + h.lastResetTime = h.now() + h.resetScheduled = false +} + // maybeWidenZeroBucket widens the zero bucket until it includes the existing // buckets closest to the zero bucket (which could be two, if an equidistant // negative and a positive bucket exists, but usually it's only one bucket to be @@ -1034,15 +1116,23 @@ type HistogramVec struct { // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and // partitioned by the given label names. func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { - desc := NewDesc( + return V2.NewHistogramVec(HistogramVecOpts{ + HistogramOpts: opts, + VariableLabels: UnconstrainedLabels(labelNames), + }) +} + +// NewHistogramVec creates a new HistogramVec based on the provided HistogramVecOpts. +func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - labelNames, + opts.VariableLabels, opts.ConstLabels, ) return &HistogramVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { - return newHistogram(desc, opts, lvs...) + return newHistogram(desc, opts.HistogramOpts, lvs...) }), } } @@ -1161,6 +1251,7 @@ type constHistogram struct { sum float64 buckets map[float64]uint64 labelPairs []*dto.LabelPair + createdTs *timestamppb.Timestamp } func (h *constHistogram) Desc() *Desc { @@ -1168,7 +1259,9 @@ func (h *constHistogram) Desc() *Desc { } func (h *constHistogram) Write(out *dto.Metric) error { - his := &dto.Histogram{} + his := &dto.Histogram{ + CreatedTimestamp: h.createdTs, + } buckets := make([]*dto.Bucket, 0, len(h.buckets)) @@ -1215,7 +1308,7 @@ func NewConstHistogram( if desc.err != nil { return nil, desc.err } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { return nil, err } return &constHistogram{ @@ -1309,7 +1402,7 @@ func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) { // Multiple spans with only small gaps in between are probably // encoded more efficiently as one larger span with a few empty // buckets. Needs some research to find the sweet spot. For now, - // we assume that gaps of one ore two buckets should not create + // we assume that gaps of one or two buckets should not create // a new span. iDelta := int32(i - nextI) if n == 0 || iDelta > 2 { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go index fd0750f2c..a595a2036 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go @@ -14,7 +14,7 @@ // It provides tools to compare sequences of strings and generate textual diffs. // // Maintaining `GetUnifiedDiffString` here because original repository -// (https://github.com/pmezard/go-difflib) is no loger maintained. +// (https://github.com/pmezard/go-difflib) is no longer maintained. package internal import ( diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go index c1b8fad36..c21911f29 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go @@ -32,6 +32,104 @@ import ( // create a Desc. type Labels map[string]string +// LabelConstraint normalizes label values. +type LabelConstraint func(string) string + +// ConstrainedLabels represents a label name and its constrain function +// to normalize label values. This type is commonly used when constructing +// metric vector Collectors. +type ConstrainedLabel struct { + Name string + Constraint LabelConstraint +} + +// ConstrainableLabels is an interface that allows creating of labels that can +// be optionally constrained. +// +// prometheus.V2().NewCounterVec(CounterVecOpts{ +// CounterOpts: {...}, // Usual CounterOpts fields +// VariableLabels: []ConstrainedLabels{ +// {Name: "A"}, +// {Name: "B", Constraint: func(v string) string { ... }}, +// }, +// }) +type ConstrainableLabels interface { + compile() *compiledLabels + labelNames() []string +} + +// ConstrainedLabels represents a collection of label name -> constrain function +// to normalize label values. This type is commonly used when constructing +// metric vector Collectors. +type ConstrainedLabels []ConstrainedLabel + +func (cls ConstrainedLabels) compile() *compiledLabels { + compiled := &compiledLabels{ + names: make([]string, len(cls)), + labelConstraints: map[string]LabelConstraint{}, + } + + for i, label := range cls { + compiled.names[i] = label.Name + if label.Constraint != nil { + compiled.labelConstraints[label.Name] = label.Constraint + } + } + + return compiled +} + +func (cls ConstrainedLabels) labelNames() []string { + names := make([]string, len(cls)) + for i, label := range cls { + names[i] = label.Name + } + return names +} + +// UnconstrainedLabels represents collection of label without any constraint on +// their value. Thus, it is simply a collection of label names. +// +// UnconstrainedLabels([]string{ "A", "B" }) +// +// is equivalent to +// +// ConstrainedLabels { +// { Name: "A" }, +// { Name: "B" }, +// } +type UnconstrainedLabels []string + +func (uls UnconstrainedLabels) compile() *compiledLabels { + return &compiledLabels{ + names: uls, + } +} + +func (uls UnconstrainedLabels) labelNames() []string { + return uls +} + +type compiledLabels struct { + names []string + labelConstraints map[string]LabelConstraint +} + +func (cls *compiledLabels) compile() *compiledLabels { + return cls +} + +func (cls *compiledLabels) labelNames() []string { + return cls.names +} + +func (cls *compiledLabels) constrain(labelName, value string) string { + if fn, ok := cls.labelConstraints[labelName]; ok && fn != nil { + return fn(value) + } + return value +} + // reservedLabelPrefix is a prefix which is not legal in user-supplied // label names. const reservedLabelPrefix = "__" @@ -67,6 +165,8 @@ func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { func validateLabelValues(vals []string, expectedNumberOfValues int) error { if len(vals) != expectedNumberOfValues { + // The call below makes vals escape, copy them to avoid that. + vals := append([]string(nil), vals...) return fmt.Errorf( "%w: expected %d label values but got %d in %#v", errInconsistentCardinality, expectedNumberOfValues, diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go index b5119c504..f018e5723 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go @@ -20,11 +20,9 @@ import ( "strings" "time" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - "github.com/prometheus/common/model" - dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/model" + "google.golang.org/protobuf/proto" ) var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with xxhash. @@ -94,6 +92,9 @@ type Opts struct { // machine_role metric). See also // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels ConstLabels Labels + + // now is for testing purposes, by default it's time.Now. + now func() time.Time } // BuildFQName joins the given three name components by "_". Empty name diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go index c0152cdb6..8c1136cee 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !windows && !js -// +build !windows,!js +//go:build !windows && !js && !wasip1 +// +build !windows,!js,!wasip1 package prometheus diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go new file mode 100644 index 000000000..d8d9a6d7a --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go @@ -0,0 +1,26 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build wasip1 +// +build wasip1 + +package prometheus + +func canCollectProcess() bool { + return false +} + +func (*processCollector) processCollect(chan<- Metric) { + // noop on this platform + return +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go b/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go index 8031e8704..58f96599f 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go @@ -17,7 +17,7 @@ // constructors register the Collectors with a registry before returning them. // There are two sets of constructors. The constructors in the first set are // top-level functions, while the constructors in the other set are methods of -// the Factory type. The top-level function return Collectors registered with +// the Factory type. The top-level functions return Collectors registered with // the global registry (prometheus.DefaultRegisterer), while the methods return // Collectors registered with the registry the Factory was constructed with. All // constructors panic if the registration fails. @@ -28,30 +28,30 @@ // package main // // import ( -// "math/rand" -// "net/http" +// "math/rand" +// "net/http" // -// "github.com/prometheus/client_golang/prometheus" -// "github.com/prometheus/client_golang/prometheus/promauto" -// "github.com/prometheus/client_golang/prometheus/promhttp" +// "github.com/prometheus/client_golang/prometheus" +// "github.com/prometheus/client_golang/prometheus/promauto" +// "github.com/prometheus/client_golang/prometheus/promhttp" // ) // // var histogram = promauto.NewHistogram(prometheus.HistogramOpts{ -// Name: "random_numbers", -// Help: "A histogram of normally distributed random numbers.", -// Buckets: prometheus.LinearBuckets(-3, .1, 61), +// Name: "random_numbers", +// Help: "A histogram of normally distributed random numbers.", +// Buckets: prometheus.LinearBuckets(-3, .1, 61), // }) // // func Random() { -// for { -// histogram.Observe(rand.NormFloat64()) -// } +// for { +// histogram.Observe(rand.NormFloat64()) +// } // } // // func main() { -// go Random() -// http.Handle("/metrics", promhttp.Handler()) -// http.ListenAndServe(":1971", nil) +// go Random() +// http.Handle("/metrics", promhttp.Handler()) +// http.ListenAndServe(":1971", nil) // } // // Prometheus's version of a minimal hello-world program: @@ -85,7 +85,7 @@ // } // // A Factory is created with the With(prometheus.Registerer) function, which -// enables two usage pattern. With(prometheus.Registerer) can be called once per +// enables two usage patterns. With(prometheus.Registerer) can be called once per // line: // // var ( @@ -153,7 +153,7 @@ // importing a package. // // A separate package allows conservative users to entirely ignore it. And -// whoever wants to use it, will do so explicitly, with an opportunity to read +// whoever wants to use it will do so explicitly, with an opportunity to read // this warning. // // Enjoy promauto responsibly! diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go index a4cc9810b..09b8d2fbe 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -37,6 +37,7 @@ import ( "fmt" "io" "net/http" + "strconv" "strings" "sync" "time" @@ -47,9 +48,10 @@ import ( ) const ( - contentTypeHeader = "Content-Type" - contentEncodingHeader = "Content-Encoding" - acceptEncodingHeader = "Accept-Encoding" + contentTypeHeader = "Content-Type" + contentEncodingHeader = "Content-Encoding" + acceptEncodingHeader = "Accept-Encoding" + processStartTimeHeader = "Process-Start-Time-Unix" ) var gzipPool = sync.Pool{ @@ -121,6 +123,9 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO } h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { + if !opts.ProcessStartTime.IsZero() { + rsp.Header().Set(processStartTimeHeader, strconv.FormatInt(opts.ProcessStartTime.Unix(), 10)) + } if inFlightSem != nil { select { case inFlightSem <- struct{}{}: // All good, carry on. @@ -366,6 +371,14 @@ type HandlerOpts struct { // (which changes the identity of the resulting series on the Prometheus // server). EnableOpenMetrics bool + // ProcessStartTime allows setting process start timevalue that will be exposed + // with "Process-Start-Time-Unix" response header along with the metrics + // payload. This allow callers to have efficient transformations to cumulative + // counters (e.g. OpenTelemetry) or generally _created timestamp estimation per + // scrape target. + // NOTE: This feature is experimental and not covered by OpenMetrics or Prometheus + // exposition format. + ProcessStartTime time.Time } // gzipAccepted returns whether the client will accept gzip-encoded content. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go index 210867816..d3482c40c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go @@ -68,16 +68,17 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou o.apply(rtOpts) } - code, method := checkLabels(counter) + // Curry the counter with dynamic labels before checking the remaining labels. + code, method := checkLabels(counter.MustCurryWith(rtOpts.emptyDynamicLabels())) return func(r *http.Request) (*http.Response, error) { resp, err := next.RoundTrip(r) if err == nil { - addWithExemplar( - counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)), - 1, - rtOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) + for label, resolve := range rtOpts.extraLabelsFromCtx { + l[label] = resolve(resp.Request.Context()) + } + addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r.Context())) } return resp, err } @@ -110,17 +111,18 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT o.apply(rtOpts) } - code, method := checkLabels(obs) + // Curry the observer with dynamic labels before checking the remaining labels. + code, method := checkLabels(obs.MustCurryWith(rtOpts.emptyDynamicLabels())) return func(r *http.Request) (*http.Response, error) { start := time.Now() resp, err := next.RoundTrip(r) if err == nil { - observeWithExemplar( - obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)), - time.Since(start).Seconds(), - rtOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) + for label, resolve := range rtOpts.extraLabelsFromCtx { + l[label] = resolve(resp.Request.Context()) + } + observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r.Context())) } return resp, err } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go index cca67a78a..356edb786 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go @@ -87,7 +87,8 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op o.apply(hOpts) } - code, method := checkLabels(obs) + // Curry the observer with dynamic labels before checking the remaining labels. + code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) if code { return func(w http.ResponseWriter, r *http.Request) { @@ -95,23 +96,22 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op d := newDelegator(w, nil) next.ServeHTTP(d, r) - observeWithExemplar( - obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), - time.Since(now).Seconds(), - hOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) } } return func(w http.ResponseWriter, r *http.Request) { now := time.Now() next.ServeHTTP(w, r) - - observeWithExemplar( - obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), - time.Since(now).Seconds(), - hOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, 0, hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) } } @@ -138,28 +138,30 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, o.apply(hOpts) } - code, method := checkLabels(counter) + // Curry the counter with dynamic labels before checking the remaining labels. + code, method := checkLabels(counter.MustCurryWith(hOpts.emptyDynamicLabels())) if code { return func(w http.ResponseWriter, r *http.Request) { d := newDelegator(w, nil) next.ServeHTTP(d, r) - addWithExemplar( - counter.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), - 1, - hOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) } } return func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) - addWithExemplar( - counter.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), - 1, - hOpts.getExemplarFn(r.Context()), - ) + + l := labels(code, method, r.Method, 0, hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) } } @@ -191,16 +193,17 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha o.apply(hOpts) } - code, method := checkLabels(obs) + // Curry the observer with dynamic labels before checking the remaining labels. + code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) return func(w http.ResponseWriter, r *http.Request) { now := time.Now() d := newDelegator(w, func(status int) { - observeWithExemplar( - obs.With(labels(code, method, r.Method, status, hOpts.extraMethods...)), - time.Since(now).Seconds(), - hOpts.getExemplarFn(r.Context()), - ) + l := labels(code, method, r.Method, status, hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) }) next.ServeHTTP(d, r) } @@ -231,28 +234,32 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, o.apply(hOpts) } - code, method := checkLabels(obs) + // Curry the observer with dynamic labels before checking the remaining labels. + code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) + if code { return func(w http.ResponseWriter, r *http.Request) { d := newDelegator(w, nil) next.ServeHTTP(d, r) size := computeApproximateRequestSize(r) - observeWithExemplar( - obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), - float64(size), - hOpts.getExemplarFn(r.Context()), - ) + + l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) } } return func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) size := computeApproximateRequestSize(r) - observeWithExemplar( - obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)), - float64(size), - hOpts.getExemplarFn(r.Context()), - ) + + l := labels(code, method, r.Method, 0, hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) } } @@ -281,16 +288,18 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler o.apply(hOpts) } - code, method := checkLabels(obs) + // Curry the observer with dynamic labels before checking the remaining labels. + code, method := checkLabels(obs.MustCurryWith(hOpts.emptyDynamicLabels())) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { d := newDelegator(w, nil) next.ServeHTTP(d, r) - observeWithExemplar( - obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)), - float64(d.Written()), - hOpts.getExemplarFn(r.Context()), - ) + + l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) + for label, resolve := range hOpts.extraLabelsFromCtx { + l[label] = resolve(r.Context()) + } + observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r.Context())) }) } @@ -380,15 +389,12 @@ func isLabelCurried(c prometheus.Collector, label string) bool { return true } -// emptyLabels is a one-time allocation for non-partitioned metrics to avoid -// unnecessary allocations on each request. -var emptyLabels = prometheus.Labels{} - func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels { + labels := prometheus.Labels{} + if !(code || method) { - return emptyLabels + return labels } - labels := prometheus.Labels{} if code { labels["code"] = sanitizeCode(status) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go index c590d912c..5d4383aa1 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go @@ -24,14 +24,32 @@ type Option interface { apply(*options) } +// LabelValueFromCtx are used to compute the label value from request context. +// Context can be filled with values from request through middleware. +type LabelValueFromCtx func(ctx context.Context) string + // options store options for both a handler or round tripper. type options struct { - extraMethods []string - getExemplarFn func(requestCtx context.Context) prometheus.Labels + extraMethods []string + getExemplarFn func(requestCtx context.Context) prometheus.Labels + extraLabelsFromCtx map[string]LabelValueFromCtx } func defaultOptions() *options { - return &options{getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }} + return &options{ + getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }, + extraLabelsFromCtx: map[string]LabelValueFromCtx{}, + } +} + +func (o *options) emptyDynamicLabels() prometheus.Labels { + labels := prometheus.Labels{} + + for label := range o.extraLabelsFromCtx { + labels[label] = "" + } + + return labels } type optionApplyFunc func(*options) @@ -48,11 +66,19 @@ func WithExtraMethods(methods ...string) Option { }) } -// WithExemplarFromContext adds allows to put a hook to all counter and histogram metrics. -// If the hook function returns non-nil labels, exemplars will be added for that request, otherwise metric -// will get instrumented without exemplar. +// WithExemplarFromContext allows to inject function that will get exemplar from context that will be put to counter and histogram metrics. +// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but +// metric will continue to observe/increment. func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { return optionApplyFunc(func(o *options) { o.getExemplarFn = getExemplarFn }) } + +// WithLabelFromCtx registers a label for dynamic resolution with access to context. +// See the example for ExampleInstrumentHandlerWithLabelResolver for example usage +func WithLabelFromCtx(name string, valueFn LabelValueFromCtx) Option { + return optionApplyFunc(func(o *options) { + o.extraLabelsFromCtx[name] = valueFn + }) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go index 09e34d307..5e2ced25a 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go @@ -21,18 +21,17 @@ import ( "path/filepath" "runtime" "sort" + "strconv" "strings" "sync" "unicode/utf8" - "github.com/cespare/xxhash/v2" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - "github.com/prometheus/common/expfmt" + "github.com/prometheus/client_golang/prometheus/internal" + "github.com/cespare/xxhash/v2" dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus/internal" + "github.com/prometheus/common/expfmt" + "google.golang.org/protobuf/proto" ) const ( @@ -549,7 +548,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { goroutineBudget-- runtime.Gosched() } - // Once both checkedMetricChan and uncheckdMetricChan are closed + // Once both checkedMetricChan and uncheckedMetricChan are closed // and drained, the contraption above will nil out cmc and umc, // and then we can leave the collect loop here. if cmc == nil && umc == nil { @@ -933,6 +932,10 @@ func checkMetricConsistency( h.WriteString(lp.GetValue()) h.Write(separatorByteSlice) } + if dtoMetric.TimestampMs != nil { + h.WriteString(strconv.FormatInt(*(dtoMetric.TimestampMs), 10)) + h.Write(separatorByteSlice) + } hSum := h.Sum64() if _, exists := metricHashes[hSum]; exists { return fmt.Errorf( @@ -960,7 +963,7 @@ func checkDescConsistency( // Is the desc consistent with the content of the metric? lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label)) copy(lpsFromDesc, desc.constLabelPairs) - for _, l := range desc.variableLabels { + for _, l := range desc.variableLabels.names { lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ Name: proto.String(l), }) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index 7bc448a89..146270444 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -22,11 +22,11 @@ import ( "sync/atomic" "time" - "github.com/beorn7/perks/quantile" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" + + "github.com/beorn7/perks/quantile" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) // quantileLabel is used for the label that defines the quantile in a @@ -146,6 +146,21 @@ type SummaryOpts struct { // is the internal buffer size of the underlying package // "github.com/bmizerany/perks/quantile"). BufCap uint32 + + // now is for testing purposes, by default it's time.Now. + now func() time.Time +} + +// SummaryVecOpts bundles the options to create a SummaryVec metric. +// It is mandatory to set SummaryOpts, see there for mandatory fields. VariableLabels +// is optional and can safely be left to its default value. +type SummaryVecOpts struct { + SummaryOpts + + // VariableLabels are used to partition the metric vector by the given set + // of labels. Each label value will be constrained with the optional Constraint + // function, if provided. + VariableLabels ConstrainableLabels } // Problem with the sliding-window decay algorithm... The Merge method of @@ -177,11 +192,11 @@ func NewSummary(opts SummaryOpts) Summary { } func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { - if len(desc.variableLabels) != len(labelValues) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) + if len(desc.variableLabels.names) != len(labelValues) { + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues)) } - for _, n := range desc.variableLabels { + for _, n := range desc.variableLabels.names { if n == quantileLabel { panic(errQuantileLabelNotAllowed) } @@ -211,6 +226,9 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { opts.BufCap = DefBufCap } + if opts.now == nil { + opts.now = time.Now + } if len(opts.Objectives) == 0 { // Use the lock-free implementation of a Summary without objectives. s := &noObjectivesSummary{ @@ -219,6 +237,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { counts: [2]*summaryCounts{{}, {}}, } s.init(s) // Init self-collection. + s.createdTs = timestamppb.New(opts.now()) return s } @@ -234,7 +253,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { coldBuf: make([]float64, 0, opts.BufCap), streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets), } - s.headStreamExpTime = time.Now().Add(s.streamDuration) + s.headStreamExpTime = opts.now().Add(s.streamDuration) s.hotBufExpTime = s.headStreamExpTime for i := uint32(0); i < opts.AgeBuckets; i++ { @@ -248,6 +267,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { sort.Float64s(s.sortedObjectives) s.init(s) // Init self-collection. + s.createdTs = timestamppb.New(opts.now()) return s } @@ -275,6 +295,8 @@ type summary struct { headStream *quantile.Stream headStreamIdx int headStreamExpTime, hotBufExpTime time.Time + + createdTs *timestamppb.Timestamp } func (s *summary) Desc() *Desc { @@ -296,7 +318,9 @@ func (s *summary) Observe(v float64) { } func (s *summary) Write(out *dto.Metric) error { - sum := &dto.Summary{} + sum := &dto.Summary{ + CreatedTimestamp: s.createdTs, + } qs := make([]*dto.Quantile, 0, len(s.objectives)) s.bufMtx.Lock() @@ -429,6 +453,8 @@ type noObjectivesSummary struct { counts [2]*summaryCounts labelPairs []*dto.LabelPair + + createdTs *timestamppb.Timestamp } func (s *noObjectivesSummary) Desc() *Desc { @@ -479,8 +505,9 @@ func (s *noObjectivesSummary) Write(out *dto.Metric) error { } sum := &dto.Summary{ - SampleCount: proto.Uint64(count), - SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))), + SampleCount: proto.Uint64(count), + SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))), + CreatedTimestamp: s.createdTs, } out.Summary = sum @@ -530,20 +557,28 @@ type SummaryVec struct { // it is handled by the Prometheus server internally, “quantile†is an illegal // label name. NewSummaryVec will panic if this label name is used. func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { - for _, ln := range labelNames { + return V2.NewSummaryVec(SummaryVecOpts{ + SummaryOpts: opts, + VariableLabels: UnconstrainedLabels(labelNames), + }) +} + +// NewSummaryVec creates a new SummaryVec based on the provided SummaryVecOpts. +func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { + for _, ln := range opts.VariableLabels.labelNames() { if ln == quantileLabel { panic(errQuantileLabelNotAllowed) } } - desc := NewDesc( + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - labelNames, + opts.VariableLabels, opts.ConstLabels, ) return &SummaryVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { - return newSummary(desc, opts, lvs...) + return newSummary(desc, opts.SummaryOpts, lvs...) }), } } @@ -662,6 +697,7 @@ type constSummary struct { sum float64 quantiles map[float64]float64 labelPairs []*dto.LabelPair + createdTs *timestamppb.Timestamp } func (s *constSummary) Desc() *Desc { @@ -669,7 +705,9 @@ func (s *constSummary) Desc() *Desc { } func (s *constSummary) Write(out *dto.Metric) error { - sum := &dto.Summary{} + sum := &dto.Summary{ + CreatedTimestamp: s.createdTs, + } qs := make([]*dto.Quantile, 0, len(s.quantiles)) sum.SampleCount = proto.Uint64(s.count) @@ -718,7 +756,7 @@ func NewConstSummary( if desc.err != nil { return nil, desc.err } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { return nil, err } return &constSummary{ diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go index f28a76f3a..52344fef5 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/timer.go @@ -23,7 +23,9 @@ type Timer struct { } // NewTimer creates a new Timer. The provided Observer is used to observe a -// duration in seconds. Timer is usually used to time a function call in the +// duration in seconds. If the Observer implements ExemplarObserver, passing exemplar +// later on will be also supported. +// Timer is usually used to time a function call in the // following way: // // func TimeMe() { @@ -31,6 +33,14 @@ type Timer struct { // defer timer.ObserveDuration() // // Do actual work. // } +// +// or +// +// func TimeMeWithExemplar() { +// timer := NewTimer(myHistogram) +// defer timer.ObserveDurationWithExemplar(exemplar) +// // Do actual work. +// } func NewTimer(o Observer) *Timer { return &Timer{ begin: time.Now(), @@ -53,3 +63,19 @@ func (t *Timer) ObserveDuration() time.Duration { } return d } + +// ObserveDurationWithExemplar is like ObserveDuration, but it will also +// observe exemplar with the duration unless exemplar is nil or provided Observer can't +// be casted to ExemplarObserver. +func (t *Timer) ObserveDurationWithExemplar(exemplar Labels) time.Duration { + d := time.Since(t.begin) + eo, ok := t.observer.(ExemplarObserver) + if ok && exemplar != nil { + eo.ObserveWithExemplar(d.Seconds(), exemplar) + return d + } + if t.observer != nil { + t.observer.Observe(d.Seconds()) + } + return d +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/value.go b/vendor/github.com/prometheus/client_golang/prometheus/value.go index 2d3abc1cb..cc23011fa 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/value.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/value.go @@ -14,18 +14,17 @@ package prometheus import ( + "errors" "fmt" "sort" "time" "unicode/utf8" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" - "github.com/prometheus/client_golang/prometheus/internal" dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) // ValueType is an enumeration of metric types that represent a simple value. @@ -93,7 +92,7 @@ func (v *valueFunc) Desc() *Desc { } func (v *valueFunc) Write(out *dto.Metric) error { - return populateMetric(v.valType, v.function(), v.labelPairs, nil, out) + return populateMetric(v.valType, v.function(), v.labelPairs, nil, out, nil) } // NewConstMetric returns a metric with one fixed value that cannot be @@ -107,12 +106,12 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues if desc.err != nil { return nil, desc.err } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { return nil, err } metric := &dto.Metric{} - if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric); err != nil { + if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, nil); err != nil { return nil, err } @@ -132,6 +131,43 @@ func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelVal return m } +// NewConstMetricWithCreatedTimestamp does the same thing as NewConstMetric, but generates Counters +// with created timestamp set and returns an error for other metric types. +func NewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) (Metric, error) { + if desc.err != nil { + return nil, desc.err + } + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { + return nil, err + } + switch valueType { + case CounterValue: + break + default: + return nil, errors.New("created timestamps are only supported for counters") + } + + metric := &dto.Metric{} + if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, timestamppb.New(ct)); err != nil { + return nil, err + } + + return &constMetric{ + desc: desc, + metric: metric, + }, nil +} + +// MustNewConstMetricWithCreatedTimestamp is a version of NewConstMetricWithCreatedTimestamp that panics where +// NewConstMetricWithCreatedTimestamp would have returned an error. +func MustNewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) Metric { + m, err := NewConstMetricWithCreatedTimestamp(desc, valueType, value, ct, labelValues...) + if err != nil { + panic(err) + } + return m +} + type constMetric struct { desc *Desc metric *dto.Metric @@ -155,11 +191,12 @@ func populateMetric( labelPairs []*dto.LabelPair, e *dto.Exemplar, m *dto.Metric, + ct *timestamppb.Timestamp, ) error { m.Label = labelPairs switch t { case CounterValue: - m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e} + m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e, CreatedTimestamp: ct} case GaugeValue: m.Gauge = &dto.Gauge{Value: proto.Float64(v)} case UntypedValue: @@ -178,19 +215,19 @@ func populateMetric( // This function is only needed for custom Metric implementations. See MetricVec // example. func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { - totalLen := len(desc.variableLabels) + len(desc.constLabelPairs) + totalLen := len(desc.variableLabels.names) + len(desc.constLabelPairs) if totalLen == 0 { // Super fast path. return nil } - if len(desc.variableLabels) == 0 { + if len(desc.variableLabels.names) == 0 { // Moderately fast path. return desc.constLabelPairs } labelPairs := make([]*dto.LabelPair, 0, totalLen) - for i, n := range desc.variableLabels { + for i, l := range desc.variableLabels.names { labelPairs = append(labelPairs, &dto.LabelPair{ - Name: proto.String(n), + Name: proto.String(l), Value: proto.String(labelValues[i]), }) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/prometheus/client_golang/prometheus/vec.go index 7ae322590..955cfd59f 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go @@ -72,6 +72,8 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { // with a performance overhead (for creating and processing the Labels map). // See also the CounterVec example. func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { + lvs = constrainLabelValues(m.desc, lvs, m.curry) + h, err := m.hashLabelValues(lvs) if err != nil { return false @@ -91,6 +93,9 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { // This method is used for the same purpose as DeleteLabelValues(...string). See // there for pros and cons of the two methods. func (m *MetricVec) Delete(labels Labels) bool { + labels, closer := constrainLabels(m.desc, labels) + defer closer() + h, err := m.hashLabels(labels) if err != nil { return false @@ -106,6 +111,9 @@ func (m *MetricVec) Delete(labels Labels) bool { // Note that curried labels will never be matched if deleting from the curried vector. // To match curried labels with DeletePartialMatch, it must be called on the base vector. func (m *MetricVec) DeletePartialMatch(labels Labels) int { + labels, closer := constrainLabels(m.desc, labels) + defer closer() + return m.metricMap.deleteByLabels(labels, m.curry) } @@ -144,11 +152,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { oldCurry = m.curry iCurry int ) - for i, label := range m.desc.variableLabels { - val, ok := labels[label] + for i, labelName := range m.desc.variableLabels.names { + val, ok := labels[labelName] if iCurry < len(oldCurry) && oldCurry[iCurry].index == i { if ok { - return nil, fmt.Errorf("label name %q is already curried", label) + return nil, fmt.Errorf("label name %q is already curried", labelName) } newCurry = append(newCurry, oldCurry[iCurry]) iCurry++ @@ -156,7 +164,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { if !ok { continue // Label stays uncurried. } - newCurry = append(newCurry, curriedLabelValue{i, val}) + newCurry = append(newCurry, curriedLabelValue{ + i, + m.desc.variableLabels.constrain(labelName, val), + }) } } if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 { @@ -199,6 +210,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { // a wrapper around MetricVec, implementing a vector for a specific Metric // implementation, for example GaugeVec. func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { + lvs = constrainLabelValues(m.desc, lvs, m.curry) h, err := m.hashLabelValues(lvs) if err != nil { return nil, err @@ -224,6 +236,9 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { // around MetricVec, implementing a vector for a specific Metric implementation, // for example GaugeVec. func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { + labels, closer := constrainLabels(m.desc, labels) + defer closer() + h, err := m.hashLabels(labels) if err != nil { return nil, err @@ -233,7 +248,7 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { } func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { - if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil { + if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil { return 0, err } @@ -242,7 +257,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { curry = m.curry iVals, iCurry int ) - for i := 0; i < len(m.desc.variableLabels); i++ { + for i := 0; i < len(m.desc.variableLabels.names); i++ { if iCurry < len(curry) && curry[iCurry].index == i { h = m.hashAdd(h, curry[iCurry].value) iCurry++ @@ -256,7 +271,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { } func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { - if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil { + if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil { return 0, err } @@ -265,17 +280,17 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { curry = m.curry iCurry int ) - for i, label := range m.desc.variableLabels { - val, ok := labels[label] + for i, labelName := range m.desc.variableLabels.names { + val, ok := labels[labelName] if iCurry < len(curry) && curry[iCurry].index == i { if ok { - return 0, fmt.Errorf("label name %q is already curried", label) + return 0, fmt.Errorf("label name %q is already curried", labelName) } h = m.hashAdd(h, curry[iCurry].value) iCurry++ } else { if !ok { - return 0, fmt.Errorf("label name %q missing in label map", label) + return 0, fmt.Errorf("label name %q missing in label map", labelName) } h = m.hashAdd(h, val) } @@ -453,7 +468,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values [] func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { for l, v := range labels { // Check if the target label exists in our metrics and get the index. - varLabelIndex, validLabel := indexOf(l, desc.variableLabels) + varLabelIndex, validLabel := indexOf(l, desc.variableLabels.names) if validLabel { // Check the value of that label against the target value. // We don't consider curried values in partial matches. @@ -597,7 +612,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe return false } iCurry := 0 - for i, k := range desc.variableLabels { + for i, k := range desc.variableLabels.names { if iCurry < len(curry) && curry[iCurry].index == i { if values[i] != curry[iCurry].value { return false @@ -615,7 +630,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string { labelValues := make([]string, len(labels)+len(curry)) iCurry := 0 - for i, k := range desc.variableLabels { + for i, k := range desc.variableLabels.names { if iCurry < len(curry) && curry[iCurry].index == i { labelValues[i] = curry[iCurry].value iCurry++ @@ -640,3 +655,55 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { } return labelValues } + +var labelsPool = &sync.Pool{ + New: func() interface{} { + return make(Labels) + }, +} + +func constrainLabels(desc *Desc, labels Labels) (Labels, func()) { + if len(desc.variableLabels.labelConstraints) == 0 { + // Fast path when there's no constraints + return labels, func() {} + } + + constrainedLabels := labelsPool.Get().(Labels) + for l, v := range labels { + constrainedLabels[l] = desc.variableLabels.constrain(l, v) + } + + return constrainedLabels, func() { + for k := range constrainedLabels { + delete(constrainedLabels, k) + } + labelsPool.Put(constrainedLabels) + } +} + +func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string { + if len(desc.variableLabels.labelConstraints) == 0 { + // Fast path when there's no constraints + return lvs + } + + constrainedValues := make([]string, len(lvs)) + var iCurry, iLVs int + for i := 0; i < len(lvs)+len(curry); i++ { + if iCurry < len(curry) && curry[iCurry].index == i { + iCurry++ + continue + } + + if i < len(desc.variableLabels.names) { + constrainedValues[iLVs] = desc.variableLabels.constrain( + desc.variableLabels.names[i], + lvs[iLVs], + ) + } else { + constrainedValues[iLVs] = lvs[iLVs] + } + iLVs++ + } + return constrainedValues +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vnext.go b/vendor/github.com/prometheus/client_golang/prometheus/vnext.go new file mode 100644 index 000000000..42bc3a8f0 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/vnext.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus + +type v2 struct{} + +// V2 is a struct that can be referenced to access experimental API that might +// be present in v2 of client golang someday. It offers extended functionality +// of v1 with slightly changed API. It is acceptable to use some pieces from v1 +// and e.g `prometheus.NewGauge` and some from v2 e.g. `prometheus.V2.NewDesc` +// in the same codebase. +var V2 = v2{} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go index 1498ee144..25da157f1 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go @@ -17,12 +17,10 @@ import ( "fmt" "sort" - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/golang/protobuf/proto" + "github.com/prometheus/client_golang/prometheus/internal" dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus/internal" + "google.golang.org/protobuf/proto" ) // WrapRegistererWith returns a Registerer wrapping the provided @@ -206,7 +204,7 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { constLabels[ln] = lv } // NewDesc will do remaining validations. - newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) + newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) // Propagate errors if there was any. This will override any errer // created by NewDesc above, i.e. earlier errors get precedence. if desc.err != nil { diff --git a/vendor/github.com/prometheus/client_model/go/metrics.pb.go b/vendor/github.com/prometheus/client_model/go/metrics.pb.go index 35904ea19..2f1549075 100644 --- a/vendor/github.com/prometheus/client_model/go/metrics.pb.go +++ b/vendor/github.com/prometheus/client_model/go/metrics.pb.go @@ -1,25 +1,38 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.20.3 // source: io/prometheus/client/metrics.proto package io_prometheus_client import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - timestamp "github.com/golang/protobuf/ptypes/timestamp" - math "math" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type MetricType int32 @@ -38,23 +51,25 @@ const ( MetricType_GAUGE_HISTOGRAM MetricType = 5 ) -var MetricType_name = map[int32]string{ - 0: "COUNTER", - 1: "GAUGE", - 2: "SUMMARY", - 3: "UNTYPED", - 4: "HISTOGRAM", - 5: "GAUGE_HISTOGRAM", -} - -var MetricType_value = map[string]int32{ - "COUNTER": 0, - "GAUGE": 1, - "SUMMARY": 2, - "UNTYPED": 3, - "HISTOGRAM": 4, - "GAUGE_HISTOGRAM": 5, -} +// Enum value maps for MetricType. +var ( + MetricType_name = map[int32]string{ + 0: "COUNTER", + 1: "GAUGE", + 2: "SUMMARY", + 3: "UNTYPED", + 4: "HISTOGRAM", + 5: "GAUGE_HISTOGRAM", + } + MetricType_value = map[string]int32{ + "COUNTER": 0, + "GAUGE": 1, + "SUMMARY": 2, + "UNTYPED": 3, + "HISTOGRAM": 4, + "GAUGE_HISTOGRAM": 5, + } +) func (x MetricType) Enum() *MetricType { p := new(MetricType) @@ -63,449 +78,555 @@ func (x MetricType) Enum() *MetricType { } func (x MetricType) String() string { - return proto.EnumName(MetricType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MetricType) Descriptor() protoreflect.EnumDescriptor { + return file_io_prometheus_client_metrics_proto_enumTypes[0].Descriptor() +} + +func (MetricType) Type() protoreflect.EnumType { + return &file_io_prometheus_client_metrics_proto_enumTypes[0] } -func (x *MetricType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(MetricType_value, data, "MetricType") +func (x MetricType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *MetricType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = MetricType(value) + *x = MetricType(num) return nil } +// Deprecated: Use MetricType.Descriptor instead. func (MetricType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{0} + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{0} } type LabelPair struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *LabelPair) Reset() { *m = LabelPair{} } -func (m *LabelPair) String() string { return proto.CompactTextString(m) } -func (*LabelPair) ProtoMessage() {} -func (*LabelPair) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{0} + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` } -func (m *LabelPair) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LabelPair.Unmarshal(m, b) -} -func (m *LabelPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LabelPair.Marshal(b, m, deterministic) -} -func (m *LabelPair) XXX_Merge(src proto.Message) { - xxx_messageInfo_LabelPair.Merge(m, src) +func (x *LabelPair) Reset() { + *x = LabelPair{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *LabelPair) XXX_Size() int { - return xxx_messageInfo_LabelPair.Size(m) + +func (x *LabelPair) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *LabelPair) XXX_DiscardUnknown() { - xxx_messageInfo_LabelPair.DiscardUnknown(m) + +func (*LabelPair) ProtoMessage() {} + +func (x *LabelPair) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_LabelPair proto.InternalMessageInfo +// Deprecated: Use LabelPair.ProtoReflect.Descriptor instead. +func (*LabelPair) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{0} +} -func (m *LabelPair) GetName() string { - if m != nil && m.Name != nil { - return *m.Name +func (x *LabelPair) GetName() string { + if x != nil && x.Name != nil { + return *x.Name } return "" } -func (m *LabelPair) GetValue() string { - if m != nil && m.Value != nil { - return *m.Value +func (x *LabelPair) GetValue() string { + if x != nil && x.Value != nil { + return *x.Value } return "" } type Gauge struct { - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Gauge) Reset() { *m = Gauge{} } -func (m *Gauge) String() string { return proto.CompactTextString(m) } -func (*Gauge) ProtoMessage() {} -func (*Gauge) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{1} + Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` } -func (m *Gauge) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Gauge.Unmarshal(m, b) -} -func (m *Gauge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Gauge.Marshal(b, m, deterministic) -} -func (m *Gauge) XXX_Merge(src proto.Message) { - xxx_messageInfo_Gauge.Merge(m, src) +func (x *Gauge) Reset() { + *x = Gauge{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Gauge) XXX_Size() int { - return xxx_messageInfo_Gauge.Size(m) + +func (x *Gauge) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Gauge) XXX_DiscardUnknown() { - xxx_messageInfo_Gauge.DiscardUnknown(m) + +func (*Gauge) ProtoMessage() {} + +func (x *Gauge) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Gauge proto.InternalMessageInfo +// Deprecated: Use Gauge.ProtoReflect.Descriptor instead. +func (*Gauge) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{1} +} -func (m *Gauge) GetValue() float64 { - if m != nil && m.Value != nil { - return *m.Value +func (x *Gauge) GetValue() float64 { + if x != nil && x.Value != nil { + return *x.Value } return 0 } type Counter struct { - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` - Exemplar *Exemplar `protobuf:"bytes,2,opt,name=exemplar" json:"exemplar,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Counter) Reset() { *m = Counter{} } -func (m *Counter) String() string { return proto.CompactTextString(m) } -func (*Counter) ProtoMessage() {} -func (*Counter) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{2} + Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` + Exemplar *Exemplar `protobuf:"bytes,2,opt,name=exemplar" json:"exemplar,omitempty"` + CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` } -func (m *Counter) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Counter.Unmarshal(m, b) -} -func (m *Counter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Counter.Marshal(b, m, deterministic) -} -func (m *Counter) XXX_Merge(src proto.Message) { - xxx_messageInfo_Counter.Merge(m, src) +func (x *Counter) Reset() { + *x = Counter{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Counter) XXX_Size() int { - return xxx_messageInfo_Counter.Size(m) + +func (x *Counter) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Counter) XXX_DiscardUnknown() { - xxx_messageInfo_Counter.DiscardUnknown(m) + +func (*Counter) ProtoMessage() {} + +func (x *Counter) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Counter proto.InternalMessageInfo +// Deprecated: Use Counter.ProtoReflect.Descriptor instead. +func (*Counter) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{2} +} -func (m *Counter) GetValue() float64 { - if m != nil && m.Value != nil { - return *m.Value +func (x *Counter) GetValue() float64 { + if x != nil && x.Value != nil { + return *x.Value } return 0 } -func (m *Counter) GetExemplar() *Exemplar { - if m != nil { - return m.Exemplar +func (x *Counter) GetExemplar() *Exemplar { + if x != nil { + return x.Exemplar } return nil } -type Quantile struct { - Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"` - Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *Counter) GetCreatedTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTimestamp + } + return nil } -func (m *Quantile) Reset() { *m = Quantile{} } -func (m *Quantile) String() string { return proto.CompactTextString(m) } -func (*Quantile) ProtoMessage() {} -func (*Quantile) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{3} -} +type Quantile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Quantile) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Quantile.Unmarshal(m, b) -} -func (m *Quantile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Quantile.Marshal(b, m, deterministic) + Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"` + Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` } -func (m *Quantile) XXX_Merge(src proto.Message) { - xxx_messageInfo_Quantile.Merge(m, src) + +func (x *Quantile) Reset() { + *x = Quantile{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Quantile) XXX_Size() int { - return xxx_messageInfo_Quantile.Size(m) + +func (x *Quantile) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Quantile) XXX_DiscardUnknown() { - xxx_messageInfo_Quantile.DiscardUnknown(m) + +func (*Quantile) ProtoMessage() {} + +func (x *Quantile) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Quantile proto.InternalMessageInfo +// Deprecated: Use Quantile.ProtoReflect.Descriptor instead. +func (*Quantile) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{3} +} -func (m *Quantile) GetQuantile() float64 { - if m != nil && m.Quantile != nil { - return *m.Quantile +func (x *Quantile) GetQuantile() float64 { + if x != nil && x.Quantile != nil { + return *x.Quantile } return 0 } -func (m *Quantile) GetValue() float64 { - if m != nil && m.Value != nil { - return *m.Value +func (x *Quantile) GetValue() float64 { + if x != nil && x.Value != nil { + return *x.Value } return 0 } type Summary struct { - SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"` - SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"` - Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"` + SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"` + Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"` + CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` +} + +func (x *Summary) Reset() { + *x = Summary{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Summary) Reset() { *m = Summary{} } -func (m *Summary) String() string { return proto.CompactTextString(m) } -func (*Summary) ProtoMessage() {} -func (*Summary) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{4} +func (x *Summary) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Summary) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Summary.Unmarshal(m, b) -} -func (m *Summary) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Summary.Marshal(b, m, deterministic) -} -func (m *Summary) XXX_Merge(src proto.Message) { - xxx_messageInfo_Summary.Merge(m, src) -} -func (m *Summary) XXX_Size() int { - return xxx_messageInfo_Summary.Size(m) -} -func (m *Summary) XXX_DiscardUnknown() { - xxx_messageInfo_Summary.DiscardUnknown(m) +func (*Summary) ProtoMessage() {} + +func (x *Summary) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Summary proto.InternalMessageInfo +// Deprecated: Use Summary.ProtoReflect.Descriptor instead. +func (*Summary) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{4} +} -func (m *Summary) GetSampleCount() uint64 { - if m != nil && m.SampleCount != nil { - return *m.SampleCount +func (x *Summary) GetSampleCount() uint64 { + if x != nil && x.SampleCount != nil { + return *x.SampleCount } return 0 } -func (m *Summary) GetSampleSum() float64 { - if m != nil && m.SampleSum != nil { - return *m.SampleSum +func (x *Summary) GetSampleSum() float64 { + if x != nil && x.SampleSum != nil { + return *x.SampleSum } return 0 } -func (m *Summary) GetQuantile() []*Quantile { - if m != nil { - return m.Quantile +func (x *Summary) GetQuantile() []*Quantile { + if x != nil { + return x.Quantile } return nil } -type Untyped struct { - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *Summary) GetCreatedTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTimestamp + } + return nil } -func (m *Untyped) Reset() { *m = Untyped{} } -func (m *Untyped) String() string { return proto.CompactTextString(m) } -func (*Untyped) ProtoMessage() {} -func (*Untyped) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{5} -} +type Untyped struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Untyped) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Untyped.Unmarshal(m, b) + Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` } -func (m *Untyped) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Untyped.Marshal(b, m, deterministic) -} -func (m *Untyped) XXX_Merge(src proto.Message) { - xxx_messageInfo_Untyped.Merge(m, src) + +func (x *Untyped) Reset() { + *x = Untyped{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Untyped) XXX_Size() int { - return xxx_messageInfo_Untyped.Size(m) + +func (x *Untyped) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Untyped) XXX_DiscardUnknown() { - xxx_messageInfo_Untyped.DiscardUnknown(m) + +func (*Untyped) ProtoMessage() {} + +func (x *Untyped) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Untyped proto.InternalMessageInfo +// Deprecated: Use Untyped.ProtoReflect.Descriptor instead. +func (*Untyped) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{5} +} -func (m *Untyped) GetValue() float64 { - if m != nil && m.Value != nil { - return *m.Value +func (x *Untyped) GetValue() float64 { + if x != nil && x.Value != nil { + return *x.Value } return 0 } type Histogram struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"` - SampleCountFloat *float64 `protobuf:"fixed64,4,opt,name=sample_count_float,json=sampleCountFloat" json:"sample_count_float,omitempty"` + SampleCountFloat *float64 `protobuf:"fixed64,4,opt,name=sample_count_float,json=sampleCountFloat" json:"sample_count_float,omitempty"` // Overrides sample_count if > 0. SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"` // Buckets for the conventional histogram. - Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"` + Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"` // Ordered in increasing order of upper_bound, +Inf bucket is optional. + CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` // schema defines the bucket schema. Currently, valid numbers are -4 <= n <= 8. // They are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and // then each power of two is divided into 2^n logarithmic buckets. // Or in other words, each bucket boundary is the previous boundary times 2^(2^-n). // In the future, more bucket schemas may be added using numbers < -4 or > 8. Schema *int32 `protobuf:"zigzag32,5,opt,name=schema" json:"schema,omitempty"` - ZeroThreshold *float64 `protobuf:"fixed64,6,opt,name=zero_threshold,json=zeroThreshold" json:"zero_threshold,omitempty"` - ZeroCount *uint64 `protobuf:"varint,7,opt,name=zero_count,json=zeroCount" json:"zero_count,omitempty"` - ZeroCountFloat *float64 `protobuf:"fixed64,8,opt,name=zero_count_float,json=zeroCountFloat" json:"zero_count_float,omitempty"` + ZeroThreshold *float64 `protobuf:"fixed64,6,opt,name=zero_threshold,json=zeroThreshold" json:"zero_threshold,omitempty"` // Breadth of the zero bucket. + ZeroCount *uint64 `protobuf:"varint,7,opt,name=zero_count,json=zeroCount" json:"zero_count,omitempty"` // Count in zero bucket. + ZeroCountFloat *float64 `protobuf:"fixed64,8,opt,name=zero_count_float,json=zeroCountFloat" json:"zero_count_float,omitempty"` // Overrides sb_zero_count if > 0. // Negative buckets for the native histogram. NegativeSpan []*BucketSpan `protobuf:"bytes,9,rep,name=negative_span,json=negativeSpan" json:"negative_span,omitempty"` // Use either "negative_delta" or "negative_count", the former for // regular histograms with integer counts, the latter for float // histograms. - NegativeDelta []int64 `protobuf:"zigzag64,10,rep,name=negative_delta,json=negativeDelta" json:"negative_delta,omitempty"` - NegativeCount []float64 `protobuf:"fixed64,11,rep,name=negative_count,json=negativeCount" json:"negative_count,omitempty"` + NegativeDelta []int64 `protobuf:"zigzag64,10,rep,name=negative_delta,json=negativeDelta" json:"negative_delta,omitempty"` // Count delta of each bucket compared to previous one (or to zero for 1st bucket). + NegativeCount []float64 `protobuf:"fixed64,11,rep,name=negative_count,json=negativeCount" json:"negative_count,omitempty"` // Absolute count of each bucket. // Positive buckets for the native histogram. + // Use a no-op span (offset 0, length 0) for a native histogram without any + // observations yet and with a zero_threshold of 0. Otherwise, it would be + // indistinguishable from a classic histogram. PositiveSpan []*BucketSpan `protobuf:"bytes,12,rep,name=positive_span,json=positiveSpan" json:"positive_span,omitempty"` // Use either "positive_delta" or "positive_count", the former for // regular histograms with integer counts, the latter for float // histograms. - PositiveDelta []int64 `protobuf:"zigzag64,13,rep,name=positive_delta,json=positiveDelta" json:"positive_delta,omitempty"` - PositiveCount []float64 `protobuf:"fixed64,14,rep,name=positive_count,json=positiveCount" json:"positive_count,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PositiveDelta []int64 `protobuf:"zigzag64,13,rep,name=positive_delta,json=positiveDelta" json:"positive_delta,omitempty"` // Count delta of each bucket compared to previous one (or to zero for 1st bucket). + PositiveCount []float64 `protobuf:"fixed64,14,rep,name=positive_count,json=positiveCount" json:"positive_count,omitempty"` // Absolute count of each bucket. + // Only used for native histograms. These exemplars MUST have a timestamp. + Exemplars []*Exemplar `protobuf:"bytes,16,rep,name=exemplars" json:"exemplars,omitempty"` +} + +func (x *Histogram) Reset() { + *x = Histogram{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Histogram) Reset() { *m = Histogram{} } -func (m *Histogram) String() string { return proto.CompactTextString(m) } -func (*Histogram) ProtoMessage() {} -func (*Histogram) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{6} +func (x *Histogram) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Histogram) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Histogram.Unmarshal(m, b) -} -func (m *Histogram) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Histogram.Marshal(b, m, deterministic) -} -func (m *Histogram) XXX_Merge(src proto.Message) { - xxx_messageInfo_Histogram.Merge(m, src) -} -func (m *Histogram) XXX_Size() int { - return xxx_messageInfo_Histogram.Size(m) -} -func (m *Histogram) XXX_DiscardUnknown() { - xxx_messageInfo_Histogram.DiscardUnknown(m) +func (*Histogram) ProtoMessage() {} + +func (x *Histogram) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Histogram proto.InternalMessageInfo +// Deprecated: Use Histogram.ProtoReflect.Descriptor instead. +func (*Histogram) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{6} +} -func (m *Histogram) GetSampleCount() uint64 { - if m != nil && m.SampleCount != nil { - return *m.SampleCount +func (x *Histogram) GetSampleCount() uint64 { + if x != nil && x.SampleCount != nil { + return *x.SampleCount } return 0 } -func (m *Histogram) GetSampleCountFloat() float64 { - if m != nil && m.SampleCountFloat != nil { - return *m.SampleCountFloat +func (x *Histogram) GetSampleCountFloat() float64 { + if x != nil && x.SampleCountFloat != nil { + return *x.SampleCountFloat } return 0 } -func (m *Histogram) GetSampleSum() float64 { - if m != nil && m.SampleSum != nil { - return *m.SampleSum +func (x *Histogram) GetSampleSum() float64 { + if x != nil && x.SampleSum != nil { + return *x.SampleSum } return 0 } -func (m *Histogram) GetBucket() []*Bucket { - if m != nil { - return m.Bucket +func (x *Histogram) GetBucket() []*Bucket { + if x != nil { + return x.Bucket + } + return nil +} + +func (x *Histogram) GetCreatedTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTimestamp } return nil } -func (m *Histogram) GetSchema() int32 { - if m != nil && m.Schema != nil { - return *m.Schema +func (x *Histogram) GetSchema() int32 { + if x != nil && x.Schema != nil { + return *x.Schema } return 0 } -func (m *Histogram) GetZeroThreshold() float64 { - if m != nil && m.ZeroThreshold != nil { - return *m.ZeroThreshold +func (x *Histogram) GetZeroThreshold() float64 { + if x != nil && x.ZeroThreshold != nil { + return *x.ZeroThreshold } return 0 } -func (m *Histogram) GetZeroCount() uint64 { - if m != nil && m.ZeroCount != nil { - return *m.ZeroCount +func (x *Histogram) GetZeroCount() uint64 { + if x != nil && x.ZeroCount != nil { + return *x.ZeroCount } return 0 } -func (m *Histogram) GetZeroCountFloat() float64 { - if m != nil && m.ZeroCountFloat != nil { - return *m.ZeroCountFloat +func (x *Histogram) GetZeroCountFloat() float64 { + if x != nil && x.ZeroCountFloat != nil { + return *x.ZeroCountFloat } return 0 } -func (m *Histogram) GetNegativeSpan() []*BucketSpan { - if m != nil { - return m.NegativeSpan +func (x *Histogram) GetNegativeSpan() []*BucketSpan { + if x != nil { + return x.NegativeSpan + } + return nil +} + +func (x *Histogram) GetNegativeDelta() []int64 { + if x != nil { + return x.NegativeDelta } return nil } -func (m *Histogram) GetNegativeDelta() []int64 { - if m != nil { - return m.NegativeDelta +func (x *Histogram) GetNegativeCount() []float64 { + if x != nil { + return x.NegativeCount } return nil } -func (m *Histogram) GetNegativeCount() []float64 { - if m != nil { - return m.NegativeCount +func (x *Histogram) GetPositiveSpan() []*BucketSpan { + if x != nil { + return x.PositiveSpan } return nil } -func (m *Histogram) GetPositiveSpan() []*BucketSpan { - if m != nil { - return m.PositiveSpan +func (x *Histogram) GetPositiveDelta() []int64 { + if x != nil { + return x.PositiveDelta } return nil } -func (m *Histogram) GetPositiveDelta() []int64 { - if m != nil { - return m.PositiveDelta +func (x *Histogram) GetPositiveCount() []float64 { + if x != nil { + return x.PositiveCount } return nil } -func (m *Histogram) GetPositiveCount() []float64 { - if m != nil { - return m.PositiveCount +func (x *Histogram) GetExemplars() []*Exemplar { + if x != nil { + return x.Exemplars } return nil } @@ -513,64 +634,72 @@ func (m *Histogram) GetPositiveCount() []float64 { // A Bucket of a conventional histogram, each of which is treated as // an individual counter-like time series by Prometheus. type Bucket struct { - CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count,json=cumulativeCount" json:"cumulative_count,omitempty"` - CumulativeCountFloat *float64 `protobuf:"fixed64,4,opt,name=cumulative_count_float,json=cumulativeCountFloat" json:"cumulative_count_float,omitempty"` - UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound,json=upperBound" json:"upper_bound,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count,json=cumulativeCount" json:"cumulative_count,omitempty"` // Cumulative in increasing order. + CumulativeCountFloat *float64 `protobuf:"fixed64,4,opt,name=cumulative_count_float,json=cumulativeCountFloat" json:"cumulative_count_float,omitempty"` // Overrides cumulative_count if > 0. + UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound,json=upperBound" json:"upper_bound,omitempty"` // Inclusive. Exemplar *Exemplar `protobuf:"bytes,3,opt,name=exemplar" json:"exemplar,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` } -func (m *Bucket) Reset() { *m = Bucket{} } -func (m *Bucket) String() string { return proto.CompactTextString(m) } -func (*Bucket) ProtoMessage() {} -func (*Bucket) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{7} +func (x *Bucket) Reset() { + *x = Bucket{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Bucket) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Bucket.Unmarshal(m, b) -} -func (m *Bucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Bucket.Marshal(b, m, deterministic) +func (x *Bucket) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Bucket) XXX_Merge(src proto.Message) { - xxx_messageInfo_Bucket.Merge(m, src) -} -func (m *Bucket) XXX_Size() int { - return xxx_messageInfo_Bucket.Size(m) -} -func (m *Bucket) XXX_DiscardUnknown() { - xxx_messageInfo_Bucket.DiscardUnknown(m) + +func (*Bucket) ProtoMessage() {} + +func (x *Bucket) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Bucket proto.InternalMessageInfo +// Deprecated: Use Bucket.ProtoReflect.Descriptor instead. +func (*Bucket) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{7} +} -func (m *Bucket) GetCumulativeCount() uint64 { - if m != nil && m.CumulativeCount != nil { - return *m.CumulativeCount +func (x *Bucket) GetCumulativeCount() uint64 { + if x != nil && x.CumulativeCount != nil { + return *x.CumulativeCount } return 0 } -func (m *Bucket) GetCumulativeCountFloat() float64 { - if m != nil && m.CumulativeCountFloat != nil { - return *m.CumulativeCountFloat +func (x *Bucket) GetCumulativeCountFloat() float64 { + if x != nil && x.CumulativeCountFloat != nil { + return *x.CumulativeCountFloat } return 0 } -func (m *Bucket) GetUpperBound() float64 { - if m != nil && m.UpperBound != nil { - return *m.UpperBound +func (x *Bucket) GetUpperBound() float64 { + if x != nil && x.UpperBound != nil { + return *x.UpperBound } return 0 } -func (m *Bucket) GetExemplar() *Exemplar { - if m != nil { - return m.Exemplar +func (x *Bucket) GetExemplar() *Exemplar { + if x != nil { + return x.Exemplar } return nil } @@ -582,333 +711,689 @@ func (m *Bucket) GetExemplar() *Exemplar { // structured here (with all the buckets in a single array separate // from the Spans). type BucketSpan struct { - Offset *int32 `protobuf:"zigzag32,1,opt,name=offset" json:"offset,omitempty"` - Length *uint32 `protobuf:"varint,2,opt,name=length" json:"length,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *BucketSpan) Reset() { *m = BucketSpan{} } -func (m *BucketSpan) String() string { return proto.CompactTextString(m) } -func (*BucketSpan) ProtoMessage() {} -func (*BucketSpan) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{8} + Offset *int32 `protobuf:"zigzag32,1,opt,name=offset" json:"offset,omitempty"` // Gap to previous span, or starting point for 1st span (which can be negative). + Length *uint32 `protobuf:"varint,2,opt,name=length" json:"length,omitempty"` // Length of consecutive buckets. } -func (m *BucketSpan) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_BucketSpan.Unmarshal(m, b) -} -func (m *BucketSpan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_BucketSpan.Marshal(b, m, deterministic) -} -func (m *BucketSpan) XXX_Merge(src proto.Message) { - xxx_messageInfo_BucketSpan.Merge(m, src) +func (x *BucketSpan) Reset() { + *x = BucketSpan{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *BucketSpan) XXX_Size() int { - return xxx_messageInfo_BucketSpan.Size(m) + +func (x *BucketSpan) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *BucketSpan) XXX_DiscardUnknown() { - xxx_messageInfo_BucketSpan.DiscardUnknown(m) + +func (*BucketSpan) ProtoMessage() {} + +func (x *BucketSpan) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_BucketSpan proto.InternalMessageInfo +// Deprecated: Use BucketSpan.ProtoReflect.Descriptor instead. +func (*BucketSpan) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{8} +} -func (m *BucketSpan) GetOffset() int32 { - if m != nil && m.Offset != nil { - return *m.Offset +func (x *BucketSpan) GetOffset() int32 { + if x != nil && x.Offset != nil { + return *x.Offset } return 0 } -func (m *BucketSpan) GetLength() uint32 { - if m != nil && m.Length != nil { - return *m.Length +func (x *BucketSpan) GetLength() uint32 { + if x != nil && x.Length != nil { + return *x.Length } return 0 } type Exemplar struct { - Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` - Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` - Timestamp *timestamp.Timestamp `protobuf:"bytes,3,opt,name=timestamp" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Exemplar) Reset() { *m = Exemplar{} } -func (m *Exemplar) String() string { return proto.CompactTextString(m) } -func (*Exemplar) ProtoMessage() {} -func (*Exemplar) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{9} + Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` + Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timestamp" json:"timestamp,omitempty"` // OpenMetrics-style. } -func (m *Exemplar) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Exemplar.Unmarshal(m, b) -} -func (m *Exemplar) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Exemplar.Marshal(b, m, deterministic) -} -func (m *Exemplar) XXX_Merge(src proto.Message) { - xxx_messageInfo_Exemplar.Merge(m, src) +func (x *Exemplar) Reset() { + *x = Exemplar{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Exemplar) XXX_Size() int { - return xxx_messageInfo_Exemplar.Size(m) + +func (x *Exemplar) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Exemplar) XXX_DiscardUnknown() { - xxx_messageInfo_Exemplar.DiscardUnknown(m) + +func (*Exemplar) ProtoMessage() {} + +func (x *Exemplar) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Exemplar proto.InternalMessageInfo +// Deprecated: Use Exemplar.ProtoReflect.Descriptor instead. +func (*Exemplar) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{9} +} -func (m *Exemplar) GetLabel() []*LabelPair { - if m != nil { - return m.Label +func (x *Exemplar) GetLabel() []*LabelPair { + if x != nil { + return x.Label } return nil } -func (m *Exemplar) GetValue() float64 { - if m != nil && m.Value != nil { - return *m.Value +func (x *Exemplar) GetValue() float64 { + if x != nil && x.Value != nil { + return *x.Value } return 0 } -func (m *Exemplar) GetTimestamp() *timestamp.Timestamp { - if m != nil { - return m.Timestamp +func (x *Exemplar) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp } return nil } type Metric struct { - Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` - Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"` - Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"` - Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"` - Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"` - Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"` - TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms,json=timestampMs" json:"timestamp_ms,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Metric) Reset() { *m = Metric{} } -func (m *Metric) String() string { return proto.CompactTextString(m) } -func (*Metric) ProtoMessage() {} -func (*Metric) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{10} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` + Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"` + Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"` + Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"` + Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"` + Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"` + TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms,json=timestampMs" json:"timestamp_ms,omitempty"` +} + +func (x *Metric) Reset() { + *x = Metric{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Metric) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Metric.Unmarshal(m, b) +func (x *Metric) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Metric) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Metric.Marshal(b, m, deterministic) -} -func (m *Metric) XXX_Merge(src proto.Message) { - xxx_messageInfo_Metric.Merge(m, src) -} -func (m *Metric) XXX_Size() int { - return xxx_messageInfo_Metric.Size(m) -} -func (m *Metric) XXX_DiscardUnknown() { - xxx_messageInfo_Metric.DiscardUnknown(m) + +func (*Metric) ProtoMessage() {} + +func (x *Metric) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Metric proto.InternalMessageInfo +// Deprecated: Use Metric.ProtoReflect.Descriptor instead. +func (*Metric) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{10} +} -func (m *Metric) GetLabel() []*LabelPair { - if m != nil { - return m.Label +func (x *Metric) GetLabel() []*LabelPair { + if x != nil { + return x.Label } return nil } -func (m *Metric) GetGauge() *Gauge { - if m != nil { - return m.Gauge +func (x *Metric) GetGauge() *Gauge { + if x != nil { + return x.Gauge } return nil } -func (m *Metric) GetCounter() *Counter { - if m != nil { - return m.Counter +func (x *Metric) GetCounter() *Counter { + if x != nil { + return x.Counter } return nil } -func (m *Metric) GetSummary() *Summary { - if m != nil { - return m.Summary +func (x *Metric) GetSummary() *Summary { + if x != nil { + return x.Summary } return nil } -func (m *Metric) GetUntyped() *Untyped { - if m != nil { - return m.Untyped +func (x *Metric) GetUntyped() *Untyped { + if x != nil { + return x.Untyped } return nil } -func (m *Metric) GetHistogram() *Histogram { - if m != nil { - return m.Histogram +func (x *Metric) GetHistogram() *Histogram { + if x != nil { + return x.Histogram } return nil } -func (m *Metric) GetTimestampMs() int64 { - if m != nil && m.TimestampMs != nil { - return *m.TimestampMs +func (x *Metric) GetTimestampMs() int64 { + if x != nil && x.TimestampMs != nil { + return *x.TimestampMs } return 0 } type MetricFamily struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"` - Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"` - Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *MetricFamily) Reset() { *m = MetricFamily{} } -func (m *MetricFamily) String() string { return proto.CompactTextString(m) } -func (*MetricFamily) ProtoMessage() {} -func (*MetricFamily) Descriptor() ([]byte, []int) { - return fileDescriptor_d1e5ddb18987a258, []int{11} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"` + Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"` + Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"` + Unit *string `protobuf:"bytes,5,opt,name=unit" json:"unit,omitempty"` +} + +func (x *MetricFamily) Reset() { + *x = MetricFamily{} + if protoimpl.UnsafeEnabled { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *MetricFamily) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_MetricFamily.Unmarshal(m, b) -} -func (m *MetricFamily) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_MetricFamily.Marshal(b, m, deterministic) +func (x *MetricFamily) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *MetricFamily) XXX_Merge(src proto.Message) { - xxx_messageInfo_MetricFamily.Merge(m, src) -} -func (m *MetricFamily) XXX_Size() int { - return xxx_messageInfo_MetricFamily.Size(m) -} -func (m *MetricFamily) XXX_DiscardUnknown() { - xxx_messageInfo_MetricFamily.DiscardUnknown(m) + +func (*MetricFamily) ProtoMessage() {} + +func (x *MetricFamily) ProtoReflect() protoreflect.Message { + mi := &file_io_prometheus_client_metrics_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_MetricFamily proto.InternalMessageInfo +// Deprecated: Use MetricFamily.ProtoReflect.Descriptor instead. +func (*MetricFamily) Descriptor() ([]byte, []int) { + return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{11} +} -func (m *MetricFamily) GetName() string { - if m != nil && m.Name != nil { - return *m.Name +func (x *MetricFamily) GetName() string { + if x != nil && x.Name != nil { + return *x.Name } return "" } -func (m *MetricFamily) GetHelp() string { - if m != nil && m.Help != nil { - return *m.Help +func (x *MetricFamily) GetHelp() string { + if x != nil && x.Help != nil { + return *x.Help } return "" } -func (m *MetricFamily) GetType() MetricType { - if m != nil && m.Type != nil { - return *m.Type +func (x *MetricFamily) GetType() MetricType { + if x != nil && x.Type != nil { + return *x.Type } return MetricType_COUNTER } -func (m *MetricFamily) GetMetric() []*Metric { - if m != nil { - return m.Metric +func (x *MetricFamily) GetMetric() []*Metric { + if x != nil { + return x.Metric } return nil } -func init() { - proto.RegisterEnum("io.prometheus.client.MetricType", MetricType_name, MetricType_value) - proto.RegisterType((*LabelPair)(nil), "io.prometheus.client.LabelPair") - proto.RegisterType((*Gauge)(nil), "io.prometheus.client.Gauge") - proto.RegisterType((*Counter)(nil), "io.prometheus.client.Counter") - proto.RegisterType((*Quantile)(nil), "io.prometheus.client.Quantile") - proto.RegisterType((*Summary)(nil), "io.prometheus.client.Summary") - proto.RegisterType((*Untyped)(nil), "io.prometheus.client.Untyped") - proto.RegisterType((*Histogram)(nil), "io.prometheus.client.Histogram") - proto.RegisterType((*Bucket)(nil), "io.prometheus.client.Bucket") - proto.RegisterType((*BucketSpan)(nil), "io.prometheus.client.BucketSpan") - proto.RegisterType((*Exemplar)(nil), "io.prometheus.client.Exemplar") - proto.RegisterType((*Metric)(nil), "io.prometheus.client.Metric") - proto.RegisterType((*MetricFamily)(nil), "io.prometheus.client.MetricFamily") -} - -func init() { - proto.RegisterFile("io/prometheus/client/metrics.proto", fileDescriptor_d1e5ddb18987a258) -} - -var fileDescriptor_d1e5ddb18987a258 = []byte{ - // 896 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x8e, 0xdb, 0x44, - 0x18, 0xc5, 0x9b, 0x5f, 0x7f, 0xd9, 0x6c, 0xd3, 0x61, 0x55, 0x59, 0x0b, 0xcb, 0x06, 0x4b, 0x48, - 0x0b, 0x42, 0x8e, 0x40, 0x5b, 0x81, 0x0a, 0x5c, 0xec, 0xb6, 0xe9, 0x16, 0x89, 0xb4, 0x65, 0x92, - 0x5c, 0x14, 0x2e, 0xac, 0x49, 0x32, 0xeb, 0x58, 0x78, 0x3c, 0xc6, 0x1e, 0x57, 0x2c, 0x2f, 0xc0, - 0x35, 0xaf, 0xc0, 0xc3, 0xf0, 0x22, 0x3c, 0x08, 0x68, 0xfe, 0xec, 0xdd, 0xe2, 0x94, 0xd2, 0x3b, - 0x7f, 0x67, 0xce, 0xf7, 0xcd, 0x39, 0xe3, 0xc9, 0x71, 0xc0, 0x8f, 0xf9, 0x24, 0xcb, 0x39, 0xa3, - 0x62, 0x4b, 0xcb, 0x62, 0xb2, 0x4e, 0x62, 0x9a, 0x8a, 0x09, 0xa3, 0x22, 0x8f, 0xd7, 0x45, 0x90, - 0xe5, 0x5c, 0x70, 0x74, 0x18, 0xf3, 0xa0, 0xe6, 0x04, 0x9a, 0x73, 0x74, 0x12, 0x71, 0x1e, 0x25, - 0x74, 0xa2, 0x38, 0xab, 0xf2, 0x6a, 0x22, 0x62, 0x46, 0x0b, 0x41, 0x58, 0xa6, 0xdb, 0xfc, 0xfb, - 0xe0, 0x7e, 0x47, 0x56, 0x34, 0x79, 0x4e, 0xe2, 0x1c, 0x21, 0x68, 0xa7, 0x84, 0x51, 0xcf, 0x19, - 0x3b, 0xa7, 0x2e, 0x56, 0xcf, 0xe8, 0x10, 0x3a, 0x2f, 0x49, 0x52, 0x52, 0x6f, 0x4f, 0x81, 0xba, - 0xf0, 0x8f, 0xa1, 0x73, 0x49, 0xca, 0xe8, 0xc6, 0xb2, 0xec, 0x71, 0xec, 0xf2, 0x8f, 0xd0, 0x7b, - 0xc8, 0xcb, 0x54, 0xd0, 0xbc, 0x99, 0x80, 0x1e, 0x40, 0x9f, 0xfe, 0x42, 0x59, 0x96, 0x90, 0x5c, - 0x0d, 0x1e, 0x7c, 0xfe, 0x41, 0xd0, 0x64, 0x20, 0x98, 0x1a, 0x16, 0xae, 0xf8, 0xfe, 0xd7, 0xd0, - 0xff, 0xbe, 0x24, 0xa9, 0x88, 0x13, 0x8a, 0x8e, 0xa0, 0xff, 0xb3, 0x79, 0x36, 0x1b, 0x54, 0xf5, - 0x6d, 0xe5, 0x95, 0xb4, 0xdf, 0x1c, 0xe8, 0xcd, 0x4b, 0xc6, 0x48, 0x7e, 0x8d, 0x3e, 0x84, 0xfd, - 0x82, 0xb0, 0x2c, 0xa1, 0xe1, 0x5a, 0xaa, 0x55, 0x13, 0xda, 0x78, 0xa0, 0x31, 0x65, 0x00, 0x1d, - 0x03, 0x18, 0x4a, 0x51, 0x32, 0x33, 0xc9, 0xd5, 0xc8, 0xbc, 0x64, 0xd2, 0x47, 0xb5, 0x7f, 0x6b, - 0xdc, 0xda, 0xed, 0xc3, 0x2a, 0xae, 0xf5, 0xf9, 0x27, 0xd0, 0x5b, 0xa6, 0xe2, 0x3a, 0xa3, 0x9b, - 0x1d, 0xa7, 0xf8, 0x57, 0x1b, 0xdc, 0x27, 0x71, 0x21, 0x78, 0x94, 0x13, 0xf6, 0x26, 0x62, 0x3f, - 0x05, 0x74, 0x93, 0x12, 0x5e, 0x25, 0x9c, 0x08, 0xaf, 0xad, 0x66, 0x8e, 0x6e, 0x10, 0x1f, 0x4b, - 0xfc, 0xbf, 0xac, 0x9d, 0x41, 0x77, 0x55, 0xae, 0x7f, 0xa2, 0xc2, 0x18, 0x7b, 0xbf, 0xd9, 0xd8, - 0x85, 0xe2, 0x60, 0xc3, 0x45, 0xf7, 0xa0, 0x5b, 0xac, 0xb7, 0x94, 0x11, 0xaf, 0x33, 0x76, 0x4e, - 0xef, 0x62, 0x53, 0xa1, 0x8f, 0xe0, 0xe0, 0x57, 0x9a, 0xf3, 0x50, 0x6c, 0x73, 0x5a, 0x6c, 0x79, - 0xb2, 0xf1, 0xba, 0x6a, 0xc3, 0xa1, 0x44, 0x17, 0x16, 0x94, 0x9a, 0x14, 0x4d, 0x5b, 0xec, 0x29, - 0x8b, 0xae, 0x44, 0xb4, 0xc1, 0x53, 0x18, 0xd5, 0xcb, 0xc6, 0x5e, 0x5f, 0xcd, 0x39, 0xa8, 0x48, - 0xda, 0xdc, 0x14, 0x86, 0x29, 0x8d, 0x88, 0x88, 0x5f, 0xd2, 0xb0, 0xc8, 0x48, 0xea, 0xb9, 0xca, - 0xc4, 0xf8, 0x75, 0x26, 0xe6, 0x19, 0x49, 0xf1, 0xbe, 0x6d, 0x93, 0x95, 0x94, 0x5d, 0x8d, 0xd9, - 0xd0, 0x44, 0x10, 0x0f, 0xc6, 0xad, 0x53, 0x84, 0xab, 0xe1, 0x8f, 0x24, 0x78, 0x8b, 0xa6, 0xa5, - 0x0f, 0xc6, 0x2d, 0xe9, 0xce, 0xa2, 0x5a, 0xfe, 0x14, 0x86, 0x19, 0x2f, 0xe2, 0x5a, 0xd4, 0xfe, - 0x9b, 0x8a, 0xb2, 0x6d, 0x56, 0x54, 0x35, 0x46, 0x8b, 0x1a, 0x6a, 0x51, 0x16, 0xad, 0x44, 0x55, - 0x34, 0x2d, 0xea, 0x40, 0x8b, 0xb2, 0xa8, 0x12, 0xe5, 0xff, 0xe9, 0x40, 0x57, 0x6f, 0x85, 0x3e, - 0x86, 0xd1, 0xba, 0x64, 0x65, 0x72, 0xd3, 0x88, 0xbe, 0x66, 0x77, 0x6a, 0x5c, 0x5b, 0x39, 0x83, - 0x7b, 0xaf, 0x52, 0x6f, 0x5d, 0xb7, 0xc3, 0x57, 0x1a, 0xf4, 0x5b, 0x39, 0x81, 0x41, 0x99, 0x65, - 0x34, 0x0f, 0x57, 0xbc, 0x4c, 0x37, 0xe6, 0xce, 0x81, 0x82, 0x2e, 0x24, 0x72, 0x2b, 0x17, 0x5a, - 0xff, 0x3b, 0x17, 0xa0, 0x3e, 0x32, 0x79, 0x11, 0xf9, 0xd5, 0x55, 0x41, 0xb5, 0x83, 0xbb, 0xd8, - 0x54, 0x12, 0x4f, 0x68, 0x1a, 0x89, 0xad, 0xda, 0x7d, 0x88, 0x4d, 0xe5, 0xff, 0xee, 0x40, 0xdf, - 0x0e, 0x45, 0xf7, 0xa1, 0x93, 0xc8, 0x54, 0xf4, 0x1c, 0xf5, 0x82, 0x4e, 0x9a, 0x35, 0x54, 0xc1, - 0x89, 0x35, 0xbb, 0x39, 0x71, 0xd0, 0x97, 0xe0, 0x56, 0xa9, 0x6b, 0x4c, 0x1d, 0x05, 0x3a, 0x97, - 0x03, 0x9b, 0xcb, 0xc1, 0xc2, 0x32, 0x70, 0x4d, 0xf6, 0xff, 0xde, 0x83, 0xee, 0x4c, 0xa5, 0xfc, - 0xdb, 0x2a, 0xfa, 0x0c, 0x3a, 0x91, 0xcc, 0x69, 0x13, 0xb2, 0xef, 0x35, 0xb7, 0xa9, 0x28, 0xc7, - 0x9a, 0x89, 0xbe, 0x80, 0xde, 0x5a, 0x67, 0xb7, 0x11, 0x7b, 0xdc, 0xdc, 0x64, 0x02, 0x1e, 0x5b, - 0xb6, 0x6c, 0x2c, 0x74, 0xb0, 0xaa, 0x3b, 0xb0, 0xb3, 0xd1, 0xa4, 0x2f, 0xb6, 0x6c, 0xd9, 0x58, - 0xea, 0x20, 0x54, 0xa1, 0xb1, 0xb3, 0xd1, 0xa4, 0x25, 0xb6, 0x6c, 0xf4, 0x0d, 0xb8, 0x5b, 0x9b, - 0x8f, 0x2a, 0x2c, 0x76, 0x1e, 0x4c, 0x15, 0xa3, 0xb8, 0xee, 0x90, 0x89, 0x5a, 0x9d, 0x75, 0xc8, - 0x0a, 0x95, 0x48, 0x2d, 0x3c, 0xa8, 0xb0, 0x59, 0xe1, 0xff, 0xe1, 0xc0, 0xbe, 0x7e, 0x03, 0x8f, - 0x09, 0x8b, 0x93, 0xeb, 0xc6, 0x4f, 0x24, 0x82, 0xf6, 0x96, 0x26, 0x99, 0xf9, 0x42, 0xaa, 0x67, - 0x74, 0x06, 0x6d, 0xa9, 0x51, 0x1d, 0xe1, 0xc1, 0xae, 0x5f, 0xb8, 0x9e, 0xbc, 0xb8, 0xce, 0x28, - 0x56, 0x6c, 0x99, 0xb9, 0xfa, 0xab, 0xee, 0xb5, 0x5f, 0x97, 0xb9, 0xba, 0x0f, 0x1b, 0xee, 0x27, - 0x2b, 0x80, 0x7a, 0x12, 0x1a, 0x40, 0xef, 0xe1, 0xb3, 0xe5, 0xd3, 0xc5, 0x14, 0x8f, 0xde, 0x41, - 0x2e, 0x74, 0x2e, 0xcf, 0x97, 0x97, 0xd3, 0x91, 0x23, 0xf1, 0xf9, 0x72, 0x36, 0x3b, 0xc7, 0x2f, - 0x46, 0x7b, 0xb2, 0x58, 0x3e, 0x5d, 0xbc, 0x78, 0x3e, 0x7d, 0x34, 0x6a, 0xa1, 0x21, 0xb8, 0x4f, - 0xbe, 0x9d, 0x2f, 0x9e, 0x5d, 0xe2, 0xf3, 0xd9, 0xa8, 0x8d, 0xde, 0x85, 0x3b, 0xaa, 0x27, 0xac, - 0xc1, 0xce, 0x05, 0x86, 0xc6, 0x3f, 0x18, 0x3f, 0x3c, 0x88, 0x62, 0xb1, 0x2d, 0x57, 0xc1, 0x9a, - 0xb3, 0x7f, 0xff, 0x45, 0x09, 0x19, 0xdf, 0xd0, 0x64, 0x12, 0xf1, 0xaf, 0x62, 0x1e, 0xd6, 0xab, - 0xa1, 0x5e, 0xfd, 0x27, 0x00, 0x00, 0xff, 0xff, 0x16, 0x77, 0x81, 0x98, 0xd7, 0x08, 0x00, 0x00, +func (x *MetricFamily) GetUnit() string { + if x != nil && x.Unit != nil { + return *x.Unit + } + return "" +} + +var File_io_prometheus_client_metrics_proto protoreflect.FileDescriptor + +var file_io_prometheus_client_metrics_proto_rawDesc = []byte{ + 0x0a, 0x22, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x09, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x47, 0x61, 0x75, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, + 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12, + 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, + 0x74, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x07, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, + 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x53, 0x75, 0x6d, 0x12, 0x3a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x51, 0x75, + 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x12, 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x1f, 0x0a, 0x07, 0x55, 0x6e, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xea, 0x05, 0x0a, 0x09, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x34, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, + 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x47, + 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, + 0x25, 0x0a, 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x7a, 0x65, 0x72, 0x6f, 0x54, 0x68, 0x72, + 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x7a, 0x65, 0x72, 0x6f, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, + 0x45, 0x0a, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0d, + 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x25, 0x0a, + 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x0b, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, + 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0d, 0x20, + 0x03, 0x28, 0x12, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, + 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x78, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, + 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x09, 0x65, 0x78, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x75, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, + 0x16, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, + 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, + 0x6f, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, 0x72, 0x42, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, + 0x22, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x91, + 0x01, 0x0a, 0x08, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12, 0x35, 0x0a, 0x05, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, + 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x22, 0xff, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x35, 0x0a, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, + 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x61, 0x75, 0x67, 0x65, + 0x52, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, + 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x12, 0x37, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, + 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x07, 0x75, 0x6e, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, + 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x2e, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x79, 0x70, + 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, + 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x4d, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x0c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, + 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x6c, + 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x12, 0x34, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x69, 0x6f, + 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6e, 0x69, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x2a, 0x62, 0x0a, + 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, + 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, + 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x4d, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x02, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x54, 0x59, 0x50, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, + 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, + 0x47, 0x41, 0x55, 0x47, 0x45, 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, + 0x05, 0x42, 0x52, 0x0a, 0x14, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, + 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, + 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x67, 0x6f, + 0x3b, 0x69, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x5f, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, +} + +var ( + file_io_prometheus_client_metrics_proto_rawDescOnce sync.Once + file_io_prometheus_client_metrics_proto_rawDescData = file_io_prometheus_client_metrics_proto_rawDesc +) + +func file_io_prometheus_client_metrics_proto_rawDescGZIP() []byte { + file_io_prometheus_client_metrics_proto_rawDescOnce.Do(func() { + file_io_prometheus_client_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_io_prometheus_client_metrics_proto_rawDescData) + }) + return file_io_prometheus_client_metrics_proto_rawDescData +} + +var file_io_prometheus_client_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_io_prometheus_client_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_io_prometheus_client_metrics_proto_goTypes = []interface{}{ + (MetricType)(0), // 0: io.prometheus.client.MetricType + (*LabelPair)(nil), // 1: io.prometheus.client.LabelPair + (*Gauge)(nil), // 2: io.prometheus.client.Gauge + (*Counter)(nil), // 3: io.prometheus.client.Counter + (*Quantile)(nil), // 4: io.prometheus.client.Quantile + (*Summary)(nil), // 5: io.prometheus.client.Summary + (*Untyped)(nil), // 6: io.prometheus.client.Untyped + (*Histogram)(nil), // 7: io.prometheus.client.Histogram + (*Bucket)(nil), // 8: io.prometheus.client.Bucket + (*BucketSpan)(nil), // 9: io.prometheus.client.BucketSpan + (*Exemplar)(nil), // 10: io.prometheus.client.Exemplar + (*Metric)(nil), // 11: io.prometheus.client.Metric + (*MetricFamily)(nil), // 12: io.prometheus.client.MetricFamily + (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp +} +var file_io_prometheus_client_metrics_proto_depIdxs = []int32{ + 10, // 0: io.prometheus.client.Counter.exemplar:type_name -> io.prometheus.client.Exemplar + 13, // 1: io.prometheus.client.Counter.created_timestamp:type_name -> google.protobuf.Timestamp + 4, // 2: io.prometheus.client.Summary.quantile:type_name -> io.prometheus.client.Quantile + 13, // 3: io.prometheus.client.Summary.created_timestamp:type_name -> google.protobuf.Timestamp + 8, // 4: io.prometheus.client.Histogram.bucket:type_name -> io.prometheus.client.Bucket + 13, // 5: io.prometheus.client.Histogram.created_timestamp:type_name -> google.protobuf.Timestamp + 9, // 6: io.prometheus.client.Histogram.negative_span:type_name -> io.prometheus.client.BucketSpan + 9, // 7: io.prometheus.client.Histogram.positive_span:type_name -> io.prometheus.client.BucketSpan + 10, // 8: io.prometheus.client.Histogram.exemplars:type_name -> io.prometheus.client.Exemplar + 10, // 9: io.prometheus.client.Bucket.exemplar:type_name -> io.prometheus.client.Exemplar + 1, // 10: io.prometheus.client.Exemplar.label:type_name -> io.prometheus.client.LabelPair + 13, // 11: io.prometheus.client.Exemplar.timestamp:type_name -> google.protobuf.Timestamp + 1, // 12: io.prometheus.client.Metric.label:type_name -> io.prometheus.client.LabelPair + 2, // 13: io.prometheus.client.Metric.gauge:type_name -> io.prometheus.client.Gauge + 3, // 14: io.prometheus.client.Metric.counter:type_name -> io.prometheus.client.Counter + 5, // 15: io.prometheus.client.Metric.summary:type_name -> io.prometheus.client.Summary + 6, // 16: io.prometheus.client.Metric.untyped:type_name -> io.prometheus.client.Untyped + 7, // 17: io.prometheus.client.Metric.histogram:type_name -> io.prometheus.client.Histogram + 0, // 18: io.prometheus.client.MetricFamily.type:type_name -> io.prometheus.client.MetricType + 11, // 19: io.prometheus.client.MetricFamily.metric:type_name -> io.prometheus.client.Metric + 20, // [20:20] is the sub-list for method output_type + 20, // [20:20] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name +} + +func init() { file_io_prometheus_client_metrics_proto_init() } +func file_io_prometheus_client_metrics_proto_init() { + if File_io_prometheus_client_metrics_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_io_prometheus_client_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LabelPair); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Gauge); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Counter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Quantile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Summary); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Untyped); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Histogram); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bucket); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BucketSpan); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Exemplar); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Metric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_prometheus_client_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MetricFamily); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_io_prometheus_client_metrics_proto_rawDesc, + NumEnums: 1, + NumMessages: 12, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_io_prometheus_client_metrics_proto_goTypes, + DependencyIndexes: file_io_prometheus_client_metrics_proto_depIdxs, + EnumInfos: file_io_prometheus_client_metrics_proto_enumTypes, + MessageInfos: file_io_prometheus_client_metrics_proto_msgTypes, + }.Build() + File_io_prometheus_client_metrics_proto = out.File + file_io_prometheus_client_metrics_proto_rawDesc = nil + file_io_prometheus_client_metrics_proto_goTypes = nil + file_io_prometheus_client_metrics_proto_depIdxs = nil } diff --git a/vendor/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/prometheus/common/expfmt/decode.go index 7657f841d..25cfaa216 100644 --- a/vendor/github.com/prometheus/common/expfmt/decode.go +++ b/vendor/github.com/prometheus/common/expfmt/decode.go @@ -14,6 +14,7 @@ package expfmt import ( + "bufio" "fmt" "io" "math" @@ -21,8 +22,8 @@ import ( "net/http" dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/encoding/protodelim" - "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/common/model" ) @@ -44,7 +45,7 @@ func ResponseFormat(h http.Header) Format { mediatype, params, err := mime.ParseMediaType(ct) if err != nil { - return FmtUnknown + return fmtUnknown } const textType = "text/plain" @@ -52,42 +53,44 @@ func ResponseFormat(h http.Header) Format { switch mediatype { case ProtoType: if p, ok := params["proto"]; ok && p != ProtoProtocol { - return FmtUnknown + return fmtUnknown } if e, ok := params["encoding"]; ok && e != "delimited" { - return FmtUnknown + return fmtUnknown } - return FmtProtoDelim + return fmtProtoDelim case textType: if v, ok := params["version"]; ok && v != TextVersion { - return FmtUnknown + return fmtUnknown } - return FmtText + return fmtText } - return FmtUnknown + return fmtUnknown } // NewDecoder returns a new decoder based on the given input format. // If the input format does not imply otherwise, a text format decoder is returned. func NewDecoder(r io.Reader, format Format) Decoder { - switch format { - case FmtProtoDelim: - return &protoDecoder{r: r} + switch format.FormatType() { + case TypeProtoDelim: + return &protoDecoder{r: bufio.NewReader(r)} } return &textDecoder{r: r} } // protoDecoder implements the Decoder interface for protocol buffers. type protoDecoder struct { - r io.Reader + r protodelim.Reader } // Decode implements the Decoder interface. func (d *protoDecoder) Decode(v *dto.MetricFamily) error { - _, err := pbutil.ReadDelimited(d.r, v) - if err != nil { + opts := protodelim.UnmarshalOptions{ + MaxSize: -1, + } + if err := opts.UnmarshalFrom(d.r, v); err != nil { return err } if !model.IsValidMetricName(model.LabelValue(v.GetName())) { @@ -115,32 +118,31 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error { // textDecoder implements the Decoder interface for the text protocol. type textDecoder struct { r io.Reader - p TextParser - fams []*dto.MetricFamily + fams map[string]*dto.MetricFamily + err error } // Decode implements the Decoder interface. func (d *textDecoder) Decode(v *dto.MetricFamily) error { - // TODO(fabxc): Wrap this as a line reader to make streaming safer. - if len(d.fams) == 0 { - // No cached metric families, read everything and parse metrics. - fams, err := d.p.TextToMetricFamilies(d.r) - if err != nil { - return err - } - if len(fams) == 0 { - return io.EOF - } - d.fams = make([]*dto.MetricFamily, 0, len(fams)) - for _, f := range fams { - d.fams = append(d.fams, f) + if d.err == nil { + // Read all metrics in one shot. + var p TextParser + d.fams, d.err = p.TextToMetricFamilies(d.r) + // If we don't get an error, store io.EOF for the end. + if d.err == nil { + d.err = io.EOF } } - - *v = *d.fams[0] - d.fams = d.fams[1:] - - return nil + // Pick off one MetricFamily per Decode until there's nothing left. + for key, fam := range d.fams { + v.Name = fam.Name + v.Help = fam.Help + v.Type = fam.Type + v.Metric = fam.Metric + delete(d.fams, key) + return nil + } + return d.err } // SampleDecoder wraps a Decoder to extract samples from the metric families diff --git a/vendor/github.com/prometheus/common/expfmt/encode.go b/vendor/github.com/prometheus/common/expfmt/encode.go index 64dc0eb40..7f6cbe7d2 100644 --- a/vendor/github.com/prometheus/common/expfmt/encode.go +++ b/vendor/github.com/prometheus/common/expfmt/encode.go @@ -18,9 +18,11 @@ import ( "io" "net/http" - "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. - "github.com/matttproud/golang_protobuf_extensions/pbutil" + "google.golang.org/protobuf/encoding/protodelim" + "google.golang.org/protobuf/encoding/prototext" + "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg" + "github.com/prometheus/common/model" dto "github.com/prometheus/client_model/go" ) @@ -60,23 +62,32 @@ func (ec encoderCloser) Close() error { // as the support is still experimental. To include the option to negotiate // FmtOpenMetrics, use NegotiateOpenMetrics. func Negotiate(h http.Header) Format { + escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String()))) for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) { + if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" { + switch Format(escapeParam) { + case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues: + escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam)) + default: + // If the escaping parameter is unknown, ignore it. + } + } ver := ac.Params["version"] if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol { switch ac.Params["encoding"] { case "delimited": - return FmtProtoDelim + return fmtProtoDelim + escapingScheme case "text": - return FmtProtoText + return fmtProtoText + escapingScheme case "compact-text": - return FmtProtoCompact + return fmtProtoCompact + escapingScheme } } if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { - return FmtText + return fmtText + escapingScheme } } - return FmtText + return fmtText + escapingScheme } // NegotiateIncludingOpenMetrics works like Negotiate but includes @@ -84,26 +95,40 @@ func Negotiate(h http.Header) Format { // temporary and will disappear once FmtOpenMetrics is fully supported and as // such may be negotiated by the normal Negotiate function. func NegotiateIncludingOpenMetrics(h http.Header) Format { + escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String()))) for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) { + if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" { + switch Format(escapeParam) { + case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues: + escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam)) + default: + // If the escaping parameter is unknown, ignore it. + } + } ver := ac.Params["version"] if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol { switch ac.Params["encoding"] { case "delimited": - return FmtProtoDelim + return fmtProtoDelim + escapingScheme case "text": - return FmtProtoText + return fmtProtoText + escapingScheme case "compact-text": - return FmtProtoCompact + return fmtProtoCompact + escapingScheme } } if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { - return FmtText + return fmtText + escapingScheme } - if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") { - return FmtOpenMetrics + if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") { + switch ver { + case OpenMetricsVersion_1_0_0: + return fmtOpenMetrics_1_0_0 + escapingScheme + default: + return fmtOpenMetrics_0_0_1 + escapingScheme + } } } - return FmtText + return fmtText + escapingScheme } // NewEncoder returns a new encoder based on content type negotiation. All @@ -112,44 +137,54 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format { // for FmtOpenMetrics, but a future (breaking) release will add the Close method // to the Encoder interface directly. The current version of the Encoder // interface is kept for backwards compatibility. -func NewEncoder(w io.Writer, format Format) Encoder { - switch format { - case FmtProtoDelim: +// In cases where the Format does not allow for UTF-8 names, the global +// NameEscapingScheme will be applied. +// +// NewEncoder can be called with additional options to customize the OpenMetrics text output. +// For example: +// NewEncoder(w, FmtOpenMetrics_1_0_0, WithCreatedLines()) +// +// Extra options are ignored for all other formats. +func NewEncoder(w io.Writer, format Format, options ...EncoderOption) Encoder { + escapingScheme := format.ToEscapingScheme() + + switch format.FormatType() { + case TypeProtoDelim: return encoderCloser{ encode: func(v *dto.MetricFamily) error { - _, err := pbutil.WriteDelimited(w, v) + _, err := protodelim.MarshalTo(w, v) return err }, close: func() error { return nil }, } - case FmtProtoCompact: + case TypeProtoCompact: return encoderCloser{ encode: func(v *dto.MetricFamily) error { - _, err := fmt.Fprintln(w, v.String()) + _, err := fmt.Fprintln(w, model.EscapeMetricFamily(v, escapingScheme).String()) return err }, close: func() error { return nil }, } - case FmtProtoText: + case TypeProtoText: return encoderCloser{ encode: func(v *dto.MetricFamily) error { - _, err := fmt.Fprintln(w, proto.MarshalTextString(v)) + _, err := fmt.Fprintln(w, prototext.Format(model.EscapeMetricFamily(v, escapingScheme))) return err }, close: func() error { return nil }, } - case FmtText: + case TypeTextPlain: return encoderCloser{ encode: func(v *dto.MetricFamily) error { - _, err := MetricFamilyToText(w, v) + _, err := MetricFamilyToText(w, model.EscapeMetricFamily(v, escapingScheme)) return err }, close: func() error { return nil }, } - case FmtOpenMetrics: + case TypeOpenMetrics: return encoderCloser{ encode: func(v *dto.MetricFamily) error { - _, err := MetricFamilyToOpenMetrics(w, v) + _, err := MetricFamilyToOpenMetrics(w, model.EscapeMetricFamily(v, escapingScheme), options...) return err }, close: func() error { diff --git a/vendor/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/prometheus/common/expfmt/expfmt.go index 0f176fa64..051b38cd1 100644 --- a/vendor/github.com/prometheus/common/expfmt/expfmt.go +++ b/vendor/github.com/prometheus/common/expfmt/expfmt.go @@ -14,28 +14,164 @@ // Package expfmt contains tools for reading and writing Prometheus metrics. package expfmt +import ( + "fmt" + "strings" + + "github.com/prometheus/common/model" +) + // Format specifies the HTTP content type of the different wire protocols. type Format string -// Constants to assemble the Content-Type values for the different wire protocols. +// Constants to assemble the Content-Type values for the different wire +// protocols. The Content-Type strings here are all for the legacy exposition +// formats, where valid characters for metric names and label names are limited. +// Support for arbitrary UTF-8 characters in those names is already partially +// implemented in this module (see model.ValidationScheme), but to actually use +// it on the wire, new content-type strings will have to be agreed upon and +// added here. const ( - TextVersion = "0.0.4" - ProtoType = `application/vnd.google.protobuf` - ProtoProtocol = `io.prometheus.client.MetricFamily` - ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" - OpenMetricsType = `application/openmetrics-text` - OpenMetricsVersion = "0.0.1" - - // The Content-Type values for the different wire protocols. - FmtUnknown Format = `` - FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8` - FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` - FmtProtoText Format = ProtoFmt + ` encoding=text` - FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` - FmtOpenMetrics Format = OpenMetricsType + `; version=` + OpenMetricsVersion + `; charset=utf-8` + TextVersion = "0.0.4" + ProtoType = `application/vnd.google.protobuf` + ProtoProtocol = `io.prometheus.client.MetricFamily` + protoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" + OpenMetricsType = `application/openmetrics-text` + OpenMetricsVersion_0_0_1 = "0.0.1" + OpenMetricsVersion_1_0_0 = "1.0.0" + + // The Content-Type values for the different wire protocols. Note that these + // values are now unexported. If code was relying on comparisons to these + // constants, instead use FormatType(). + fmtUnknown Format = `` + fmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8` + fmtProtoDelim Format = protoFmt + ` encoding=delimited` + fmtProtoText Format = protoFmt + ` encoding=text` + fmtProtoCompact Format = protoFmt + ` encoding=compact-text` + fmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8` + fmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8` ) const ( hdrContentType = "Content-Type" hdrAccept = "Accept" ) + +// FormatType is a Go enum representing the overall category for the given +// Format. As the number of Format permutations increases, doing basic string +// comparisons are not feasible, so this enum captures the most useful +// high-level attribute of the Format string. +type FormatType int + +const ( + TypeUnknown FormatType = iota + TypeProtoCompact + TypeProtoDelim + TypeProtoText + TypeTextPlain + TypeOpenMetrics +) + +// NewFormat generates a new Format from the type provided. Mostly used for +// tests, most Formats should be generated as part of content negotiation in +// encode.go. If a type has more than one version, the latest version will be +// returned. +func NewFormat(t FormatType) Format { + switch t { + case TypeProtoCompact: + return fmtProtoCompact + case TypeProtoDelim: + return fmtProtoDelim + case TypeProtoText: + return fmtProtoText + case TypeTextPlain: + return fmtText + case TypeOpenMetrics: + return fmtOpenMetrics_1_0_0 + default: + return fmtUnknown + } +} + +// NewOpenMetricsFormat generates a new OpenMetrics format matching the +// specified version number. +func NewOpenMetricsFormat(version string) (Format, error) { + if version == OpenMetricsVersion_0_0_1 { + return fmtOpenMetrics_0_0_1, nil + } + if version == OpenMetricsVersion_1_0_0 { + return fmtOpenMetrics_1_0_0, nil + } + return fmtUnknown, fmt.Errorf("unknown open metrics version string") +} + +// FormatType deduces an overall FormatType for the given format. +func (f Format) FormatType() FormatType { + toks := strings.Split(string(f), ";") + params := make(map[string]string) + for i, t := range toks { + if i == 0 { + continue + } + args := strings.Split(t, "=") + if len(args) != 2 { + continue + } + params[strings.TrimSpace(args[0])] = strings.TrimSpace(args[1]) + } + + switch strings.TrimSpace(toks[0]) { + case ProtoType: + if params["proto"] != ProtoProtocol { + return TypeUnknown + } + switch params["encoding"] { + case "delimited": + return TypeProtoDelim + case "text": + return TypeProtoText + case "compact-text": + return TypeProtoCompact + default: + return TypeUnknown + } + case OpenMetricsType: + if params["charset"] != "utf-8" { + return TypeUnknown + } + return TypeOpenMetrics + case "text/plain": + v, ok := params["version"] + if !ok { + return TypeTextPlain + } + if v == TextVersion { + return TypeTextPlain + } + return TypeUnknown + default: + return TypeUnknown + } +} + +// ToEscapingScheme returns an EscapingScheme depending on the Format. Iff the +// Format contains a escaping=allow-utf-8 term, it will select NoEscaping. If a valid +// "escaping" term exists, that will be used. Otherwise, the global default will +// be returned. +func (format Format) ToEscapingScheme() model.EscapingScheme { + for _, p := range strings.Split(string(format), ";") { + toks := strings.Split(p, "=") + if len(toks) != 2 { + continue + } + key, value := strings.TrimSpace(toks[0]), strings.TrimSpace(toks[1]) + if key == model.EscapingKey { + scheme, err := model.ToEscapingScheme(value) + if err != nil { + return model.NameEscapingScheme + } + return scheme + } + } + return model.NameEscapingScheme +} diff --git a/vendor/github.com/prometheus/common/expfmt/fuzz.go b/vendor/github.com/prometheus/common/expfmt/fuzz.go index f819e4f8b..dfac962a4 100644 --- a/vendor/github.com/prometheus/common/expfmt/fuzz.go +++ b/vendor/github.com/prometheus/common/expfmt/fuzz.go @@ -21,8 +21,8 @@ import "bytes" // Fuzz text metric parser with with github.com/dvyukov/go-fuzz: // -// go-fuzz-build github.com/prometheus/common/expfmt -// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz +// go-fuzz-build github.com/prometheus/common/expfmt +// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz // // Further input samples should go in the folder fuzz/corpus. func Fuzz(in []byte) int { diff --git a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go index 9d94ae9ef..353c5e93f 100644 --- a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go +++ b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go @@ -22,11 +22,47 @@ import ( "strconv" "strings" + "google.golang.org/protobuf/types/known/timestamppb" + "github.com/prometheus/common/model" dto "github.com/prometheus/client_model/go" ) +type encoderOption struct { + withCreatedLines bool + withUnit bool +} + +type EncoderOption func(*encoderOption) + +// WithCreatedLines is an EncoderOption that configures the OpenMetrics encoder +// to include _created lines (See +// https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#counter-1). +// Created timestamps can improve the accuracy of series reset detection, but +// come with a bandwidth cost. +// +// At the time of writing, created timestamp ingestion is still experimental in +// Prometheus and need to be enabled with the feature-flag +// `--feature-flag=created-timestamp-zero-ingestion`, and breaking changes are +// still possible. Therefore, it is recommended to use this feature with caution. +func WithCreatedLines() EncoderOption { + return func(t *encoderOption) { + t.withCreatedLines = true + } +} + +// WithUnit is an EncoderOption enabling a set unit to be written to the output +// and to be added to the metric name, if it's not there already, as a suffix. +// Without opting in this way, the unit will not be added to the metric name and, +// on top of that, the unit will not be passed onto the output, even if it +// were declared in the *dto.MetricFamily struct, i.e. even if in.Unit !=nil. +func WithUnit() EncoderOption { + return func(t *encoderOption) { + t.withUnit = true + } +} + // MetricFamilyToOpenMetrics converts a MetricFamily proto message into the // OpenMetrics text format and writes the resulting lines to 'out'. It returns // the number of bytes written and any error encountered. The output will have @@ -35,6 +71,18 @@ import ( // sanity checks. If the input contains duplicate metrics or invalid metric or // label names, the conversion will result in invalid text format output. // +// If metric names conform to the legacy validation pattern, they will be placed +// outside the brackets in the traditional way, like `foo{}`. If the metric name +// fails the legacy validation check, it will be placed quoted inside the +// brackets: `{"foo"}`. As stated above, the input is assumed to be santized and +// no error will be thrown in this case. +// +// Similar to metric names, if label names conform to the legacy validation +// pattern, they will be unquoted as normal, like `foo{bar="baz"}`. If the label +// name fails the legacy validation check, it will be quoted: +// `foo{"bar"="baz"}`. As stated above, the input is assumed to be santized and +// no error will be thrown in this case. +// // This function fulfills the type 'expfmt.encoder'. // // Note that OpenMetrics requires a final `# EOF` line. Since this function acts @@ -46,21 +94,35 @@ import ( // missing features and peculiarities to avoid complications when switching from // Prometheus to OpenMetrics or vice versa: // -// - Counters are expected to have the `_total` suffix in their metric name. In -// the output, the suffix will be truncated from the `# TYPE` and `# HELP` -// line. A counter with a missing `_total` suffix is not an error. However, -// its type will be set to `unknown` in that case to avoid invalid OpenMetrics -// output. +// - Counters are expected to have the `_total` suffix in their metric name. In +// the output, the suffix will be truncated from the `# TYPE`, `# HELP` and `# UNIT` +// lines. A counter with a missing `_total` suffix is not an error. However, +// its type will be set to `unknown` in that case to avoid invalid OpenMetrics +// output. // -// - No support for the following (optional) features: `# UNIT` line, `_created` -// line, info type, stateset type, gaugehistogram type. +// - According to the OM specs, the `# UNIT` line is optional, but if populated, +// the unit has to be present in the metric name as its suffix: +// (see https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#unit). +// However, in order to accommodate any potential scenario where such a change in the +// metric name is not desirable, the users are here given the choice of either explicitly +// opt in, in case they wish for the unit to be included in the output AND in the metric name +// as a suffix (see the description of the WithUnit function above), +// or not to opt in, in case they don't want for any of that to happen. // -// - The size of exemplar labels is not checked (i.e. it's possible to create -// exemplars that are larger than allowed by the OpenMetrics specification). +// - No support for the following (optional) features: info type, +// stateset type, gaugehistogram type. // -// - The value of Counters is not checked. (OpenMetrics doesn't allow counters -// with a `NaN` value.) -func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) { +// - The size of exemplar labels is not checked (i.e. it's possible to create +// exemplars that are larger than allowed by the OpenMetrics specification). +// +// - The value of Counters is not checked. (OpenMetrics doesn't allow counters +// with a `NaN` value.) +func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...EncoderOption) (written int, err error) { + toOM := encoderOption{} + for _, option := range options { + option(&toOM) + } + name := in.GetName() if name == "" { return 0, fmt.Errorf("MetricFamily has no name: %s", in) @@ -83,12 +145,15 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int } var ( - n int - metricType = in.GetType() - shortName = name + n int + metricType = in.GetType() + compliantName = name ) - if metricType == dto.MetricType_COUNTER && strings.HasSuffix(shortName, "_total") { - shortName = name[:len(name)-6] + if metricType == dto.MetricType_COUNTER && strings.HasSuffix(compliantName, "_total") { + compliantName = name[:len(name)-6] + } + if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, fmt.Sprintf("_%s", *in.Unit)) { + compliantName = compliantName + fmt.Sprintf("_%s", *in.Unit) } // Comments, first HELP, then TYPE. @@ -98,7 +163,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int if err != nil { return } - n, err = w.WriteString(shortName) + n, err = writeName(w, compliantName) written += n if err != nil { return @@ -124,7 +189,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int if err != nil { return } - n, err = w.WriteString(shortName) + n, err = writeName(w, compliantName) written += n if err != nil { return @@ -151,55 +216,89 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int if err != nil { return } + if toOM.withUnit && in.Unit != nil { + n, err = w.WriteString("# UNIT ") + written += n + if err != nil { + return + } + n, err = writeName(w, compliantName) + written += n + if err != nil { + return + } + + err = w.WriteByte(' ') + written++ + if err != nil { + return + } + n, err = writeEscapedString(w, *in.Unit, true) + written += n + if err != nil { + return + } + err = w.WriteByte('\n') + written++ + if err != nil { + return + } + } + + var createdTsBytesWritten int // Finally the samples, one line for each. + if metricType == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") { + compliantName = compliantName + "_total" + } for _, metric := range in.Metric { switch metricType { case dto.MetricType_COUNTER: if metric.Counter == nil { return written, fmt.Errorf( - "expected counter in metric %s %s", name, metric, + "expected counter in metric %s %s", compliantName, metric, ) } - // Note that we have ensured above that either the name - // ends on `_total` or that the rendered type is - // `unknown`. Therefore, no `_total` must be added here. n, err = writeOpenMetricsSample( - w, name, "", metric, "", 0, + w, compliantName, "", metric, "", 0, metric.Counter.GetValue(), 0, false, metric.Counter.Exemplar, ) + if toOM.withCreatedLines && metric.Counter.CreatedTimestamp != nil { + createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "_total", metric, "", 0, metric.Counter.GetCreatedTimestamp()) + n += createdTsBytesWritten + } case dto.MetricType_GAUGE: if metric.Gauge == nil { return written, fmt.Errorf( - "expected gauge in metric %s %s", name, metric, + "expected gauge in metric %s %s", compliantName, metric, ) } n, err = writeOpenMetricsSample( - w, name, "", metric, "", 0, + w, compliantName, "", metric, "", 0, metric.Gauge.GetValue(), 0, false, nil, ) case dto.MetricType_UNTYPED: if metric.Untyped == nil { return written, fmt.Errorf( - "expected untyped in metric %s %s", name, metric, + "expected untyped in metric %s %s", compliantName, metric, ) } n, err = writeOpenMetricsSample( - w, name, "", metric, "", 0, + w, compliantName, "", metric, "", 0, metric.Untyped.GetValue(), 0, false, nil, ) case dto.MetricType_SUMMARY: if metric.Summary == nil { return written, fmt.Errorf( - "expected summary in metric %s %s", name, metric, + "expected summary in metric %s %s", compliantName, metric, ) } for _, q := range metric.Summary.Quantile { n, err = writeOpenMetricsSample( - w, name, "", metric, + w, compliantName, "", metric, model.QuantileLabel, q.GetQuantile(), q.GetValue(), 0, false, nil, @@ -210,7 +309,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int } } n, err = writeOpenMetricsSample( - w, name, "_sum", metric, "", 0, + w, compliantName, "_sum", metric, "", 0, metric.Summary.GetSampleSum(), 0, false, nil, ) @@ -219,20 +318,24 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int return } n, err = writeOpenMetricsSample( - w, name, "_count", metric, "", 0, + w, compliantName, "_count", metric, "", 0, 0, metric.Summary.GetSampleCount(), true, nil, ) + if toOM.withCreatedLines && metric.Summary.CreatedTimestamp != nil { + createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "", metric, "", 0, metric.Summary.GetCreatedTimestamp()) + n += createdTsBytesWritten + } case dto.MetricType_HISTOGRAM: if metric.Histogram == nil { return written, fmt.Errorf( - "expected histogram in metric %s %s", name, metric, + "expected histogram in metric %s %s", compliantName, metric, ) } infSeen := false for _, b := range metric.Histogram.Bucket { n, err = writeOpenMetricsSample( - w, name, "_bucket", metric, + w, compliantName, "_bucket", metric, model.BucketLabel, b.GetUpperBound(), 0, b.GetCumulativeCount(), true, b.Exemplar, @@ -247,7 +350,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int } if !infSeen { n, err = writeOpenMetricsSample( - w, name, "_bucket", metric, + w, compliantName, "_bucket", metric, model.BucketLabel, math.Inf(+1), 0, metric.Histogram.GetSampleCount(), true, nil, @@ -258,7 +361,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int } } n, err = writeOpenMetricsSample( - w, name, "_sum", metric, "", 0, + w, compliantName, "_sum", metric, "", 0, metric.Histogram.GetSampleSum(), 0, false, nil, ) @@ -267,13 +370,17 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int return } n, err = writeOpenMetricsSample( - w, name, "_count", metric, "", 0, + w, compliantName, "_count", metric, "", 0, 0, metric.Histogram.GetSampleCount(), true, nil, ) + if toOM.withCreatedLines && metric.Histogram.CreatedTimestamp != nil { + createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "", metric, "", 0, metric.Histogram.GetCreatedTimestamp()) + n += createdTsBytesWritten + } default: return written, fmt.Errorf( - "unexpected type in metric %s %s", name, metric, + "unexpected type in metric %s %s", compliantName, metric, ) } written += n @@ -303,21 +410,9 @@ func writeOpenMetricsSample( floatValue float64, intValue uint64, useIntValue bool, exemplar *dto.Exemplar, ) (int, error) { - var written int - n, err := w.WriteString(name) - written += n - if err != nil { - return written, err - } - if suffix != "" { - n, err = w.WriteString(suffix) - written += n - if err != nil { - return written, err - } - } - n, err = writeOpenMetricsLabelPairs( - w, metric.Label, additionalLabelName, additionalLabelValue, + written := 0 + n, err := writeOpenMetricsNameAndLabelPairs( + w, name+suffix, metric.Label, additionalLabelName, additionalLabelValue, ) written += n if err != nil { @@ -350,7 +445,7 @@ func writeOpenMetricsSample( return written, err } } - if exemplar != nil { + if exemplar != nil && len(exemplar.Label) > 0 { n, err = writeExemplar(w, exemplar) written += n if err != nil { @@ -365,27 +460,58 @@ func writeOpenMetricsSample( return written, nil } -// writeOpenMetricsLabelPairs works like writeOpenMetrics but formats the float -// in OpenMetrics style. -func writeOpenMetricsLabelPairs( +// writeOpenMetricsNameAndLabelPairs works like writeOpenMetricsSample but +// formats the float in OpenMetrics style. +func writeOpenMetricsNameAndLabelPairs( w enhancedWriter, + name string, in []*dto.LabelPair, additionalLabelName string, additionalLabelValue float64, ) (int, error) { - if len(in) == 0 && additionalLabelName == "" { - return 0, nil - } var ( - written int - separator byte = '{' + written int + separator byte = '{' + metricInsideBraces = false ) + + if name != "" { + // If the name does not pass the legacy validity check, we must put the + // metric name inside the braces, quoted. + if !model.IsValidLegacyMetricName(model.LabelValue(name)) { + metricInsideBraces = true + err := w.WriteByte(separator) + written++ + if err != nil { + return written, err + } + separator = ',' + } + + n, err := writeName(w, name) + written += n + if err != nil { + return written, err + } + } + + if len(in) == 0 && additionalLabelName == "" { + if metricInsideBraces { + err := w.WriteByte('}') + written++ + if err != nil { + return written, err + } + } + return written, nil + } + for _, lp := range in { err := w.WriteByte(separator) written++ if err != nil { return written, err } - n, err := w.WriteString(lp.GetName()) + n, err := writeName(w, lp.GetName()) written += n if err != nil { return written, err @@ -442,6 +568,49 @@ func writeOpenMetricsLabelPairs( return written, nil } +// writeOpenMetricsCreated writes the created timestamp for a single time series +// following OpenMetrics text format to w, given the metric name, the metric proto +// message itself, optionally a suffix to be removed, e.g. '_total' for counters, +// an additional label name with a float64 value (use empty string as label name if +// not required) and the timestamp that represents the created timestamp. +// The function returns the number of bytes written and any error encountered. +func writeOpenMetricsCreated(w enhancedWriter, + name, suffixToTrim string, metric *dto.Metric, + additionalLabelName string, additionalLabelValue float64, + createdTimestamp *timestamppb.Timestamp, +) (int, error) { + written := 0 + n, err := writeOpenMetricsNameAndLabelPairs( + w, strings.TrimSuffix(name, suffixToTrim)+"_created", metric.Label, additionalLabelName, additionalLabelValue, + ) + written += n + if err != nil { + return written, err + } + + err = w.WriteByte(' ') + written++ + if err != nil { + return written, err + } + + // TODO(beorn7): Format this directly from components of ts to + // avoid overflow/underflow and precision issues of the float + // conversion. + n, err = writeOpenMetricsFloat(w, float64(createdTimestamp.AsTime().UnixNano())/1e9) + written += n + if err != nil { + return written, err + } + + err = w.WriteByte('\n') + written++ + if err != nil { + return written, err + } + return written, nil +} + // writeExemplar writes the provided exemplar in OpenMetrics format to w. The // function returns the number of bytes written and any error encountered. func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) { @@ -451,7 +620,7 @@ func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) { if err != nil { return written, err } - n, err = writeOpenMetricsLabelPairs(w, e.Label, "", 0) + n, err = writeOpenMetricsNameAndLabelPairs(w, "", e.Label, "", 0) written += n if err != nil { return written, err diff --git a/vendor/github.com/prometheus/common/expfmt/text_create.go b/vendor/github.com/prometheus/common/expfmt/text_create.go index 5ba503b06..f9b8265a9 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_create.go +++ b/vendor/github.com/prometheus/common/expfmt/text_create.go @@ -17,7 +17,6 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "math" "strconv" "strings" @@ -44,7 +43,7 @@ const ( var ( bufPool = sync.Pool{ New: func() interface{} { - return bufio.NewWriter(ioutil.Discard) + return bufio.NewWriter(io.Discard) }, } numBufPool = sync.Pool{ @@ -63,6 +62,18 @@ var ( // contains duplicate metrics or invalid metric or label names, the conversion // will result in invalid text format output. // +// If metric names conform to the legacy validation pattern, they will be placed +// outside the brackets in the traditional way, like `foo{}`. If the metric name +// fails the legacy validation check, it will be placed quoted inside the +// brackets: `{"foo"}`. As stated above, the input is assumed to be santized and +// no error will be thrown in this case. +// +// Similar to metric names, if label names conform to the legacy validation +// pattern, they will be unquoted as normal, like `foo{bar="baz"}`. If the label +// name fails the legacy validation check, it will be quoted: +// `foo{"bar"="baz"}`. As stated above, the input is assumed to be santized and +// no error will be thrown in this case. +// // This method fulfills the type 'prometheus.encoder'. func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) { // Fail-fast checks. @@ -99,7 +110,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e if err != nil { return } - n, err = w.WriteString(name) + n, err = writeName(w, name) written += n if err != nil { return @@ -125,7 +136,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e if err != nil { return } - n, err = w.WriteString(name) + n, err = writeName(w, name) written += n if err != nil { return @@ -281,21 +292,9 @@ func writeSample( additionalLabelName string, additionalLabelValue float64, value float64, ) (int, error) { - var written int - n, err := w.WriteString(name) - written += n - if err != nil { - return written, err - } - if suffix != "" { - n, err = w.WriteString(suffix) - written += n - if err != nil { - return written, err - } - } - n, err = writeLabelPairs( - w, metric.Label, additionalLabelName, additionalLabelValue, + written := 0 + n, err := writeNameAndLabelPairs( + w, name+suffix, metric.Label, additionalLabelName, additionalLabelValue, ) written += n if err != nil { @@ -331,32 +330,64 @@ func writeSample( return written, nil } -// writeLabelPairs converts a slice of LabelPair proto messages plus the -// explicitly given additional label pair into text formatted as required by the -// text format and writes it to 'w'. An empty slice in combination with an empty -// string 'additionalLabelName' results in nothing being written. Otherwise, the -// label pairs are written, escaped as required by the text format, and enclosed -// in '{...}'. The function returns the number of bytes written and any error -// encountered. -func writeLabelPairs( +// writeNameAndLabelPairs converts a slice of LabelPair proto messages plus the +// explicitly given metric name and additional label pair into text formatted as +// required by the text format and writes it to 'w'. An empty slice in +// combination with an empty string 'additionalLabelName' results in nothing +// being written. Otherwise, the label pairs are written, escaped as required by +// the text format, and enclosed in '{...}'. The function returns the number of +// bytes written and any error encountered. If the metric name is not +// legacy-valid, it will be put inside the brackets as well. Legacy-invalid +// label names will also be quoted. +func writeNameAndLabelPairs( w enhancedWriter, + name string, in []*dto.LabelPair, additionalLabelName string, additionalLabelValue float64, ) (int, error) { - if len(in) == 0 && additionalLabelName == "" { - return 0, nil - } var ( - written int - separator byte = '{' + written int + separator byte = '{' + metricInsideBraces = false ) + + if name != "" { + // If the name does not pass the legacy validity check, we must put the + // metric name inside the braces. + if !model.IsValidLegacyMetricName(model.LabelValue(name)) { + metricInsideBraces = true + err := w.WriteByte(separator) + written++ + if err != nil { + return written, err + } + separator = ',' + } + n, err := writeName(w, name) + written += n + if err != nil { + return written, err + } + } + + if len(in) == 0 && additionalLabelName == "" { + if metricInsideBraces { + err := w.WriteByte('}') + written++ + if err != nil { + return written, err + } + } + return written, nil + } + for _, lp := range in { err := w.WriteByte(separator) written++ if err != nil { return written, err } - n, err := w.WriteString(lp.GetName()) + n, err := writeName(w, lp.GetName()) written += n if err != nil { return written, err @@ -463,3 +494,27 @@ func writeInt(w enhancedWriter, i int64) (int, error) { numBufPool.Put(bp) return written, err } + +// writeName writes a string as-is if it complies with the legacy naming +// scheme, or escapes it in double quotes if not. +func writeName(w enhancedWriter, name string) (int, error) { + if model.IsValidLegacyMetricName(model.LabelValue(name)) { + return w.WriteString(name) + } + var written int + var err error + err = w.WriteByte('"') + written++ + if err != nil { + return written, err + } + var n int + n, err = writeEscapedString(w, name, true) + written += n + if err != nil { + return written, err + } + err = w.WriteByte('"') + written++ + return written, err +} diff --git a/vendor/github.com/prometheus/common/expfmt/text_parse.go b/vendor/github.com/prometheus/common/expfmt/text_parse.go index 84be0643e..26490211a 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_parse.go +++ b/vendor/github.com/prometheus/common/expfmt/text_parse.go @@ -16,6 +16,7 @@ package expfmt import ( "bufio" "bytes" + "errors" "fmt" "io" "math" @@ -24,7 +25,8 @@ import ( dto "github.com/prometheus/client_model/go" - "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. + "google.golang.org/protobuf/proto" + "github.com/prometheus/common/model" ) @@ -112,7 +114,7 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF // stream. Turn this error into something nicer and more // meaningful. (io.EOF is often used as a signal for the legitimate end // of an input stream.) - if p.err == io.EOF { + if p.err != nil && errors.Is(p.err, io.EOF) { p.parseError("unexpected end of input stream") } return p.metricFamiliesByName, p.err @@ -142,9 +144,13 @@ func (p *TextParser) reset(in io.Reader) { func (p *TextParser) startOfLine() stateFn { p.lineCount++ if p.skipBlankTab(); p.err != nil { - // End of input reached. This is the only case where - // that is not an error but a signal that we are done. - p.err = nil + // This is the only place that we expect to see io.EOF, + // which is not an error but the signal that we are done. + // Any other error that happens to align with the start of + // a line is still an error. + if errors.Is(p.err, io.EOF) { + p.err = nil + } return nil } switch p.currentByte { diff --git a/vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go b/vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go index 26e92288c..a21b9d15d 100644 --- a/vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go +++ b/vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go @@ -11,18 +11,18 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. - Neither the name of the Open Knowledge Foundation Ltd. nor the - names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. + Neither the name of the Open Knowledge Foundation Ltd. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -35,8 +35,6 @@ 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 goautoneg diff --git a/vendor/github.com/prometheus/common/model/alert.go b/vendor/github.com/prometheus/common/model/alert.go index 35e739c7a..80d1fe944 100644 --- a/vendor/github.com/prometheus/common/model/alert.go +++ b/vendor/github.com/prometheus/common/model/alert.go @@ -75,7 +75,12 @@ func (a *Alert) ResolvedAt(ts time.Time) bool { // Status returns the status of the alert. func (a *Alert) Status() AlertStatus { - if a.Resolved() { + return a.StatusAt(time.Now()) +} + +// StatusAt returns the status of the alert at the given timestamp. +func (a *Alert) StatusAt(ts time.Time) AlertStatus { + if a.ResolvedAt(ts) { return AlertResolved } return AlertFiring @@ -90,13 +95,13 @@ func (a *Alert) Validate() error { return fmt.Errorf("start time must be before end time") } if err := a.Labels.Validate(); err != nil { - return fmt.Errorf("invalid label set: %s", err) + return fmt.Errorf("invalid label set: %w", err) } if len(a.Labels) == 0 { return fmt.Errorf("at least one label pair required") } if err := a.Annotations.Validate(); err != nil { - return fmt.Errorf("invalid annotations: %s", err) + return fmt.Errorf("invalid annotations: %w", err) } return nil } @@ -127,6 +132,17 @@ func (as Alerts) HasFiring() bool { return false } +// HasFiringAt returns true iff one of the alerts is not resolved +// at the time ts. +func (as Alerts) HasFiringAt(ts time.Time) bool { + for _, a := range as { + if !a.ResolvedAt(ts) { + return true + } + } + return false +} + // Status returns StatusFiring iff at least one of the alerts is firing. func (as Alerts) Status() AlertStatus { if as.HasFiring() { @@ -134,3 +150,12 @@ func (as Alerts) Status() AlertStatus { } return AlertResolved } + +// StatusAt returns StatusFiring iff at least one of the alerts is firing +// at the time ts. +func (as Alerts) StatusAt(ts time.Time) AlertStatus { + if as.HasFiringAt(ts) { + return AlertFiring + } + return AlertResolved +} diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go index ef8956335..3317ce22f 100644 --- a/vendor/github.com/prometheus/common/model/labels.go +++ b/vendor/github.com/prometheus/common/model/labels.go @@ -97,17 +97,25 @@ var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") // therewith. type LabelName string -// IsValid is true iff the label name matches the pattern of LabelNameRE. This -// method, however, does not use LabelNameRE for the check but a much faster -// hardcoded implementation. +// IsValid returns true iff name matches the pattern of LabelNameRE for legacy +// names, and iff it's valid UTF-8 if NameValidationScheme is set to +// UTF8Validation. For the legacy matching, it does not use LabelNameRE for the +// check but a much faster hardcoded implementation. func (ln LabelName) IsValid() bool { if len(ln) == 0 { return false } - for i, b := range ln { - if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) { - return false + switch NameValidationScheme { + case LegacyValidation: + for i, b := range ln { + if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) { + return false + } } + case UTF8Validation: + return utf8.ValidString(string(ln)) + default: + panic(fmt.Sprintf("Invalid name validation scheme requested: %d", NameValidationScheme)) } return true } @@ -164,7 +172,7 @@ func (l LabelNames) String() string { // A LabelValue is an associated value for a LabelName. type LabelValue string -// IsValid returns true iff the string is a valid UTF8. +// IsValid returns true iff the string is a valid UTF-8. func (lv LabelValue) IsValid() bool { return utf8.ValidString(string(lv)) } diff --git a/vendor/github.com/prometheus/common/model/labelset.go b/vendor/github.com/prometheus/common/model/labelset.go index 6eda08a73..d0ad88da3 100644 --- a/vendor/github.com/prometheus/common/model/labelset.go +++ b/vendor/github.com/prometheus/common/model/labelset.go @@ -17,7 +17,6 @@ import ( "encoding/json" "fmt" "sort" - "strings" ) // A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet @@ -129,16 +128,6 @@ func (l LabelSet) Merge(other LabelSet) LabelSet { return result } -func (l LabelSet) String() string { - lstrs := make([]string, 0, len(l)) - for l, v := range l { - lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v)) - } - - sort.Strings(lstrs) - return fmt.Sprintf("{%s}", strings.Join(lstrs, ", ")) -} - // Fingerprint returns the LabelSet's fingerprint. func (ls LabelSet) Fingerprint() Fingerprint { return labelSetToFingerprint(ls) diff --git a/vendor/github.com/prometheus/common/model/labelset_string.go b/vendor/github.com/prometheus/common/model/labelset_string.go new file mode 100644 index 000000000..481c47b46 --- /dev/null +++ b/vendor/github.com/prometheus/common/model/labelset_string.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build go1.21 + +package model + +import ( + "bytes" + "slices" + "strconv" +) + +// String will look like `{foo="bar", more="less"}`. Names are sorted alphabetically. +func (l LabelSet) String() string { + var lna [32]string // On stack to avoid memory allocation for sorting names. + labelNames := lna[:0] + for name := range l { + labelNames = append(labelNames, string(name)) + } + slices.Sort(labelNames) + var bytea [1024]byte // On stack to avoid memory allocation while building the output. + b := bytes.NewBuffer(bytea[:0]) + b.WriteByte('{') + for i, name := range labelNames { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(name) + b.WriteByte('=') + b.Write(strconv.AppendQuote(b.AvailableBuffer(), string(l[LabelName(name)]))) + } + b.WriteByte('}') + return b.String() +} diff --git a/vendor/github.com/prometheus/common/model/labelset_string_go120.go b/vendor/github.com/prometheus/common/model/labelset_string_go120.go new file mode 100644 index 000000000..c4212685e --- /dev/null +++ b/vendor/github.com/prometheus/common/model/labelset_string_go120.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !go1.21 + +package model + +import ( + "fmt" + "sort" + "strings" +) + +// String was optimized using functions not available for go 1.20 +// or lower. We keep the old implementation for compatibility with client_golang. +// Once client golang drops support for go 1.20 (scheduled for August 2024), this +// file can be removed. +func (l LabelSet) String() string { + labelNames := make([]string, 0, len(l)) + for name := range l { + labelNames = append(labelNames, string(name)) + } + sort.Strings(labelNames) + lstrs := make([]string, 0, len(l)) + for _, name := range labelNames { + lstrs = append(lstrs, fmt.Sprintf("%s=%q", name, l[LabelName(name)])) + } + return fmt.Sprintf("{%s}", strings.Join(lstrs, ", ")) +} diff --git a/vendor/github.com/prometheus/common/model/metadata.go b/vendor/github.com/prometheus/common/model/metadata.go new file mode 100644 index 000000000..447ab8ad6 --- /dev/null +++ b/vendor/github.com/prometheus/common/model/metadata.go @@ -0,0 +1,28 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +// MetricType represents metric type values. +type MetricType string + +const ( + MetricTypeCounter = MetricType("counter") + MetricTypeGauge = MetricType("gauge") + MetricTypeHistogram = MetricType("histogram") + MetricTypeGaugeHistogram = MetricType("gaugehistogram") + MetricTypeSummary = MetricType("summary") + MetricTypeInfo = MetricType("info") + MetricTypeStateset = MetricType("stateset") + MetricTypeUnknown = MetricType("unknown") +) diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go index 00804b7fe..eb865e5a5 100644 --- a/vendor/github.com/prometheus/common/model/metric.go +++ b/vendor/github.com/prometheus/common/model/metric.go @@ -18,15 +18,84 @@ import ( "regexp" "sort" "strings" + "unicode/utf8" + + dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" ) var ( - // MetricNameRE is a regular expression matching valid metric - // names. Note that the IsValidMetricName function performs the same - // check but faster than a match with this regular expression. - MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`) + // NameValidationScheme determines the method of name validation to be used by + // all calls to IsValidMetricName() and LabelName IsValid(). Setting UTF-8 mode + // in isolation from other components that don't support UTF-8 may result in + // bugs or other undefined behavior. This value is intended to be set by + // UTF-8-aware binaries as part of their startup. To avoid need for locking, + // this value should be set once, ideally in an init(), before multiple + // goroutines are started. + NameValidationScheme = LegacyValidation + + // NameEscapingScheme defines the default way that names will be + // escaped when presented to systems that do not support UTF-8 names. If the + // Content-Type "escaping" term is specified, that will override this value. + NameEscapingScheme = ValueEncodingEscaping +) + +// ValidationScheme is a Go enum for determining how metric and label names will +// be validated by this library. +type ValidationScheme int + +const ( + // LegacyValidation is a setting that requirets that metric and label names + // conform to the original Prometheus character requirements described by + // MetricNameRE and LabelNameRE. + LegacyValidation ValidationScheme = iota + + // UTF8Validation only requires that metric and label names be valid UTF-8 + // strings. + UTF8Validation +) + +type EscapingScheme int + +const ( + // NoEscaping indicates that a name will not be escaped. Unescaped names that + // do not conform to the legacy validity check will use a new exposition + // format syntax that will be officially standardized in future versions. + NoEscaping EscapingScheme = iota + + // UnderscoreEscaping replaces all legacy-invalid characters with underscores. + UnderscoreEscaping + + // DotsEscaping is similar to UnderscoreEscaping, except that dots are + // converted to `_dot_` and pre-existing underscores are converted to `__`. + DotsEscaping + + // ValueEncodingEscaping prepends the name with `U__` and replaces all invalid + // characters with the unicode value, surrounded by underscores. Single + // underscores are replaced with double underscores. + ValueEncodingEscaping +) + +const ( + // EscapingKey is the key in an Accept or Content-Type header that defines how + // metric and label names that do not conform to the legacy character + // requirements should be escaped when being scraped by a legacy prometheus + // system. If a system does not explicitly pass an escaping parameter in the + // Accept header, the default NameEscapingScheme will be used. + EscapingKey = "escaping" + + // Possible values for Escaping Key: + AllowUTF8 = "allow-utf-8" // No escaping required. + EscapeUnderscores = "underscores" + EscapeDots = "dots" + EscapeValues = "values" ) +// MetricNameRE is a regular expression matching valid metric +// names. Note that the IsValidMetricName function performs the same +// check but faster than a match with this regular expression. +var MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`) + // A Metric is similar to a LabelSet, but the key difference is that a Metric is // a singleton and refers to one and only one stream of samples. type Metric LabelSet @@ -86,17 +155,303 @@ func (m Metric) FastFingerprint() Fingerprint { return LabelSet(m).FastFingerprint() } -// IsValidMetricName returns true iff name matches the pattern of MetricNameRE. +// IsValidMetricName returns true iff name matches the pattern of MetricNameRE +// for legacy names, and iff it's valid UTF-8 if the UTF8Validation scheme is +// selected. +func IsValidMetricName(n LabelValue) bool { + switch NameValidationScheme { + case LegacyValidation: + return IsValidLegacyMetricName(n) + case UTF8Validation: + if len(n) == 0 { + return false + } + return utf8.ValidString(string(n)) + default: + panic(fmt.Sprintf("Invalid name validation scheme requested: %d", NameValidationScheme)) + } +} + +// IsValidLegacyMetricName is similar to IsValidMetricName but always uses the +// legacy validation scheme regardless of the value of NameValidationScheme. // This function, however, does not use MetricNameRE for the check but a much // faster hardcoded implementation. -func IsValidMetricName(n LabelValue) bool { +func IsValidLegacyMetricName(n LabelValue) bool { if len(n) == 0 { return false } for i, b := range n { - if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) { + if !isValidLegacyRune(b, i) { return false } } return true } + +// EscapeMetricFamily escapes the given metric names and labels with the given +// escaping scheme. Returns a new object that uses the same pointers to fields +// when possible and creates new escaped versions so as not to mutate the +// input. +func EscapeMetricFamily(v *dto.MetricFamily, scheme EscapingScheme) *dto.MetricFamily { + if v == nil { + return nil + } + + if scheme == NoEscaping { + return v + } + + out := &dto.MetricFamily{ + Help: v.Help, + Type: v.Type, + Unit: v.Unit, + } + + // If the name is nil, copy as-is, don't try to escape. + if v.Name == nil || IsValidLegacyMetricName(LabelValue(v.GetName())) { + out.Name = v.Name + } else { + out.Name = proto.String(EscapeName(v.GetName(), scheme)) + } + for _, m := range v.Metric { + if !metricNeedsEscaping(m) { + out.Metric = append(out.Metric, m) + continue + } + + escaped := &dto.Metric{ + Gauge: m.Gauge, + Counter: m.Counter, + Summary: m.Summary, + Untyped: m.Untyped, + Histogram: m.Histogram, + TimestampMs: m.TimestampMs, + } + + for _, l := range m.Label { + if l.GetName() == MetricNameLabel { + if l.Value == nil || IsValidLegacyMetricName(LabelValue(l.GetValue())) { + escaped.Label = append(escaped.Label, l) + continue + } + escaped.Label = append(escaped.Label, &dto.LabelPair{ + Name: proto.String(MetricNameLabel), + Value: proto.String(EscapeName(l.GetValue(), scheme)), + }) + continue + } + if l.Name == nil || IsValidLegacyMetricName(LabelValue(l.GetName())) { + escaped.Label = append(escaped.Label, l) + continue + } + escaped.Label = append(escaped.Label, &dto.LabelPair{ + Name: proto.String(EscapeName(l.GetName(), scheme)), + Value: l.Value, + }) + } + out.Metric = append(out.Metric, escaped) + } + return out +} + +func metricNeedsEscaping(m *dto.Metric) bool { + for _, l := range m.Label { + if l.GetName() == MetricNameLabel && !IsValidLegacyMetricName(LabelValue(l.GetValue())) { + return true + } + if !IsValidLegacyMetricName(LabelValue(l.GetName())) { + return true + } + } + return false +} + +const ( + lowerhex = "0123456789abcdef" +) + +// EscapeName escapes the incoming name according to the provided escaping +// scheme. Depending on the rules of escaping, this may cause no change in the +// string that is returned. (Especially NoEscaping, which by definition is a +// noop). This function does not do any validation of the name. +func EscapeName(name string, scheme EscapingScheme) string { + if len(name) == 0 { + return name + } + var escaped strings.Builder + switch scheme { + case NoEscaping: + return name + case UnderscoreEscaping: + if IsValidLegacyMetricName(LabelValue(name)) { + return name + } + for i, b := range name { + if isValidLegacyRune(b, i) { + escaped.WriteRune(b) + } else { + escaped.WriteRune('_') + } + } + return escaped.String() + case DotsEscaping: + // Do not early return for legacy valid names, we still escape underscores. + for i, b := range name { + if b == '_' { + escaped.WriteString("__") + } else if b == '.' { + escaped.WriteString("_dot_") + } else if isValidLegacyRune(b, i) { + escaped.WriteRune(b) + } else { + escaped.WriteRune('_') + } + } + return escaped.String() + case ValueEncodingEscaping: + if IsValidLegacyMetricName(LabelValue(name)) { + return name + } + escaped.WriteString("U__") + for i, b := range name { + if isValidLegacyRune(b, i) { + escaped.WriteRune(b) + } else if !utf8.ValidRune(b) { + escaped.WriteString("_FFFD_") + } else if b < 0x100 { + escaped.WriteRune('_') + for s := 4; s >= 0; s -= 4 { + escaped.WriteByte(lowerhex[b>>uint(s)&0xF]) + } + escaped.WriteRune('_') + } else if b < 0x10000 { + escaped.WriteRune('_') + for s := 12; s >= 0; s -= 4 { + escaped.WriteByte(lowerhex[b>>uint(s)&0xF]) + } + escaped.WriteRune('_') + } + } + return escaped.String() + default: + panic(fmt.Sprintf("invalid escaping scheme %d", scheme)) + } +} + +// lower function taken from strconv.atoi +func lower(c byte) byte { + return c | ('x' - 'X') +} + +// UnescapeName unescapes the incoming name according to the provided escaping +// scheme if possible. Some schemes are partially or totally non-roundtripable. +// If any error is enountered, returns the original input. +func UnescapeName(name string, scheme EscapingScheme) string { + if len(name) == 0 { + return name + } + switch scheme { + case NoEscaping: + return name + case UnderscoreEscaping: + // It is not possible to unescape from underscore replacement. + return name + case DotsEscaping: + name = strings.ReplaceAll(name, "_dot_", ".") + name = strings.ReplaceAll(name, "__", "_") + return name + case ValueEncodingEscaping: + escapedName, found := strings.CutPrefix(name, "U__") + if !found { + return name + } + + var unescaped strings.Builder + TOP: + for i := 0; i < len(escapedName); i++ { + // All non-underscores are treated normally. + if escapedName[i] != '_' { + unescaped.WriteByte(escapedName[i]) + continue + } + i++ + if i >= len(escapedName) { + return name + } + // A double underscore is a single underscore. + if escapedName[i] == '_' { + unescaped.WriteByte('_') + continue + } + // We think we are in a UTF-8 code, process it. + var utf8Val uint + for j := 0; i < len(escapedName); j++ { + // This is too many characters for a utf8 value. + if j > 4 { + return name + } + // Found a closing underscore, convert to a rune, check validity, and append. + if escapedName[i] == '_' { + utf8Rune := rune(utf8Val) + if !utf8.ValidRune(utf8Rune) { + return name + } + unescaped.WriteRune(utf8Rune) + continue TOP + } + r := lower(escapedName[i]) + utf8Val *= 16 + if r >= '0' && r <= '9' { + utf8Val += uint(r) - '0' + } else if r >= 'a' && r <= 'f' { + utf8Val += uint(r) - 'a' + 10 + } else { + return name + } + i++ + } + // Didn't find closing underscore, invalid. + return name + } + return unescaped.String() + default: + panic(fmt.Sprintf("invalid escaping scheme %d", scheme)) + } +} + +func isValidLegacyRune(b rune, i int) bool { + return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0) +} + +func (e EscapingScheme) String() string { + switch e { + case NoEscaping: + return AllowUTF8 + case UnderscoreEscaping: + return EscapeUnderscores + case DotsEscaping: + return EscapeDots + case ValueEncodingEscaping: + return EscapeValues + default: + panic(fmt.Sprintf("unknown format scheme %d", e)) + } +} + +func ToEscapingScheme(s string) (EscapingScheme, error) { + if s == "" { + return NoEscaping, fmt.Errorf("got empty string instead of escaping scheme") + } + switch s { + case AllowUTF8: + return NoEscaping, nil + case EscapeUnderscores: + return UnderscoreEscaping, nil + case EscapeDots: + return DotsEscaping, nil + case EscapeValues: + return ValueEncodingEscaping, nil + default: + return NoEscaping, fmt.Errorf("unknown format scheme " + s) + } +} diff --git a/vendor/github.com/prometheus/common/model/signature.go b/vendor/github.com/prometheus/common/model/signature.go index 8762b13c6..dc8a0026c 100644 --- a/vendor/github.com/prometheus/common/model/signature.go +++ b/vendor/github.com/prometheus/common/model/signature.go @@ -22,10 +22,8 @@ import ( // when calculating their combined hash value (aka signature aka fingerprint). const SeparatorByte byte = 255 -var ( - // cache the signature of an empty label set. - emptyLabelSignature = hashNew() -) +// cache the signature of an empty label set. +var emptyLabelSignature = hashNew() // LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a // given label set. (Collisions are possible but unlikely if the number of label diff --git a/vendor/github.com/prometheus/common/model/silence.go b/vendor/github.com/prometheus/common/model/silence.go index bb99889d2..910b0b71f 100644 --- a/vendor/github.com/prometheus/common/model/silence.go +++ b/vendor/github.com/prometheus/common/model/silence.go @@ -81,7 +81,7 @@ func (s *Silence) Validate() error { } for _, m := range s.Matchers { if err := m.Validate(); err != nil { - return fmt.Errorf("invalid matcher: %s", err) + return fmt.Errorf("invalid matcher: %w", err) } } if s.StartsAt.IsZero() { diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index c909b8aa8..5727452c1 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -18,7 +18,6 @@ import ( "errors" "fmt" "math" - "regexp" "strconv" "strings" "time" @@ -183,54 +182,78 @@ func (d *Duration) Type() string { return "duration" } -var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") +func isdigit(c byte) bool { return c >= '0' && c <= '9' } + +// Units are required to go in order from biggest to smallest. +// This guards against confusion from "1m1d" being 1 minute + 1 day, not 1 month + 1 day. +var unitMap = map[string]struct { + pos int + mult uint64 +}{ + "ms": {7, uint64(time.Millisecond)}, + "s": {6, uint64(time.Second)}, + "m": {5, uint64(time.Minute)}, + "h": {4, uint64(time.Hour)}, + "d": {3, uint64(24 * time.Hour)}, + "w": {2, uint64(7 * 24 * time.Hour)}, + "y": {1, uint64(365 * 24 * time.Hour)}, +} // ParseDuration parses a string into a time.Duration, assuming that a year // always has 365d, a week always has 7d, and a day always has 24h. -func ParseDuration(durationStr string) (Duration, error) { - switch durationStr { +func ParseDuration(s string) (Duration, error) { + switch s { case "0": // Allow 0 without a unit. return 0, nil case "": return 0, errors.New("empty duration string") } - matches := durationRE.FindStringSubmatch(durationStr) - if matches == nil { - return 0, fmt.Errorf("not a valid duration string: %q", durationStr) - } - var dur time.Duration - // Parse the match at pos `pos` in the regex and use `mult` to turn that - // into ms, then add that value to the total parsed duration. - var overflowErr error - m := func(pos int, mult time.Duration) { - if matches[pos] == "" { - return + orig := s + var dur uint64 + lastUnitPos := 0 + + for s != "" { + if !isdigit(s[0]) { + return 0, fmt.Errorf("not a valid duration string: %q", orig) + } + // Consume [0-9]* + i := 0 + for ; i < len(s) && isdigit(s[i]); i++ { + } + v, err := strconv.ParseUint(s[:i], 10, 0) + if err != nil { + return 0, fmt.Errorf("not a valid duration string: %q", orig) } - n, _ := strconv.Atoi(matches[pos]) + s = s[i:] + // Consume unit. + for i = 0; i < len(s) && !isdigit(s[i]); i++ { + } + if i == 0 { + return 0, fmt.Errorf("not a valid duration string: %q", orig) + } + u := s[:i] + s = s[i:] + unit, ok := unitMap[u] + if !ok { + return 0, fmt.Errorf("unknown unit %q in duration %q", u, orig) + } + if unit.pos <= lastUnitPos { // Units must go in order from biggest to smallest. + return 0, fmt.Errorf("not a valid duration string: %q", orig) + } + lastUnitPos = unit.pos // Check if the provided duration overflows time.Duration (> ~ 290years). - if n > int((1<<63-1)/mult/time.Millisecond) { - overflowErr = errors.New("duration out of range") + if v > 1<<63/unit.mult { + return 0, errors.New("duration out of range") } - d := time.Duration(n) * time.Millisecond - dur += d * mult - - if dur < 0 { - overflowErr = errors.New("duration out of range") + dur += v * unit.mult + if dur > 1<<63-1 { + return 0, errors.New("duration out of range") } } - - m(2, 1000*60*60*24*365) // y - m(4, 1000*60*60*24*7) // w - m(6, 1000*60*60*24) // d - m(8, 1000*60*60) // h - m(10, 1000*60) // m - m(12, 1000) // s - m(14, 1) // ms - - return Duration(dur), overflowErr + return Duration(dur), nil } func (d Duration) String() string { diff --git a/vendor/github.com/prometheus/common/model/value.go b/vendor/github.com/prometheus/common/model/value.go index c9d8fb1a2..8050637d8 100644 --- a/vendor/github.com/prometheus/common/model/value.go +++ b/vendor/github.com/prometheus/common/model/value.go @@ -16,104 +16,26 @@ package model import ( "encoding/json" "fmt" - "math" "sort" "strconv" "strings" ) -var ( - // ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a - // non-existing sample pair. It is a SamplePair with timestamp Earliest and - // value 0.0. Note that the natural zero value of SamplePair has a timestamp - // of 0, which is possible to appear in a real SamplePair and thus not - // suitable to signal a non-existing SamplePair. - ZeroSamplePair = SamplePair{Timestamp: Earliest} - - // ZeroSample is the pseudo zero-value of Sample used to signal a - // non-existing sample. It is a Sample with timestamp Earliest, value 0.0, - // and metric nil. Note that the natural zero value of Sample has a timestamp - // of 0, which is possible to appear in a real Sample and thus not suitable - // to signal a non-existing Sample. - ZeroSample = Sample{Timestamp: Earliest} -) - -// A SampleValue is a representation of a value for a given sample at a given -// time. -type SampleValue float64 - -// MarshalJSON implements json.Marshaler. -func (v SampleValue) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *SampleValue) UnmarshalJSON(b []byte) error { - if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("sample value must be a quoted string") - } - f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) - if err != nil { - return err - } - *v = SampleValue(f) - return nil -} - -// Equal returns true if the value of v and o is equal or if both are NaN. Note -// that v==o is false if both are NaN. If you want the conventional float -// behavior, use == to compare two SampleValues. -func (v SampleValue) Equal(o SampleValue) bool { - if v == o { - return true - } - return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) -} - -func (v SampleValue) String() string { - return strconv.FormatFloat(float64(v), 'f', -1, 64) -} - -// SamplePair pairs a SampleValue with a Timestamp. -type SamplePair struct { - Timestamp Time - Value SampleValue -} - -// MarshalJSON implements json.Marshaler. -func (s SamplePair) MarshalJSON() ([]byte, error) { - t, err := json.Marshal(s.Timestamp) - if err != nil { - return nil, err - } - v, err := json.Marshal(s.Value) - if err != nil { - return nil, err - } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (s *SamplePair) UnmarshalJSON(b []byte) error { - v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} - return json.Unmarshal(b, &v) -} - -// Equal returns true if this SamplePair and o have equal Values and equal -// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. -func (s *SamplePair) Equal(o *SamplePair) bool { - return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) -} - -func (s SamplePair) String() string { - return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) -} +// ZeroSample is the pseudo zero-value of Sample used to signal a +// non-existing sample. It is a Sample with timestamp Earliest, value 0.0, +// and metric nil. Note that the natural zero value of Sample has a timestamp +// of 0, which is possible to appear in a real Sample and thus not suitable +// to signal a non-existing Sample. +var ZeroSample = Sample{Timestamp: Earliest} -// Sample is a sample pair associated with a metric. +// Sample is a sample pair associated with a metric. A single sample must either +// define Value or Histogram but not both. Histogram == nil implies the Value +// field is used, otherwise it should be ignored. type Sample struct { - Metric Metric `json:"metric"` - Value SampleValue `json:"value"` - Timestamp Time `json:"timestamp"` + Metric Metric `json:"metric"` + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` + Histogram *SampleHistogram `json:"histogram"` } // Equal compares first the metrics, then the timestamp, then the value. The @@ -129,11 +51,19 @@ func (s *Sample) Equal(o *Sample) bool { if !s.Timestamp.Equal(o.Timestamp) { return false } - + if s.Histogram != nil { + return s.Histogram.Equal(o.Histogram) + } return s.Value.Equal(o.Value) } func (s Sample) String() string { + if s.Histogram != nil { + return fmt.Sprintf("%s => %s", s.Metric, SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }) + } return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ Timestamp: s.Timestamp, Value: s.Value, @@ -142,6 +72,19 @@ func (s Sample) String() string { // MarshalJSON implements json.Marshaler. func (s Sample) MarshalJSON() ([]byte, error) { + if s.Histogram != nil { + v := struct { + Metric Metric `json:"metric"` + Histogram SampleHistogramPair `json:"histogram"` + }{ + Metric: s.Metric, + Histogram: SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }, + } + return json.Marshal(&v) + } v := struct { Metric Metric `json:"metric"` Value SamplePair `json:"value"` @@ -152,21 +95,25 @@ func (s Sample) MarshalJSON() ([]byte, error) { Value: s.Value, }, } - return json.Marshal(&v) } // UnmarshalJSON implements json.Unmarshaler. func (s *Sample) UnmarshalJSON(b []byte) error { v := struct { - Metric Metric `json:"metric"` - Value SamplePair `json:"value"` + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + Histogram SampleHistogramPair `json:"histogram"` }{ Metric: s.Metric, Value: SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }, + Histogram: SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }, } if err := json.Unmarshal(b, &v); err != nil { @@ -174,8 +121,13 @@ func (s *Sample) UnmarshalJSON(b []byte) error { } s.Metric = v.Metric - s.Timestamp = v.Value.Timestamp - s.Value = v.Value.Value + if v.Histogram.Histogram != nil { + s.Timestamp = v.Histogram.Timestamp + s.Histogram = v.Histogram.Histogram + } else { + s.Timestamp = v.Value.Timestamp + s.Value = v.Value.Value + } return nil } @@ -221,80 +173,76 @@ func (s Samples) Equal(o Samples) bool { // SampleStream is a stream of Values belonging to an attached COWMetric. type SampleStream struct { - Metric Metric `json:"metric"` - Values []SamplePair `json:"values"` + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` } func (ss SampleStream) String() string { - vals := make([]string, len(ss.Values)) + valuesLength := len(ss.Values) + vals := make([]string, valuesLength+len(ss.Histograms)) for i, v := range ss.Values { vals[i] = v.String() } + for i, v := range ss.Histograms { + vals[i+valuesLength] = v.String() + } return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) } -// Value is a generic interface for values resulting from a query evaluation. -type Value interface { - Type() ValueType - String() string +func (ss SampleStream) MarshalJSON() ([]byte, error) { + if len(ss.Histograms) > 0 && len(ss.Values) > 0 { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Values: ss.Values, + Histograms: ss.Histograms, + } + return json.Marshal(&v) + } else if len(ss.Histograms) > 0 { + v := struct { + Metric Metric `json:"metric"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Histograms: ss.Histograms, + } + return json.Marshal(&v) + } else { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + }{ + Metric: ss.Metric, + Values: ss.Values, + } + return json.Marshal(&v) + } } -func (Matrix) Type() ValueType { return ValMatrix } -func (Vector) Type() ValueType { return ValVector } -func (*Scalar) Type() ValueType { return ValScalar } -func (*String) Type() ValueType { return ValString } - -type ValueType int - -const ( - ValNone ValueType = iota - ValScalar - ValVector - ValMatrix - ValString -) - -// MarshalJSON implements json.Marshaler. -func (et ValueType) MarshalJSON() ([]byte, error) { - return json.Marshal(et.String()) -} +func (ss *SampleStream) UnmarshalJSON(b []byte) error { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Values: ss.Values, + Histograms: ss.Histograms, + } -func (et *ValueType) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { + if err := json.Unmarshal(b, &v); err != nil { return err } - switch s { - case "": - *et = ValNone - case "scalar": - *et = ValScalar - case "vector": - *et = ValVector - case "matrix": - *et = ValMatrix - case "string": - *et = ValString - default: - return fmt.Errorf("unknown value type %q", s) - } - return nil -} -func (e ValueType) String() string { - switch e { - case ValNone: - return "" - case ValScalar: - return "scalar" - case ValVector: - return "vector" - case ValMatrix: - return "matrix" - case ValString: - return "string" - } - panic("ValueType.String: unhandled value type") + ss.Metric = v.Metric + ss.Values = v.Values + ss.Histograms = v.Histograms + + return nil } // Scalar is a scalar value evaluated at the set timestamp. @@ -324,7 +272,7 @@ func (s *Scalar) UnmarshalJSON(b []byte) error { value, err := strconv.ParseFloat(f, 64) if err != nil { - return fmt.Errorf("error parsing sample value: %s", err) + return fmt.Errorf("error parsing sample value: %w", err) } s.Value = SampleValue(value) return nil diff --git a/vendor/github.com/prometheus/common/model/value_float.go b/vendor/github.com/prometheus/common/model/value_float.go new file mode 100644 index 000000000..ae35cc2ab --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_float.go @@ -0,0 +1,98 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" + "math" + "strconv" +) + +// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a +// non-existing sample pair. It is a SamplePair with timestamp Earliest and +// value 0.0. Note that the natural zero value of SamplePair has a timestamp +// of 0, which is possible to appear in a real SamplePair and thus not +// suitable to signal a non-existing SamplePair. +var ZeroSamplePair = SamplePair{Timestamp: Earliest} + +// A SampleValue is a representation of a value for a given sample at a given +// time. +type SampleValue float64 + +// MarshalJSON implements json.Marshaler. +func (v SampleValue) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *SampleValue) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("sample value must be a quoted string") + } + f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) + if err != nil { + return err + } + *v = SampleValue(f) + return nil +} + +// Equal returns true if the value of v and o is equal or if both are NaN. Note +// that v==o is false if both are NaN. If you want the conventional float +// behavior, use == to compare two SampleValues. +func (v SampleValue) Equal(o SampleValue) bool { + if v == o { + return true + } + return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) +} + +func (v SampleValue) String() string { + return strconv.FormatFloat(float64(v), 'f', -1, 64) +} + +// SamplePair pairs a SampleValue with a Timestamp. +type SamplePair struct { + Timestamp Time + Value SampleValue +} + +func (s SamplePair) MarshalJSON() ([]byte, error) { + t, err := json.Marshal(s.Timestamp) + if err != nil { + return nil, err + } + v, err := json.Marshal(s.Value) + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *SamplePair) UnmarshalJSON(b []byte) error { + v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) +} + +// Equal returns true if this SamplePair and o have equal Values and equal +// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. +func (s *SamplePair) Equal(o *SamplePair) bool { + return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) +} + +func (s SamplePair) String() string { + return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) +} diff --git a/vendor/github.com/prometheus/common/model/value_histogram.go b/vendor/github.com/prometheus/common/model/value_histogram.go new file mode 100644 index 000000000..54bb038cf --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_histogram.go @@ -0,0 +1,178 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +type FloatString float64 + +func (v FloatString) String() string { + return strconv.FormatFloat(float64(v), 'f', -1, 64) +} + +func (v FloatString) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +func (v *FloatString) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("float value must be a quoted string") + } + f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) + if err != nil { + return err + } + *v = FloatString(f) + return nil +} + +type HistogramBucket struct { + Boundaries int32 + Lower FloatString + Upper FloatString + Count FloatString +} + +func (s HistogramBucket) MarshalJSON() ([]byte, error) { + b, err := json.Marshal(s.Boundaries) + if err != nil { + return nil, err + } + l, err := json.Marshal(s.Lower) + if err != nil { + return nil, err + } + u, err := json.Marshal(s.Upper) + if err != nil { + return nil, err + } + c, err := json.Marshal(s.Count) + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil +} + +func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} + wantLen := len(tmp) + if err := json.Unmarshal(buf, &tmp); err != nil { + return err + } + if gotLen := len(tmp); gotLen != wantLen { + return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) + } + return nil +} + +func (s *HistogramBucket) Equal(o *HistogramBucket) bool { + return s == o || (s.Boundaries == o.Boundaries && s.Lower == o.Lower && s.Upper == o.Upper && s.Count == o.Count) +} + +func (b HistogramBucket) String() string { + var sb strings.Builder + lowerInclusive := b.Boundaries == 1 || b.Boundaries == 3 + upperInclusive := b.Boundaries == 0 || b.Boundaries == 3 + if lowerInclusive { + sb.WriteRune('[') + } else { + sb.WriteRune('(') + } + fmt.Fprintf(&sb, "%g,%g", b.Lower, b.Upper) + if upperInclusive { + sb.WriteRune(']') + } else { + sb.WriteRune(')') + } + fmt.Fprintf(&sb, ":%v", b.Count) + return sb.String() +} + +type HistogramBuckets []*HistogramBucket + +func (s HistogramBuckets) Equal(o HistogramBuckets) bool { + if len(s) != len(o) { + return false + } + + for i, bucket := range s { + if !bucket.Equal(o[i]) { + return false + } + } + return true +} + +type SampleHistogram struct { + Count FloatString `json:"count"` + Sum FloatString `json:"sum"` + Buckets HistogramBuckets `json:"buckets"` +} + +func (s SampleHistogram) String() string { + return fmt.Sprintf("Count: %f, Sum: %f, Buckets: %v", s.Count, s.Sum, s.Buckets) +} + +func (s *SampleHistogram) Equal(o *SampleHistogram) bool { + return s == o || (s.Count == o.Count && s.Sum == o.Sum && s.Buckets.Equal(o.Buckets)) +} + +type SampleHistogramPair struct { + Timestamp Time + // Histogram should never be nil, it's only stored as pointer for efficiency. + Histogram *SampleHistogram +} + +func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { + if s.Histogram == nil { + return nil, fmt.Errorf("histogram is nil") + } + t, err := json.Marshal(s.Timestamp) + if err != nil { + return nil, err + } + v, err := json.Marshal(s.Histogram) + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil +} + +func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&s.Timestamp, &s.Histogram} + wantLen := len(tmp) + if err := json.Unmarshal(buf, &tmp); err != nil { + return err + } + if gotLen := len(tmp); gotLen != wantLen { + return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) + } + if s.Histogram == nil { + return fmt.Errorf("histogram is null") + } + return nil +} + +func (s SampleHistogramPair) String() string { + return fmt.Sprintf("%s @[%s]", s.Histogram, s.Timestamp) +} + +func (s *SampleHistogramPair) Equal(o *SampleHistogramPair) bool { + return s == o || (s.Histogram.Equal(o.Histogram) && s.Timestamp.Equal(o.Timestamp)) +} diff --git a/vendor/github.com/prometheus/common/model/value_type.go b/vendor/github.com/prometheus/common/model/value_type.go new file mode 100644 index 000000000..726c50ee6 --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_type.go @@ -0,0 +1,83 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" +) + +// Value is a generic interface for values resulting from a query evaluation. +type Value interface { + Type() ValueType + String() string +} + +func (Matrix) Type() ValueType { return ValMatrix } +func (Vector) Type() ValueType { return ValVector } +func (*Scalar) Type() ValueType { return ValScalar } +func (*String) Type() ValueType { return ValString } + +type ValueType int + +const ( + ValNone ValueType = iota + ValScalar + ValVector + ValMatrix + ValString +) + +// MarshalJSON implements json.Marshaler. +func (et ValueType) MarshalJSON() ([]byte, error) { + return json.Marshal(et.String()) +} + +func (et *ValueType) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + switch s { + case "": + *et = ValNone + case "scalar": + *et = ValScalar + case "vector": + *et = ValVector + case "matrix": + *et = ValMatrix + case "string": + *et = ValString + default: + return fmt.Errorf("unknown value type %q", s) + } + return nil +} + +func (e ValueType) String() string { + switch e { + case ValNone: + return "" + case ValScalar: + return "scalar" + case ValVector: + return "vector" + case ValMatrix: + return "matrix" + case ValString: + return "string" + } + panic("ValueType.String: unhandled value type") +} diff --git a/vendor/github.com/prometheus/procfs/.golangci.yml b/vendor/github.com/prometheus/procfs/.golangci.yml index a197699a1..c24864a92 100644 --- a/vendor/github.com/prometheus/procfs/.golangci.yml +++ b/vendor/github.com/prometheus/procfs/.golangci.yml @@ -2,6 +2,7 @@ linters: enable: - godot + - misspell - revive linter-settings: @@ -10,3 +11,5 @@ linter-settings: exclude: # Ignore "See: URL" - 'See:' + misspell: + locale: US diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index 6c8e3e219..062a28185 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -49,25 +49,28 @@ endif GOTEST := $(GO) test GOTEST_DIR := ifneq ($(CIRCLE_JOB),) -ifneq ($(shell which gotestsum),) +ifneq ($(shell command -v gotestsum > /dev/null),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif -PROMU_VERSION ?= 0.13.0 +PROMU_VERSION ?= 0.15.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz +SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.45.2 +GOLANGCI_LINT_VERSION ?= v1.54.2 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) # If we're in CI and there is an Actions file, that means the linter # is being run in Actions, so we don't need to run it here. - ifeq (,$(CIRCLE_JOB)) + ifneq (,$(SKIP_GOLANGCI_LINT)) + GOLANGCI_LINT := + else ifeq (,$(CIRCLE_JOB)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint @@ -88,6 +91,8 @@ BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) +SANITIZED_DOCKER_IMAGE_TAG := $(subst +,-,$(DOCKER_IMAGE_TAG)) + ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows)) # Only supported on amd64 @@ -173,7 +178,7 @@ endif .PHONY: common-yamllint common-yamllint: @echo ">> running yamllint on all YAML files in the repository" -ifeq (, $(shell which yamllint)) +ifeq (, $(shell command -v yamllint > /dev/null)) @echo "yamllint not installed so skipping" else yamllint . @@ -202,7 +207,7 @@ common-tarball: promu .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: - docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \ + docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \ -f $(DOCKERFILE_PATH) \ --build-arg ARCH="$*" \ --build-arg OS="linux" \ @@ -211,19 +216,19 @@ $(BUILD_DOCKER_ARCHS): common-docker-%: .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: - docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: - docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" - docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" .PHONY: common-docker-manifest common-docker-manifest: - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG)) - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)) + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" .PHONY: promu promu: $(PROMU) diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 43c37735a..1224816c2 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -51,11 +51,11 @@ ensure the `fixtures` directory is up to date by removing the existing directory extracting the ttar file using `make fixtures/.unpacked` or just `make test`. ```bash -rm -rf fixtures +rm -rf testdata/fixtures make test ``` Next, make the required changes to the extracted files in the `fixtures` directory. When the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file based on the updated `fixtures` directory. And finally, verify the changes using -`git diff fixtures.ttar`. +`git diff testdata/fixtures.ttar`. diff --git a/vendor/github.com/prometheus/procfs/arp.go b/vendor/github.com/prometheus/procfs/arp.go index 68f36e888..28783e2dd 100644 --- a/vendor/github.com/prometheus/procfs/arp.go +++ b/vendor/github.com/prometheus/procfs/arp.go @@ -55,7 +55,7 @@ type ARPEntry struct { func (fs FS) GatherARPEntries() ([]ARPEntry, error) { data, err := os.ReadFile(fs.proc.Path("net/arp")) if err != nil { - return nil, fmt.Errorf("error reading arp %q: %w", fs.proc.Path("net/arp"), err) + return nil, fmt.Errorf("%s: error reading arp %s: %w", ErrFileRead, fs.proc.Path("net/arp"), err) } return parseARPEntries(data) @@ -78,11 +78,11 @@ func parseARPEntries(data []byte) ([]ARPEntry, error) { } else if width == expectedDataWidth { entry, err := parseARPEntry(columns) if err != nil { - return []ARPEntry{}, fmt.Errorf("failed to parse ARP entry: %w", err) + return []ARPEntry{}, fmt.Errorf("%s: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err) } entries = append(entries, entry) } else { - return []ARPEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedDataWidth) + return []ARPEntry{}, fmt.Errorf("%s: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err) } } diff --git a/vendor/github.com/prometheus/procfs/buddyinfo.go b/vendor/github.com/prometheus/procfs/buddyinfo.go index f5b7939b2..4a173636c 100644 --- a/vendor/github.com/prometheus/procfs/buddyinfo.go +++ b/vendor/github.com/prometheus/procfs/buddyinfo.go @@ -55,7 +55,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { parts := strings.Fields(line) if len(parts) < 4 { - return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo") + return nil, fmt.Errorf("%w: Invalid number of fields, found: %v", ErrFileParse, parts) } node := strings.TrimRight(parts[1], ",") @@ -66,7 +66,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { bucketCount = arraySize } else { if bucketCount != arraySize { - return nil, fmt.Errorf("mismatch in number of buddyinfo buckets, previous count %d, new count %d", bucketCount, arraySize) + return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize) } } @@ -74,7 +74,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { for i := 0; i < arraySize; i++ { sizes[i], err = strconv.ParseFloat(parts[i+4], 64) if err != nil { - return nil, fmt.Errorf("invalid value in buddyinfo: %w", err) + return nil, fmt.Errorf("%s: Invalid valid in buddyinfo: %f: %w", ErrFileParse, sizes[i], err) } } diff --git a/vendor/github.com/prometheus/procfs/cpuinfo.go b/vendor/github.com/prometheus/procfs/cpuinfo.go index ff6b927da..f4f5501c6 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo.go @@ -79,7 +79,7 @@ func parseCPUInfoX86(info []byte) ([]CPUInfo, error) { // find the first "processor" line firstLine := firstNonEmptyLine(scanner) if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine) } field := strings.SplitN(firstLine, ": ", 2) v, err := strconv.ParseUint(field[1], 0, 32) @@ -192,9 +192,10 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(info)) firstLine := firstNonEmptyLine(scanner) - match, _ := regexp.MatchString("^[Pp]rocessor", firstLine) + match, err := regexp.MatchString("^[Pp]rocessor", firstLine) if !match || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%s: Cannot parse line: %q: %w", ErrFileParse, firstLine, err) + } field := strings.SplitN(firstLine, ": ", 2) cpuinfo := []CPUInfo{} @@ -258,7 +259,7 @@ func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) { firstLine := firstNonEmptyLine(scanner) if !strings.HasPrefix(firstLine, "vendor_id") || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine) } field := strings.SplitN(firstLine, ": ", 2) cpuinfo := []CPUInfo{} @@ -283,7 +284,7 @@ func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) { if strings.HasPrefix(line, "processor") { match := cpuinfoS390XProcessorRegexp.FindStringSubmatch(line) if len(match) < 2 { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine) } cpu := commonCPUInfo v, err := strconv.ParseUint(match[1], 0, 32) @@ -343,7 +344,7 @@ func parseCPUInfoMips(info []byte) ([]CPUInfo, error) { // find the first "processor" line firstLine := firstNonEmptyLine(scanner) if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine) } field := strings.SplitN(firstLine, ": ", 2) cpuinfo := []CPUInfo{} @@ -380,12 +381,48 @@ func parseCPUInfoMips(info []byte) ([]CPUInfo, error) { return cpuinfo, nil } +func parseCPUInfoLoong(info []byte) ([]CPUInfo, error) { + scanner := bufio.NewScanner(bytes.NewReader(info)) + // find the first "processor" line + firstLine := firstNonEmptyLine(scanner) + if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") { + return nil, errors.New("invalid cpuinfo file: " + firstLine) + } + field := strings.SplitN(firstLine, ": ", 2) + cpuinfo := []CPUInfo{} + systemType := field[1] + i := 0 + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, ":") { + continue + } + field := strings.SplitN(line, ": ", 2) + switch strings.TrimSpace(field[0]) { + case "processor": + v, err := strconv.ParseUint(field[1], 0, 32) + if err != nil { + return nil, err + } + i = int(v) + cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor + cpuinfo[i].Processor = uint(v) + cpuinfo[i].VendorID = systemType + case "CPU Family": + cpuinfo[i].CPUFamily = field[1] + case "Model Name": + cpuinfo[i].ModelName = field[1] + } + } + return cpuinfo, nil +} + func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(info)) firstLine := firstNonEmptyLine(scanner) if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine) } field := strings.SplitN(firstLine, ": ", 2) v, err := strconv.ParseUint(field[1], 0, 32) @@ -430,7 +467,7 @@ func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) { firstLine := firstNonEmptyLine(scanner) if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { - return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) + return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine) } field := strings.SplitN(firstLine, ": ", 2) v, err := strconv.ParseUint(field[1], 0, 32) diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go new file mode 100644 index 000000000..d88442f0e --- /dev/null +++ b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux +// +build linux + +package procfs + +var parseCPUInfo = parseCPUInfoLoong diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_others.go b/vendor/github.com/prometheus/procfs/cpuinfo_others.go index ea41bf2ca..a6b2b3127 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_others.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_others.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux && !386 && !amd64 && !arm && !arm64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x -// +build linux,!386,!amd64,!arm,!arm64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x +//go:build linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x +// +build linux,!386,!amd64,!arm,!arm64,!loong64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x package procfs diff --git a/vendor/github.com/prometheus/procfs/crypto.go b/vendor/github.com/prometheus/procfs/crypto.go index 5048ad1f2..9a73e2639 100644 --- a/vendor/github.com/prometheus/procfs/crypto.go +++ b/vendor/github.com/prometheus/procfs/crypto.go @@ -55,12 +55,13 @@ func (fs FS) Crypto() ([]Crypto, error) { path := fs.proc.Path("crypto") b, err := util.ReadFileNoStat(path) if err != nil { - return nil, fmt.Errorf("error reading crypto %q: %w", path, err) + return nil, fmt.Errorf("%s: Cannot read file %v: %w", ErrFileRead, b, err) + } crypto, err := parseCrypto(bytes.NewReader(b)) if err != nil { - return nil, fmt.Errorf("error parsing crypto %q: %w", path, err) + return nil, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, crypto, err) } return crypto, nil @@ -83,7 +84,7 @@ func parseCrypto(r io.Reader) ([]Crypto, error) { kv := strings.Split(text, ":") if len(kv) != 2 { - return nil, fmt.Errorf("malformed crypto line: %q", text) + return nil, fmt.Errorf("%w: Cannot parae line: %q", ErrFileParse, text) } k := strings.TrimSpace(kv[0]) diff --git a/vendor/github.com/prometheus/procfs/doc.go b/vendor/github.com/prometheus/procfs/doc.go index d31a82600..f9d961e44 100644 --- a/vendor/github.com/prometheus/procfs/doc.go +++ b/vendor/github.com/prometheus/procfs/doc.go @@ -16,30 +16,29 @@ // // Example: // -// package main -// -// import ( -// "fmt" -// "log" -// -// "github.com/prometheus/procfs" -// ) -// -// func main() { -// p, err := procfs.Self() -// if err != nil { -// log.Fatalf("could not get process: %s", err) -// } -// -// stat, err := p.Stat() -// if err != nil { -// log.Fatalf("could not get process stat: %s", err) -// } -// -// fmt.Printf("command: %s\n", stat.Comm) -// fmt.Printf("cpu time: %fs\n", stat.CPUTime()) -// fmt.Printf("vsize: %dB\n", stat.VirtualMemory()) -// fmt.Printf("rss: %dB\n", stat.ResidentMemory()) -// } -// +// package main +// +// import ( +// "fmt" +// "log" +// +// "github.com/prometheus/procfs" +// ) +// +// func main() { +// p, err := procfs.Self() +// if err != nil { +// log.Fatalf("could not get process: %s", err) +// } +// +// stat, err := p.Stat() +// if err != nil { +// log.Fatalf("could not get process stat: %s", err) +// } +// +// fmt.Printf("command: %s\n", stat.Comm) +// fmt.Printf("cpu time: %fs\n", stat.CPUTime()) +// fmt.Printf("vsize: %dB\n", stat.VirtualMemory()) +// fmt.Printf("rss: %dB\n", stat.ResidentMemory()) +// } package procfs diff --git a/vendor/github.com/prometheus/procfs/fs.go b/vendor/github.com/prometheus/procfs/fs.go index 0102ab0fd..4980c875b 100644 --- a/vendor/github.com/prometheus/procfs/fs.go +++ b/vendor/github.com/prometheus/procfs/fs.go @@ -20,7 +20,8 @@ import ( // FS represents the pseudo-filesystem sys, which provides an interface to // kernel data structures. type FS struct { - proc fs.FS + proc fs.FS + isReal bool } // DefaultMountPoint is the common mount point of the proc filesystem. @@ -39,5 +40,11 @@ func NewFS(mountPoint string) (FS, error) { if err != nil { return FS{}, err } - return FS{fs}, nil + + isReal, err := isRealProc(mountPoint) + if err != nil { + return FS{}, err + } + + return FS{fs, isReal}, nil } diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go new file mode 100644 index 000000000..134767d69 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !freebsd && !linux +// +build !freebsd,!linux + +package procfs + +// isRealProc returns true on architectures that don't have a Type argument +// in their Statfs_t struct +func isRealProc(mountPoint string) (bool, error) { + return true, nil +} diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_type.go b/vendor/github.com/prometheus/procfs/fs_statfs_type.go new file mode 100644 index 000000000..80df79c31 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/fs_statfs_type.go @@ -0,0 +1,33 @@ +// Copyright 2018 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build freebsd || linux +// +build freebsd linux + +package procfs + +import ( + "syscall" +) + +// isRealProc determines whether supplied mountpoint is really a proc filesystem. +func isRealProc(mountPoint string) (bool, error) { + stat := syscall.Statfs_t{} + err := syscall.Statfs(mountPoint, &stat) + if err != nil { + return false, err + } + + // 0x9fa0 is PROC_SUPER_MAGIC: https://elixir.bootlin.com/linux/v6.1/source/include/uapi/linux/magic.h#L87 + return stat.Type == 0x9fa0, nil +} diff --git a/vendor/github.com/prometheus/procfs/fscache.go b/vendor/github.com/prometheus/procfs/fscache.go index f8070e6e2..f560a8db3 100644 --- a/vendor/github.com/prometheus/procfs/fscache.go +++ b/vendor/github.com/prometheus/procfs/fscache.go @@ -236,7 +236,7 @@ func (fs FS) Fscacheinfo() (Fscacheinfo, error) { m, err := parseFscacheinfo(bytes.NewReader(b)) if err != nil { - return Fscacheinfo{}, fmt.Errorf("failed to parse Fscacheinfo: %w", err) + return Fscacheinfo{}, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, m, err) } return *m, nil @@ -245,7 +245,7 @@ func (fs FS) Fscacheinfo() (Fscacheinfo, error) { func setFSCacheFields(fields []string, setFields ...*uint64) error { var err error if len(fields) < len(setFields) { - return fmt.Errorf("Insufficient number of fields, expected %v, got %v", len(setFields), len(fields)) + return fmt.Errorf("%s: Expected %d, but got %d: %w", ErrFileParse, len(setFields), len(fields), err) } for i := range setFields { @@ -263,7 +263,7 @@ func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) { for s.Scan() { fields := strings.Fields(s.Text()) if len(fields) < 2 { - return nil, fmt.Errorf("malformed Fscacheinfo line: %q", s.Text()) + return nil, fmt.Errorf("%w: malformed Fscacheinfo line: %q", ErrFileParse, s.Text()) } switch fields[0] { diff --git a/vendor/github.com/prometheus/procfs/internal/util/parse.go b/vendor/github.com/prometheus/procfs/internal/util/parse.go index b030951fa..14272dc78 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/parse.go +++ b/vendor/github.com/prometheus/procfs/internal/util/parse.go @@ -64,6 +64,21 @@ func ParsePInt64s(ss []string) ([]*int64, error) { return us, nil } +// Parses a uint64 from given hex in string. +func ParseHexUint64s(ss []string) ([]*uint64, error) { + us := make([]*uint64, 0, len(ss)) + for _, s := range ss { + u, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return nil, err + } + + us = append(us, &u) + } + + return us, nil +} + // ReadUintFromFile reads a file and attempts to parse a uint64 from it. func ReadUintFromFile(path string) (uint64, error) { data, err := os.ReadFile(path) diff --git a/vendor/github.com/prometheus/procfs/ipvs.go b/vendor/github.com/prometheus/procfs/ipvs.go index 391c07957..5a145bbfe 100644 --- a/vendor/github.com/prometheus/procfs/ipvs.go +++ b/vendor/github.com/prometheus/procfs/ipvs.go @@ -221,15 +221,16 @@ func parseIPPort(s string) (net.IP, uint16, error) { case 46: ip = net.ParseIP(s[1:40]) if ip == nil { - return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40]) + return nil, 0, fmt.Errorf("%s: Invalid IPv6 addr %s: %w", ErrFileParse, s[1:40], err) } default: - return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s) + return nil, 0, fmt.Errorf("%s: Unexpected IP:Port %s: %w", ErrFileParse, s, err) } portString := s[len(s)-4:] if len(portString) != 4 { - return nil, 0, fmt.Errorf("unexpected port string format: %s", portString) + return nil, 0, + fmt.Errorf("%s: Unexpected port string format %s: %w", ErrFileParse, portString, err) } port, err := strconv.ParseUint(portString, 16, 16) if err != nil { diff --git a/vendor/github.com/prometheus/procfs/loadavg.go b/vendor/github.com/prometheus/procfs/loadavg.go index 0096cafbd..59465c5bb 100644 --- a/vendor/github.com/prometheus/procfs/loadavg.go +++ b/vendor/github.com/prometheus/procfs/loadavg.go @@ -44,14 +44,14 @@ func parseLoad(loadavgBytes []byte) (*LoadAvg, error) { loads := make([]float64, 3) parts := strings.Fields(string(loadavgBytes)) if len(parts) < 3 { - return nil, fmt.Errorf("malformed loadavg line: too few fields in loadavg string: %q", string(loadavgBytes)) + return nil, fmt.Errorf("%w: Malformed line %q", ErrFileParse, string(loadavgBytes)) } var err error for i, load := range parts[0:3] { loads[i], err = strconv.ParseFloat(load, 64) if err != nil { - return nil, fmt.Errorf("could not parse load %q: %w", load, err) + return nil, fmt.Errorf("%s: Cannot parse load: %f: %w", ErrFileParse, loads[i], err) } } return &LoadAvg{ diff --git a/vendor/github.com/prometheus/procfs/mdstat.go b/vendor/github.com/prometheus/procfs/mdstat.go index a95c889cb..fdd4b9544 100644 --- a/vendor/github.com/prometheus/procfs/mdstat.go +++ b/vendor/github.com/prometheus/procfs/mdstat.go @@ -70,7 +70,7 @@ func (fs FS) MDStat() ([]MDStat, error) { } mdstat, err := parseMDStat(data) if err != nil { - return nil, fmt.Errorf("error parsing mdstat %q: %w", fs.proc.Path("mdstat"), err) + return nil, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, fs.proc.Path("mdstat"), err) } return mdstat, nil } @@ -90,13 +90,13 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { deviceFields := strings.Fields(line) if len(deviceFields) < 3 { - return nil, fmt.Errorf("not enough fields in mdline (expected at least 3): %s", line) + return nil, fmt.Errorf("%s: Expected 3+ lines, got %q", ErrFileParse, line) } mdName := deviceFields[0] // mdx state := deviceFields[2] // active or inactive if len(lines) <= i+3 { - return nil, fmt.Errorf("error parsing %q: too few lines for md device", mdName) + return nil, fmt.Errorf("%w: Too few lines for md device: %q", ErrFileParse, mdName) } // Failed disks have the suffix (F) & Spare disks have the suffix (S). @@ -105,7 +105,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { active, total, down, size, err := evalStatusLine(lines[i], lines[i+1]) if err != nil { - return nil, fmt.Errorf("error parsing md device lines: %w", err) + return nil, fmt.Errorf("%s: Cannot parse md device lines: %v: %w", ErrFileParse, active, err) } syncLineIdx := i + 2 @@ -140,7 +140,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { } else { syncedBlocks, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx]) if err != nil { - return nil, fmt.Errorf("error parsing sync line in md device %q: %w", mdName, err) + return nil, fmt.Errorf("%s: Cannot parse sync line in md device: %q: %w", ErrFileParse, mdName, err) } } } @@ -168,13 +168,13 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) { statusFields := strings.Fields(statusLine) if len(statusFields) < 1 { - return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q", statusLine) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err) } sizeStr := statusFields[0] size, err = strconv.ParseInt(sizeStr, 10, 64) if err != nil { - return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err) } if strings.Contains(deviceLine, "raid0") || strings.Contains(deviceLine, "linear") { @@ -189,17 +189,17 @@ func evalStatusLine(deviceLine, statusLine string) (active, total, down, size in matches := statusLineRE.FindStringSubmatch(statusLine) if len(matches) != 5 { - return 0, 0, 0, 0, fmt.Errorf("couldn't find all the substring matches: %s", statusLine) + return 0, 0, 0, 0, fmt.Errorf("%s: Could not fild all substring matches %s: %w", ErrFileParse, statusLine, err) } total, err = strconv.ParseInt(matches[2], 10, 64) if err != nil { - return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err) } active, err = strconv.ParseInt(matches[3], 10, 64) if err != nil { - return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected active %d: %w", ErrFileParse, active, err) } down = int64(strings.Count(matches[4], "_")) @@ -209,42 +209,42 @@ func evalStatusLine(deviceLine, statusLine string) (active, total, down, size in func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, finish float64, speed float64, err error) { matches := recoveryLineBlocksRE.FindStringSubmatch(recoveryLine) if len(matches) != 2 { - return 0, 0, 0, 0, fmt.Errorf("unexpected recoveryLine: %s", recoveryLine) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected recoveryLine %s: %w", ErrFileParse, recoveryLine, err) } syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64) if err != nil { - return 0, 0, 0, 0, fmt.Errorf("error parsing int from recoveryLine %q: %w", recoveryLine, err) + return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected parsing of recoveryLine %q: %w", ErrFileParse, recoveryLine, err) } // Get percentage complete matches = recoveryLinePctRE.FindStringSubmatch(recoveryLine) if len(matches) != 2 { - return syncedBlocks, 0, 0, 0, fmt.Errorf("unexpected recoveryLine matching percentage: %s", recoveryLine) + return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching percentage %s", ErrFileParse, recoveryLine) } pct, err = strconv.ParseFloat(strings.TrimSpace(matches[1]), 64) if err != nil { - return syncedBlocks, 0, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Error parsing float from recoveryLine %q", ErrFileParse, recoveryLine) } // Get time expected left to complete matches = recoveryLineFinishRE.FindStringSubmatch(recoveryLine) if len(matches) != 2 { - return syncedBlocks, pct, 0, 0, fmt.Errorf("unexpected recoveryLine matching est. finish time: %s", recoveryLine) + return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching est. finish time: %s", ErrFileParse, recoveryLine) } finish, err = strconv.ParseFloat(matches[1], 64) if err != nil { - return syncedBlocks, pct, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unable to parse float from recoveryLine: %q", ErrFileParse, recoveryLine) } // Get recovery speed matches = recoveryLineSpeedRE.FindStringSubmatch(recoveryLine) if len(matches) != 2 { - return syncedBlocks, pct, finish, 0, fmt.Errorf("unexpected recoveryLine matching speed: %s", recoveryLine) + return syncedBlocks, pct, finish, 0, fmt.Errorf("%w: Unexpected recoveryLine value: %s", ErrFileParse, recoveryLine) } speed, err = strconv.ParseFloat(matches[1], 64) if err != nil { - return syncedBlocks, pct, finish, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + return syncedBlocks, pct, finish, 0, fmt.Errorf("%s: Error parsing float from recoveryLine: %q: %w", ErrFileParse, recoveryLine, err) } return syncedBlocks, pct, finish, speed, nil diff --git a/vendor/github.com/prometheus/procfs/meminfo.go b/vendor/github.com/prometheus/procfs/meminfo.go index f65e174e5..eaf00e224 100644 --- a/vendor/github.com/prometheus/procfs/meminfo.go +++ b/vendor/github.com/prometheus/procfs/meminfo.go @@ -152,7 +152,7 @@ func (fs FS) Meminfo() (Meminfo, error) { m, err := parseMemInfo(bytes.NewReader(b)) if err != nil { - return Meminfo{}, fmt.Errorf("failed to parse meminfo: %w", err) + return Meminfo{}, fmt.Errorf("%s: %w", ErrFileParse, err) } return *m, nil @@ -165,7 +165,7 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { // Each line has at least a name and value; we ignore the unit. fields := strings.Fields(s.Text()) if len(fields) < 2 { - return nil, fmt.Errorf("malformed meminfo line: %q", s.Text()) + return nil, fmt.Errorf("%w: Malformed line %q", ErrFileParse, s.Text()) } v, err := strconv.ParseUint(fields[1], 0, 64) diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go index 59f4d5055..388ebf396 100644 --- a/vendor/github.com/prometheus/procfs/mountinfo.go +++ b/vendor/github.com/prometheus/procfs/mountinfo.go @@ -78,11 +78,11 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { mountInfo := strings.Split(mountString, " ") mountInfoLength := len(mountInfo) if mountInfoLength < 10 { - return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString) + return nil, fmt.Errorf("%w: Too few fields in mount string: %s", ErrFileParse, mountString) } if mountInfo[mountInfoLength-4] != "-" { - return nil, fmt.Errorf("couldn't find separator in expected field: %s", mountInfo[mountInfoLength-4]) + return nil, fmt.Errorf("%w: couldn't find separator in expected field: %s", ErrFileParse, mountInfo[mountInfoLength-4]) } mount := &MountInfo{ @@ -98,18 +98,18 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { mount.MountID, err = strconv.Atoi(mountInfo[0]) if err != nil { - return nil, fmt.Errorf("failed to parse mount ID") + return nil, fmt.Errorf("%w: mount ID: %q", ErrFileParse, mount.MountID) } mount.ParentID, err = strconv.Atoi(mountInfo[1]) if err != nil { - return nil, fmt.Errorf("failed to parse parent ID") + return nil, fmt.Errorf("%w: parent ID: %q", ErrFileParse, mount.ParentID) } // Has optional fields, which is a space separated list of values. // Example: shared:2 master:7 if mountInfo[6] != "" { mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4]) if err != nil { - return nil, err + return nil, fmt.Errorf("%s: %w", ErrFileParse, err) } } return mount, nil diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go index f7a828bb1..9d8af6db7 100644 --- a/vendor/github.com/prometheus/procfs/mountstats.go +++ b/vendor/github.com/prometheus/procfs/mountstats.go @@ -44,6 +44,14 @@ const ( fieldTransport11TCPLen = 13 fieldTransport11UDPLen = 10 + + // kernel version >= 4.14 MaxLen + // See: https://elixir.bootlin.com/linux/v6.4.8/source/net/sunrpc/xprtrdma/xprt_rdma.h#L393 + fieldTransport11RDMAMaxLen = 28 + + // kernel version <= 4.2 MinLen + // See: https://elixir.bootlin.com/linux/v4.2.8/source/net/sunrpc/xprtrdma/xprt_rdma.h#L331 + fieldTransport11RDMAMinLen = 20 ) // A Mount is a device mount parsed from /proc/[pid]/mountstats. @@ -186,6 +194,8 @@ type NFSOperationStats struct { CumulativeTotalResponseMilliseconds uint64 // Duration from when a request was enqueued to when it was completely handled. CumulativeTotalRequestMilliseconds uint64 + // The average time from the point the client sends RPC requests until it receives the response. + AverageRTTMilliseconds float64 // The count of operations that complete with tk_status < 0. These statuses usually indicate error conditions. Errors uint64 } @@ -231,6 +241,33 @@ type NFSTransportStats struct { // A running counter, incremented on each request as the current size of the // pending queue. CumulativePendingQueue uint64 + + // Stats below only available with stat version 1.1. + // Transport over RDMA + + // accessed when sending a call + ReadChunkCount uint64 + WriteChunkCount uint64 + ReplyChunkCount uint64 + TotalRdmaRequest uint64 + + // rarely accessed error counters + PullupCopyCount uint64 + HardwayRegisterCount uint64 + FailedMarshalCount uint64 + BadReplyCount uint64 + MrsRecovered uint64 + MrsOrphaned uint64 + MrsAllocated uint64 + EmptySendctxQ uint64 + + // accessed when receiving a reply + TotalRdmaReply uint64 + FixupCopyCount uint64 + ReplyWaitsForSend uint64 + LocalInvNeeded uint64 + NomsgCallCount uint64 + BcallCount uint64 } // parseMountStats parses a /proc/[pid]/mountstats file and returns a slice @@ -264,7 +301,7 @@ func parseMountStats(r io.Reader) ([]*Mount, error) { if len(ss) > deviceEntryLen { // Only NFSv3 and v4 are supported for parsing statistics if m.Type != nfs3Type && m.Type != nfs4Type { - return nil, fmt.Errorf("cannot parse MountStats for fstype %q", m.Type) + return nil, fmt.Errorf("%w: Cannot parse MountStats for %q", ErrFileParse, m.Type) } statVersion := strings.TrimPrefix(ss[8], statVersionPrefix) @@ -284,10 +321,11 @@ func parseMountStats(r io.Reader) ([]*Mount, error) { } // parseMount parses an entry in /proc/[pid]/mountstats in the format: -// device [device] mounted on [mount] with fstype [type] +// +// device [device] mounted on [mount] with fstype [type] func parseMount(ss []string) (*Mount, error) { if len(ss) < deviceEntryLen { - return nil, fmt.Errorf("invalid device entry: %v", ss) + return nil, fmt.Errorf("%w: Invalid device %q", ErrFileParse, ss) } // Check for specific words appearing at specific indices to ensure @@ -305,7 +343,7 @@ func parseMount(ss []string) (*Mount, error) { for _, f := range format { if ss[f.i] != f.s { - return nil, fmt.Errorf("invalid device entry: %v", ss) + return nil, fmt.Errorf("%w: Invalid device %q", ErrFileParse, ss) } } @@ -342,7 +380,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e switch ss[0] { case fieldOpts: if len(ss) < 2 { - return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) + return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss) } if stats.Opts == nil { stats.Opts = map[string]string{} @@ -357,7 +395,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e } case fieldAge: if len(ss) < 2 { - return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) + return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss) } // Age integer is in seconds d, err := time.ParseDuration(ss[1] + "s") @@ -368,7 +406,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e stats.Age = d case fieldBytes: if len(ss) < 2 { - return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) + return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss) } bstats, err := parseNFSBytesStats(ss[1:]) if err != nil { @@ -378,7 +416,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e stats.Bytes = *bstats case fieldEvents: if len(ss) < 2 { - return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) + return nil, fmt.Errorf("%w: Incomplete information for NFS events: %v", ErrFileParse, ss) } estats, err := parseNFSEventsStats(ss[1:]) if err != nil { @@ -388,7 +426,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e stats.Events = *estats case fieldTransport: if len(ss) < 3 { - return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss) + return nil, fmt.Errorf("%w: Incomplete information for NFS transport stats: %v", ErrFileParse, ss) } tstats, err := parseNFSTransportStats(ss[1:], statVersion) @@ -427,7 +465,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e // integer fields. func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) { if len(ss) != fieldBytesLen { - return nil, fmt.Errorf("invalid NFS bytes stats: %v", ss) + return nil, fmt.Errorf("%w: Invalid NFS bytes stats: %v", ErrFileParse, ss) } ns := make([]uint64, 0, fieldBytesLen) @@ -456,7 +494,7 @@ func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) { // integer fields. func parseNFSEventsStats(ss []string) (*NFSEventsStats, error) { if len(ss) != fieldEventsLen { - return nil, fmt.Errorf("invalid NFS events stats: %v", ss) + return nil, fmt.Errorf("%w: invalid NFS events stats: %v", ErrFileParse, ss) } ns := make([]uint64, 0, fieldEventsLen) @@ -520,7 +558,7 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) { } if len(ss) < minFields { - return nil, fmt.Errorf("invalid NFS per-operations stats: %v", ss) + return nil, fmt.Errorf("%w: invalid NFS per-operations stats: %v", ErrFileParse, ss) } // Skip string operation name for integers @@ -533,7 +571,6 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) { ns = append(ns, n) } - opStats := NFSOperationStats{ Operation: strings.TrimSuffix(ss[0], ":"), Requests: ns[0], @@ -545,6 +582,9 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) { CumulativeTotalResponseMilliseconds: ns[6], CumulativeTotalRequestMilliseconds: ns[7], } + if ns[0] != 0 { + opStats.AverageRTTMilliseconds = float64(ns[6]) / float64(ns[0]) + } if len(ns) > 8 { opStats.Errors = ns[8] @@ -571,10 +611,10 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats } else if protocol == "udp" { expectedLength = fieldTransport10UDPLen } else { - return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.0 statement: %v", protocol, ss) + return nil, fmt.Errorf("%w: Invalid NFS protocol \"%s\" in stats 1.0 statement: %v", ErrFileParse, protocol, ss) } if len(ss) != expectedLength { - return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss) + return nil, fmt.Errorf("%w: Invalid NFS transport stats 1.0 statement: %v", ErrFileParse, ss) } case statVersion11: var expectedLength int @@ -582,14 +622,17 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats expectedLength = fieldTransport11TCPLen } else if protocol == "udp" { expectedLength = fieldTransport11UDPLen + } else if protocol == "rdma" { + expectedLength = fieldTransport11RDMAMinLen } else { - return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.1 statement: %v", protocol, ss) + return nil, fmt.Errorf("%w: invalid NFS protocol \"%s\" in stats 1.1 statement: %v", ErrFileParse, protocol, ss) } - if len(ss) != expectedLength { - return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss) + if (len(ss) != expectedLength && (protocol == "tcp" || protocol == "udp")) || + (protocol == "rdma" && len(ss) < expectedLength) { + return nil, fmt.Errorf("%w: invalid NFS transport stats 1.1 statement: %v, protocol: %v", ErrFileParse, ss, protocol) } default: - return nil, fmt.Errorf("unrecognized NFS transport stats version: %q", statVersion) + return nil, fmt.Errorf("%s: Unrecognized NFS transport stats version: %q, protocol: %v", ErrFileParse, statVersion, protocol) } // Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay @@ -599,7 +642,9 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats // Note: slice length must be set to length of v1.1 stats to avoid a panic when // only v1.0 stats are present. // See: https://github.com/prometheus/node_exporter/issues/571. - ns := make([]uint64, fieldTransport11TCPLen) + // + // Note: NFS Over RDMA slice length is fieldTransport11RDMAMaxLen + ns := make([]uint64, fieldTransport11RDMAMaxLen+3) for i, s := range ss { n, err := strconv.ParseUint(s, 10, 64) if err != nil { @@ -617,9 +662,14 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats // we set them to 0 here. if protocol == "udp" { ns = append(ns[:2], append(make([]uint64, 3), ns[2:]...)...) + } else if protocol == "tcp" { + ns = append(ns[:fieldTransport11TCPLen], make([]uint64, fieldTransport11RDMAMaxLen-fieldTransport11TCPLen+3)...) + } else if protocol == "rdma" { + ns = append(ns[:fieldTransport10TCPLen], append(make([]uint64, 3), ns[fieldTransport10TCPLen:]...)...) } return &NFSTransportStats{ + // NFS xprt over tcp or udp Protocol: protocol, Port: ns[0], Bind: ns[1], @@ -631,8 +681,32 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats BadTransactionIDs: ns[7], CumulativeActiveRequests: ns[8], CumulativeBacklog: ns[9], - MaximumRPCSlotsUsed: ns[10], - CumulativeSendingQueue: ns[11], - CumulativePendingQueue: ns[12], + + // NFS xprt over tcp or udp + // And statVersion 1.1 + MaximumRPCSlotsUsed: ns[10], + CumulativeSendingQueue: ns[11], + CumulativePendingQueue: ns[12], + + // NFS xprt over rdma + // And stat Version 1.1 + ReadChunkCount: ns[13], + WriteChunkCount: ns[14], + ReplyChunkCount: ns[15], + TotalRdmaRequest: ns[16], + PullupCopyCount: ns[17], + HardwayRegisterCount: ns[18], + FailedMarshalCount: ns[19], + BadReplyCount: ns[20], + MrsRecovered: ns[21], + MrsOrphaned: ns[22], + MrsAllocated: ns[23], + EmptySendctxQ: ns[24], + TotalRdmaReply: ns[25], + FixupCopyCount: ns[26], + ReplyWaitsForSend: ns[27], + LocalInvNeeded: ns[28], + NomsgCallCount: ns[29], + BcallCount: ns[30], }, nil } diff --git a/vendor/github.com/prometheus/procfs/net_conntrackstat.go b/vendor/github.com/prometheus/procfs/net_conntrackstat.go index 8300daca0..fdfa45611 100644 --- a/vendor/github.com/prometheus/procfs/net_conntrackstat.go +++ b/vendor/github.com/prometheus/procfs/net_conntrackstat.go @@ -18,7 +18,6 @@ import ( "bytes" "fmt" "io" - "strconv" "strings" "github.com/prometheus/procfs/internal/util" @@ -28,9 +27,13 @@ import ( // and contains netfilter conntrack statistics at one CPU core. type ConntrackStatEntry struct { Entries uint64 + Searched uint64 Found uint64 + New uint64 Invalid uint64 Ignore uint64 + Delete uint64 + DeleteList uint64 Insert uint64 InsertFailed uint64 Drop uint64 @@ -55,7 +58,7 @@ func readConntrackStat(path string) ([]ConntrackStatEntry, error) { stat, err := parseConntrackStat(bytes.NewReader(b)) if err != nil { - return nil, fmt.Errorf("failed to read conntrack stats from %q: %w", path, err) + return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, path, err) } return stat, nil @@ -81,73 +84,35 @@ func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) { // Parses a ConntrackStatEntry from given array of fields. func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) { - if len(fields) != 17 { - return nil, fmt.Errorf("invalid conntrackstat entry, missing fields") - } - entry := &ConntrackStatEntry{} - - entries, err := parseConntrackStatField(fields[0]) - if err != nil { - return nil, err - } - entry.Entries = entries - - found, err := parseConntrackStatField(fields[2]) - if err != nil { - return nil, err - } - entry.Found = found - - invalid, err := parseConntrackStatField(fields[4]) - if err != nil { - return nil, err - } - entry.Invalid = invalid - - ignore, err := parseConntrackStatField(fields[5]) - if err != nil { - return nil, err - } - entry.Ignore = ignore - - insert, err := parseConntrackStatField(fields[8]) + entries, err := util.ParseHexUint64s(fields) if err != nil { - return nil, err + return nil, fmt.Errorf("%s: Cannot parse entry: %d: %w", ErrFileParse, entries, err) } - entry.Insert = insert - - insertFailed, err := parseConntrackStatField(fields[9]) - if err != nil { - return nil, err + numEntries := len(entries) + if numEntries < 16 || numEntries > 17 { + return nil, + fmt.Errorf("%w: invalid conntrackstat entry, invalid number of fields: %d", ErrFileParse, numEntries) } - entry.InsertFailed = insertFailed - drop, err := parseConntrackStatField(fields[10]) - if err != nil { - return nil, err + stats := &ConntrackStatEntry{ + Entries: *entries[0], + Searched: *entries[1], + Found: *entries[2], + New: *entries[3], + Invalid: *entries[4], + Ignore: *entries[5], + Delete: *entries[6], + DeleteList: *entries[7], + Insert: *entries[8], + InsertFailed: *entries[9], + Drop: *entries[10], + EarlyDrop: *entries[11], } - entry.Drop = drop - earlyDrop, err := parseConntrackStatField(fields[11]) - if err != nil { - return nil, err + // Ignore missing search_restart on Linux < 2.6.35. + if numEntries == 17 { + stats.SearchRestart = *entries[16] } - entry.EarlyDrop = earlyDrop - searchRestart, err := parseConntrackStatField(fields[16]) - if err != nil { - return nil, err - } - entry.SearchRestart = searchRestart - - return entry, nil -} - -// Parses a uint64 from given hex in string. -func parseConntrackStatField(field string) (uint64, error) { - val, err := strconv.ParseUint(field, 16, 64) - if err != nil { - return 0, fmt.Errorf("couldn't parse %q field: %w", field, err) - } - return val, err + return stats, nil } diff --git a/vendor/github.com/prometheus/procfs/net_ip_socket.go b/vendor/github.com/prometheus/procfs/net_ip_socket.go index 7fd57d7f4..4da81ea57 100644 --- a/vendor/github.com/prometheus/procfs/net_ip_socket.go +++ b/vendor/github.com/prometheus/procfs/net_ip_socket.go @@ -130,7 +130,7 @@ func parseIP(hexIP string) (net.IP, error) { var byteIP []byte byteIP, err := hex.DecodeString(hexIP) if err != nil { - return nil, fmt.Errorf("cannot parse address field in socket line %q", hexIP) + return nil, fmt.Errorf("%s: Cannot parse socket field in %q: %w", ErrFileParse, hexIP, err) } switch len(byteIP) { case 4: @@ -144,7 +144,7 @@ func parseIP(hexIP string) (net.IP, error) { } return i, nil default: - return nil, fmt.Errorf("Unable to parse IP %s", hexIP) + return nil, fmt.Errorf("%s: Unable to parse IP %s: %w", ErrFileParse, hexIP, nil) } } @@ -153,7 +153,8 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { line := &netIPSocketLine{} if len(fields) < 10 { return nil, fmt.Errorf( - "cannot parse net socket line as it has less then 10 columns %q", + "%w: Less than 10 columns found %q", + ErrFileParse, strings.Join(fields, " "), ) } @@ -162,64 +163,65 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { // sl s := strings.Split(fields[0], ":") if len(s) != 2 { - return nil, fmt.Errorf("cannot parse sl field in socket line %q", fields[0]) + return nil, fmt.Errorf("%w: Unable to parse sl field in line %q", ErrFileParse, fields[0]) } if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse sl value in socket line: %w", err) + return nil, fmt.Errorf("%s: Unable to parse sl field in %q: %w", ErrFileParse, line.Sl, err) } // local_address l := strings.Split(fields[1], ":") if len(l) != 2 { - return nil, fmt.Errorf("cannot parse local_address field in socket line %q", fields[1]) + return nil, fmt.Errorf("%w: Unable to parse local_address field in %q", ErrFileParse, fields[1]) } if line.LocalAddr, err = parseIP(l[0]); err != nil { return nil, err } if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse local_address port value in socket line: %w", err) + return nil, fmt.Errorf("%s: Unable to parse local_address port value line %q: %w", ErrFileParse, line.LocalPort, err) } // remote_address r := strings.Split(fields[2], ":") if len(r) != 2 { - return nil, fmt.Errorf("cannot parse rem_address field in socket line %q", fields[1]) + return nil, fmt.Errorf("%w: Unable to parse rem_address field in %q", ErrFileParse, fields[1]) } if line.RemAddr, err = parseIP(r[0]); err != nil { return nil, err } if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse rem_address port value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse rem_address port value in %q: %w", ErrFileParse, line.RemPort, err) } // st if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse st value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse st value in %q: %w", ErrFileParse, line.St, err) } // tx_queue and rx_queue q := strings.Split(fields[4], ":") if len(q) != 2 { return nil, fmt.Errorf( - "cannot parse tx/rx queues in socket line as it has a missing colon %q", + "%w: Missing colon for tx/rx queues in socket line %q", + ErrFileParse, fields[4], ) } if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse tx_queue value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse tx_queue value in %q: %w", ErrFileParse, line.TxQueue, err) } if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse rx_queue value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse trx_queue value in %q: %w", ErrFileParse, line.RxQueue, err) } // uid if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse uid value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse UID value in %q: %w", ErrFileParse, line.UID, err) } // inode if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse inode value in socket line: %w", err) + return nil, fmt.Errorf("%s: Cannot parse inode value in %q: %w", ErrFileParse, line.Inode, err) } return line, nil diff --git a/vendor/github.com/prometheus/procfs/net_protocols.go b/vendor/github.com/prometheus/procfs/net_protocols.go index 374b6f73f..b6c77b709 100644 --- a/vendor/github.com/prometheus/procfs/net_protocols.go +++ b/vendor/github.com/prometheus/procfs/net_protocols.go @@ -131,7 +131,7 @@ func (ps NetProtocolStats) parseLine(rawLine string) (*NetProtocolStatLine, erro } else if fields[6] == disabled { line.Slab = false } else { - return nil, fmt.Errorf("unable to parse capability for protocol: %s", line.Name) + return nil, fmt.Errorf("%w: capability for protocol: %s", ErrFileParse, line.Name) } line.ModuleName = fields[7] @@ -173,7 +173,7 @@ func (pc *NetProtocolCapabilities) parseCapabilities(capabilities []string) erro } else if capabilities[i] == "n" { *capabilityFields[i] = false } else { - return fmt.Errorf("unable to parse capability block for protocol: position %d", i) + return fmt.Errorf("%w: capability block for protocol: position %d", ErrFileParse, i) } } return nil diff --git a/vendor/github.com/prometheus/procfs/net_route.go b/vendor/github.com/prometheus/procfs/net_route.go new file mode 100644 index 000000000..deb7029fe --- /dev/null +++ b/vendor/github.com/prometheus/procfs/net_route.go @@ -0,0 +1,143 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +const ( + blackholeRepresentation string = "*" + blackholeIfaceName string = "blackhole" + routeLineColumns int = 11 +) + +// A NetRouteLine represents one line from net/route. +type NetRouteLine struct { + Iface string + Destination uint32 + Gateway uint32 + Flags uint32 + RefCnt uint32 + Use uint32 + Metric uint32 + Mask uint32 + MTU uint32 + Window uint32 + IRTT uint32 +} + +func (fs FS) NetRoute() ([]NetRouteLine, error) { + return readNetRoute(fs.proc.Path("net", "route")) +} + +func readNetRoute(path string) ([]NetRouteLine, error) { + b, err := util.ReadFileNoStat(path) + if err != nil { + return nil, err + } + + routelines, err := parseNetRoute(bytes.NewReader(b)) + if err != nil { + return nil, fmt.Errorf("failed to read net route from %s: %w", path, err) + } + return routelines, nil +} + +func parseNetRoute(r io.Reader) ([]NetRouteLine, error) { + var routelines []NetRouteLine + + scanner := bufio.NewScanner(r) + scanner.Scan() + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + routeline, err := parseNetRouteLine(fields) + if err != nil { + return nil, err + } + routelines = append(routelines, *routeline) + } + return routelines, nil +} + +func parseNetRouteLine(fields []string) (*NetRouteLine, error) { + if len(fields) != routeLineColumns { + return nil, fmt.Errorf("invalid routeline, num of digits: %d", len(fields)) + } + iface := fields[0] + if iface == blackholeRepresentation { + iface = blackholeIfaceName + } + destination, err := strconv.ParseUint(fields[1], 16, 32) + if err != nil { + return nil, err + } + gateway, err := strconv.ParseUint(fields[2], 16, 32) + if err != nil { + return nil, err + } + flags, err := strconv.ParseUint(fields[3], 10, 32) + if err != nil { + return nil, err + } + refcnt, err := strconv.ParseUint(fields[4], 10, 32) + if err != nil { + return nil, err + } + use, err := strconv.ParseUint(fields[5], 10, 32) + if err != nil { + return nil, err + } + metric, err := strconv.ParseUint(fields[6], 10, 32) + if err != nil { + return nil, err + } + mask, err := strconv.ParseUint(fields[7], 16, 32) + if err != nil { + return nil, err + } + mtu, err := strconv.ParseUint(fields[8], 10, 32) + if err != nil { + return nil, err + } + window, err := strconv.ParseUint(fields[9], 10, 32) + if err != nil { + return nil, err + } + irtt, err := strconv.ParseUint(fields[10], 10, 32) + if err != nil { + return nil, err + } + routeline := &NetRouteLine{ + Iface: iface, + Destination: uint32(destination), + Gateway: uint32(gateway), + Flags: uint32(flags), + RefCnt: uint32(refcnt), + Use: uint32(use), + Metric: uint32(metric), + Mask: uint32(mask), + MTU: uint32(mtu), + Window: uint32(window), + IRTT: uint32(irtt), + } + return routeline, nil +} diff --git a/vendor/github.com/prometheus/procfs/net_sockstat.go b/vendor/github.com/prometheus/procfs/net_sockstat.go index e36f4872d..360e36af7 100644 --- a/vendor/github.com/prometheus/procfs/net_sockstat.go +++ b/vendor/github.com/prometheus/procfs/net_sockstat.go @@ -16,7 +16,6 @@ package procfs import ( "bufio" "bytes" - "errors" "fmt" "io" "strings" @@ -70,7 +69,7 @@ func readSockstat(name string) (*NetSockstat, error) { stat, err := parseSockstat(bytes.NewReader(b)) if err != nil { - return nil, fmt.Errorf("failed to read sockstats from %q: %w", name, err) + return nil, fmt.Errorf("%s: sockstats from %q: %w", ErrFileRead, name, err) } return stat, nil @@ -84,13 +83,13 @@ func parseSockstat(r io.Reader) (*NetSockstat, error) { // Expect a minimum of a protocol and one key/value pair. fields := strings.Split(s.Text(), " ") if len(fields) < 3 { - return nil, fmt.Errorf("malformed sockstat line: %q", s.Text()) + return nil, fmt.Errorf("%w: Malformed sockstat line: %q", ErrFileParse, s.Text()) } // The remaining fields are key/value pairs. kvs, err := parseSockstatKVs(fields[1:]) if err != nil { - return nil, fmt.Errorf("error parsing sockstat key/value pairs from %q: %w", s.Text(), err) + return nil, fmt.Errorf("%s: sockstat key/value pairs from %q: %w", ErrFileParse, s.Text(), err) } // The first field is the protocol. We must trim its colon suffix. @@ -119,7 +118,7 @@ func parseSockstat(r io.Reader) (*NetSockstat, error) { // parseSockstatKVs parses a string slice into a map of key/value pairs. func parseSockstatKVs(kvs []string) (map[string]int, error) { if len(kvs)%2 != 0 { - return nil, errors.New("odd number of fields in key/value pairs") + return nil, fmt.Errorf("%w:: Odd number of fields in key/value pairs %q", ErrFileParse, kvs) } // Iterate two values at a time to gather key/value pairs. diff --git a/vendor/github.com/prometheus/procfs/net_softnet.go b/vendor/github.com/prometheus/procfs/net_softnet.go index a94f86dc4..c77085291 100644 --- a/vendor/github.com/prometheus/procfs/net_softnet.go +++ b/vendor/github.com/prometheus/procfs/net_softnet.go @@ -27,8 +27,9 @@ import ( // For the proc file format details, // See: // * Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2343 -// * Linux 4.17 https://elixir.bootlin.com/linux/v4.17/source/net/core/net-procfs.c#L162 -// and https://elixir.bootlin.com/linux/v4.17/source/include/linux/netdevice.h#L2810. +// * Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086 +// * Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162 +// * Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169 // SoftnetStat contains a single row of data from /proc/net/softnet_stat. type SoftnetStat struct { @@ -38,6 +39,18 @@ type SoftnetStat struct { Dropped uint32 // Number of times processing packets ran out of quota. TimeSqueezed uint32 + // Number of collision occur while obtaining device lock while transmitting. + CPUCollision uint32 + // Number of times cpu woken up received_rps. + ReceivedRps uint32 + // number of times flow limit has been reached. + FlowLimitCount uint32 + // Softnet backlog status. + SoftnetBacklogLen uint32 + // CPU id owning this softnet_data. + Index uint32 + // softnet_data's Width. + Width int } var softNetProcFile = "net/softnet_stat" @@ -51,7 +64,7 @@ func (fs FS) NetSoftnetStat() ([]SoftnetStat, error) { entries, err := parseSoftnet(bytes.NewReader(b)) if err != nil { - return nil, fmt.Errorf("failed to parse /proc/net/softnet_stat: %w", err) + return nil, fmt.Errorf("%s: /proc/net/softnet_stat: %w", ErrFileParse, err) } return entries, nil @@ -63,25 +76,65 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) { s := bufio.NewScanner(r) var stats []SoftnetStat + cpuIndex := 0 for s.Scan() { columns := strings.Fields(s.Text()) width := len(columns) + softnetStat := SoftnetStat{} if width < minColumns { - return nil, fmt.Errorf("%d columns were detected, but at least %d were expected", width, minColumns) + return nil, fmt.Errorf("%w: detected %d columns, but expected at least %d", ErrFileParse, width, minColumns) } - // We only parse the first three columns at the moment. - us, err := parseHexUint32s(columns[0:3]) - if err != nil { - return nil, err + // Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2347 + if width >= minColumns { + us, err := parseHexUint32s(columns[0:9]) + if err != nil { + return nil, err + } + + softnetStat.Processed = us[0] + softnetStat.Dropped = us[1] + softnetStat.TimeSqueezed = us[2] + softnetStat.CPUCollision = us[8] + } + + // Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086 + if width >= 10 { + us, err := parseHexUint32s(columns[9:10]) + if err != nil { + return nil, err + } + + softnetStat.ReceivedRps = us[0] } - stats = append(stats, SoftnetStat{ - Processed: us[0], - Dropped: us[1], - TimeSqueezed: us[2], - }) + // Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162 + if width >= 11 { + us, err := parseHexUint32s(columns[10:11]) + if err != nil { + return nil, err + } + + softnetStat.FlowLimitCount = us[0] + } + + // Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169 + if width >= 13 { + us, err := parseHexUint32s(columns[11:13]) + if err != nil { + return nil, err + } + + softnetStat.SoftnetBacklogLen = us[0] + softnetStat.Index = us[1] + } else { + // For older kernels, create the Index based on the scan line number. + softnetStat.Index = uint32(cpuIndex) + } + softnetStat.Width = width + stats = append(stats, softnetStat) + cpuIndex++ } return stats, nil diff --git a/vendor/github.com/prometheus/procfs/net_unix.go b/vendor/github.com/prometheus/procfs/net_unix.go index 98aa8e1c3..acbbc57ea 100644 --- a/vendor/github.com/prometheus/procfs/net_unix.go +++ b/vendor/github.com/prometheus/procfs/net_unix.go @@ -108,14 +108,14 @@ func parseNetUNIX(r io.Reader) (*NetUNIX, error) { line := s.Text() item, err := nu.parseLine(line, hasInode, minFields) if err != nil { - return nil, fmt.Errorf("failed to parse /proc/net/unix data %q: %w", line, err) + return nil, fmt.Errorf("%s: /proc/net/unix encountered data %q: %w", ErrFileParse, line, err) } nu.Rows = append(nu.Rows, item) } if err := s.Err(); err != nil { - return nil, fmt.Errorf("failed to scan /proc/net/unix data: %w", err) + return nil, fmt.Errorf("%s: /proc/net/unix encountered data: %w", ErrFileParse, err) } return &nu, nil @@ -126,7 +126,7 @@ func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, l := len(fields) if l < min { - return nil, fmt.Errorf("expected at least %d fields but got %d", min, l) + return nil, fmt.Errorf("%w: expected at least %d fields but got %d", ErrFileParse, min, l) } // Field offsets are as follows: @@ -136,29 +136,29 @@ func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, users, err := u.parseUsers(fields[1]) if err != nil { - return nil, fmt.Errorf("failed to parse ref count %q: %w", fields[1], err) + return nil, fmt.Errorf("%s: ref count %q: %w", ErrFileParse, fields[1], err) } flags, err := u.parseFlags(fields[3]) if err != nil { - return nil, fmt.Errorf("failed to parse flags %q: %w", fields[3], err) + return nil, fmt.Errorf("%s: Unable to parse flags %q: %w", ErrFileParse, fields[3], err) } typ, err := u.parseType(fields[4]) if err != nil { - return nil, fmt.Errorf("failed to parse type %q: %w", fields[4], err) + return nil, fmt.Errorf("%s: Failed to parse type %q: %w", ErrFileParse, fields[4], err) } state, err := u.parseState(fields[5]) if err != nil { - return nil, fmt.Errorf("failed to parse state %q: %w", fields[5], err) + return nil, fmt.Errorf("%s: Failed to parse state %q: %w", ErrFileParse, fields[5], err) } var inode uint64 if hasInode { inode, err = u.parseInode(fields[6]) if err != nil { - return nil, fmt.Errorf("failed to parse inode %q: %w", fields[6], err) + return nil, fmt.Errorf("%s failed to parse inode %q: %w", ErrFileParse, fields[6], err) } } diff --git a/vendor/github.com/prometheus/procfs/net_wireless.go b/vendor/github.com/prometheus/procfs/net_wireless.go new file mode 100644 index 000000000..7443edca9 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/net_wireless.go @@ -0,0 +1,182 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// Wireless models the content of /proc/net/wireless. +type Wireless struct { + Name string + + // Status is the current 4-digit hex value status of the interface. + Status uint64 + + // QualityLink is the link quality. + QualityLink int + + // QualityLevel is the signal gain (dBm). + QualityLevel int + + // QualityNoise is the signal noise baseline (dBm). + QualityNoise int + + // DiscardedNwid is the number of discarded packets with wrong nwid/essid. + DiscardedNwid int + + // DiscardedCrypt is the number of discarded packets with wrong code/decode (WEP). + DiscardedCrypt int + + // DiscardedFrag is the number of discarded packets that can't perform MAC reassembly. + DiscardedFrag int + + // DiscardedRetry is the number of discarded packets that reached max MAC retries. + DiscardedRetry int + + // DiscardedMisc is the number of discarded packets for other reasons. + DiscardedMisc int + + // MissedBeacon is the number of missed beacons/superframe. + MissedBeacon int +} + +// Wireless returns kernel wireless statistics. +func (fs FS) Wireless() ([]*Wireless, error) { + b, err := util.ReadFileNoStat(fs.proc.Path("net/wireless")) + if err != nil { + return nil, err + } + + m, err := parseWireless(bytes.NewReader(b)) + if err != nil { + return nil, fmt.Errorf("%s: wireless: %w", ErrFileParse, err) + } + + return m, nil +} + +// parseWireless parses the contents of /proc/net/wireless. +/* +Inter-| sta-| Quality | Discarded packets | Missed | WE +face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 + eth1: 0000 5. -256. -10. 0 1 0 3 0 0 + eth2: 0000 5. -256. -20. 0 2 0 4 0 0 +*/ +func parseWireless(r io.Reader) ([]*Wireless, error) { + var ( + interfaces []*Wireless + scanner = bufio.NewScanner(r) + ) + + for n := 0; scanner.Scan(); n++ { + // Skip the 2 header lines. + if n < 2 { + continue + } + + line := scanner.Text() + + parts := strings.Split(line, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("%w: expected 2 parts after splitting line by ':', got %d for line %q", ErrFileParse, len(parts), line) + } + + name := strings.TrimSpace(parts[0]) + stats := strings.Fields(parts[1]) + + if len(stats) < 10 { + return nil, fmt.Errorf("%w: invalid number of fields in line %d, expected 10+, got %d: %q", ErrFileParse, n, len(stats), line) + } + + status, err := strconv.ParseUint(stats[0], 16, 16) + if err != nil { + return nil, fmt.Errorf("%w: invalid status in line %d: %q", ErrFileParse, n, line) + } + + qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], ".")) + if err != nil { + return nil, fmt.Errorf("%s: parse Quality:link as integer %q: %w", ErrFileParse, qlink, err) + } + + qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], ".")) + if err != nil { + return nil, fmt.Errorf("%s: Quality:level as integer %q: %w", ErrFileParse, qlevel, err) + } + + qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], ".")) + if err != nil { + return nil, fmt.Errorf("%s: Quality:noise as integer %q: %w", ErrFileParse, qnoise, err) + } + + dnwid, err := strconv.Atoi(stats[4]) + if err != nil { + return nil, fmt.Errorf("%s: Discarded:nwid as integer %q: %w", ErrFileParse, dnwid, err) + } + + dcrypt, err := strconv.Atoi(stats[5]) + if err != nil { + return nil, fmt.Errorf("%s: Discarded:crypt as integer %q: %w", ErrFileParse, dcrypt, err) + } + + dfrag, err := strconv.Atoi(stats[6]) + if err != nil { + return nil, fmt.Errorf("%s: Discarded:frag as integer %q: %w", ErrFileParse, dfrag, err) + } + + dretry, err := strconv.Atoi(stats[7]) + if err != nil { + return nil, fmt.Errorf("%s: Discarded:retry as integer %q: %w", ErrFileParse, dretry, err) + } + + dmisc, err := strconv.Atoi(stats[8]) + if err != nil { + return nil, fmt.Errorf("%s: Discarded:misc as integer %q: %w", ErrFileParse, dmisc, err) + } + + mbeacon, err := strconv.Atoi(stats[9]) + if err != nil { + return nil, fmt.Errorf("%s: Missed:beacon as integer %q: %w", ErrFileParse, mbeacon, err) + } + + w := &Wireless{ + Name: name, + Status: status, + QualityLink: qlink, + QualityLevel: qlevel, + QualityNoise: qnoise, + DiscardedNwid: dnwid, + DiscardedCrypt: dcrypt, + DiscardedFrag: dfrag, + DiscardedRetry: dretry, + DiscardedMisc: dmisc, + MissedBeacon: mbeacon, + } + + interfaces = append(interfaces, w) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("%s: Failed to scan /proc/net/wireless: %w", ErrFileRead, err) + } + + return interfaces, nil +} diff --git a/vendor/github.com/prometheus/procfs/net_xfrm.go b/vendor/github.com/prometheus/procfs/net_xfrm.go index f9d9d243d..932ef2046 100644 --- a/vendor/github.com/prometheus/procfs/net_xfrm.go +++ b/vendor/github.com/prometheus/procfs/net_xfrm.go @@ -115,7 +115,7 @@ func (fs FS) NewXfrmStat() (XfrmStat, error) { fields := strings.Fields(s.Text()) if len(fields) != 2 { - return XfrmStat{}, fmt.Errorf("couldn't parse %q line %q", file.Name(), s.Text()) + return XfrmStat{}, fmt.Errorf("%w: %q line %q", ErrFileParse, file.Name(), s.Text()) } name := fields[0] diff --git a/vendor/github.com/prometheus/procfs/netstat.go b/vendor/github.com/prometheus/procfs/netstat.go index dcea9c5a6..742dff453 100644 --- a/vendor/github.com/prometheus/procfs/netstat.go +++ b/vendor/github.com/prometheus/procfs/netstat.go @@ -37,32 +37,46 @@ func (fs FS) NetStat() ([]NetStat, error) { var netStatsTotal []NetStat for _, filePath := range statFiles { - file, err := os.Open(filePath) + procNetstat, err := parseNetstat(filePath) if err != nil { return nil, err } + procNetstat.Filename = filepath.Base(filePath) - netStatFile := NetStat{ - Filename: filepath.Base(filePath), - Stats: make(map[string][]uint64), - } - scanner := bufio.NewScanner(file) - scanner.Scan() - // First string is always a header for stats - var headers []string - headers = append(headers, strings.Fields(scanner.Text())...) + netStatsTotal = append(netStatsTotal, procNetstat) + } + return netStatsTotal, nil +} + +// parseNetstat parses the metrics from `/proc/net/stat/` file +// and returns a NetStat structure. +func parseNetstat(filePath string) (NetStat, error) { + netStat := NetStat{ + Stats: make(map[string][]uint64), + } + file, err := os.Open(filePath) + if err != nil { + return netStat, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + scanner.Scan() - // Other strings represent per-CPU counters - for scanner.Scan() { - for num, counter := range strings.Fields(scanner.Text()) { - value, err := strconv.ParseUint(counter, 16, 64) - if err != nil { - return nil, err - } - netStatFile.Stats[headers[num]] = append(netStatFile.Stats[headers[num]], value) + // First string is always a header for stats + var headers []string + headers = append(headers, strings.Fields(scanner.Text())...) + + // Other strings represent per-CPU counters + for scanner.Scan() { + for num, counter := range strings.Fields(scanner.Text()) { + value, err := strconv.ParseUint(counter, 16, 64) + if err != nil { + return NetStat{}, err } + netStat.Stats[headers[num]] = append(netStat.Stats[headers[num]], value) } - netStatsTotal = append(netStatsTotal, netStatFile) } - return netStatsTotal, nil + + return netStat, nil } diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go index c30223af7..d1f71caa5 100644 --- a/vendor/github.com/prometheus/procfs/proc.go +++ b/vendor/github.com/prometheus/procfs/proc.go @@ -15,13 +15,13 @@ package procfs import ( "bytes" + "errors" "fmt" "io" "os" "strconv" "strings" - "github.com/prometheus/procfs/internal/fs" "github.com/prometheus/procfs/internal/util" ) @@ -30,12 +30,18 @@ type Proc struct { // The process ID. PID int - fs fs.FS + fs FS } // Procs represents a list of Proc structs. type Procs []Proc +var ( + ErrFileParse = errors.New("Error Parsing File") + ErrFileRead = errors.New("Error Reading File") + ErrMountPoint = errors.New("Error Accessing Mount point") +) + func (p Procs) Len() int { return len(p) } func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID } @@ -43,7 +49,7 @@ func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID } // Self returns a process for the current process read via /proc/self. func Self() (Proc, error) { fs, err := NewFS(DefaultMountPoint) - if err != nil { + if err != nil || errors.Unwrap(err) == ErrMountPoint { return Proc{}, err } return fs.Self() @@ -92,7 +98,7 @@ func (fs FS) Proc(pid int) (Proc, error) { if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil { return Proc{}, err } - return Proc{PID: pid, fs: fs.proc}, nil + return Proc{PID: pid, fs: fs}, nil } // AllProcs returns a list of all currently available processes. @@ -105,7 +111,7 @@ func (fs FS) AllProcs() (Procs, error) { names, err := d.Readdirnames(-1) if err != nil { - return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err) + return Procs{}, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err) } p := Procs{} @@ -114,7 +120,7 @@ func (fs FS) AllProcs() (Procs, error) { if err != nil { continue } - p = append(p, Proc{PID: int(pid), fs: fs.proc}) + p = append(p, Proc{PID: int(pid), fs: fs}) } return p, nil @@ -206,7 +212,7 @@ func (p Proc) FileDescriptors() ([]uintptr, error) { for i, n := range names { fd, err := strconv.ParseInt(n, 10, 32) if err != nil { - return nil, fmt.Errorf("could not parse fd %q: %w", n, err) + return nil, fmt.Errorf("%s: Cannot parse line: %v: %w", ErrFileParse, i, err) } fds[i] = uintptr(fd) } @@ -237,6 +243,19 @@ func (p Proc) FileDescriptorTargets() ([]string, error) { // FileDescriptorsLen returns the number of currently open file descriptors of // a process. func (p Proc) FileDescriptorsLen() (int, error) { + // Use fast path if available (Linux v6.2): https://github.com/torvalds/linux/commit/f1f1f2569901 + if p.fs.isReal { + stat, err := os.Stat(p.path("fd")) + if err != nil { + return 0, err + } + + size := stat.Size() + if size > 0 { + return int(size), nil + } + } + fds, err := p.fileDescriptors() if err != nil { return 0, err @@ -278,14 +297,14 @@ func (p Proc) fileDescriptors() ([]string, error) { names, err := d.Readdirnames(-1) if err != nil { - return nil, fmt.Errorf("could not read %q: %w", d.Name(), err) + return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err) } return names, nil } func (p Proc) path(pa ...string) string { - return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...) + return p.fs.proc.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...) } // FileDescriptorsInfo retrieves information about all file descriptors of diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go index cca03327c..daeed7f57 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroup.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go @@ -23,7 +23,7 @@ import ( "github.com/prometheus/procfs/internal/util" ) -// Cgroup models one line from /proc/[pid]/cgroup. Each Cgroup struct describes the the placement of a PID inside a +// Cgroup models one line from /proc/[pid]/cgroup. Each Cgroup struct describes the placement of a PID inside a // specific control hierarchy. The kernel has two cgroup APIs, v1 and v2. v1 has one hierarchy per available resource // controller, while v2 has one unified hierarchy shared by all controllers. Regardless of v1 or v2, all hierarchies // contain all running processes, so the question answerable with a Cgroup struct is 'where is this process in @@ -51,7 +51,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) { fields := strings.SplitN(cgroupStr, ":", 3) if len(fields) < 3 { - return nil, fmt.Errorf("at least 3 fields required, found %d fields in cgroup string: %s", len(fields), cgroupStr) + return nil, fmt.Errorf("%w: 3+ fields required, found %d fields in cgroup string: %s", ErrFileParse, len(fields), cgroupStr) } cgroup := &Cgroup{ @@ -60,7 +60,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) { } cgroup.HierarchyID, err = strconv.Atoi(fields[0]) if err != nil { - return nil, fmt.Errorf("failed to parse hierarchy ID") + return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, cgroup.HierarchyID) } if fields[1] != "" { ssNames := strings.Split(fields[1], ",") diff --git a/vendor/github.com/prometheus/procfs/proc_cgroups.go b/vendor/github.com/prometheus/procfs/proc_cgroups.go index 24d4dce9c..5dd493899 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroups.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroups.go @@ -46,7 +46,7 @@ func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) { fields := strings.Fields(CgroupSummaryStr) // require at least 4 fields if len(fields) < 4 { - return nil, fmt.Errorf("at least 4 fields required, found %d fields in cgroup info string: %s", len(fields), CgroupSummaryStr) + return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), CgroupSummaryStr) } CgroupSummary := &CgroupSummary{ @@ -54,15 +54,15 @@ func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) { } CgroupSummary.Hierarchy, err = strconv.Atoi(fields[1]) if err != nil { - return nil, fmt.Errorf("failed to parse hierarchy ID") + return nil, fmt.Errorf("%w: Unable to parse hierarchy ID from %q", ErrFileParse, fields[1]) } CgroupSummary.Cgroups, err = strconv.Atoi(fields[2]) if err != nil { - return nil, fmt.Errorf("failed to parse Cgroup Num") + return nil, fmt.Errorf("%w: Unable to parse Cgroup Num from %q", ErrFileParse, fields[2]) } CgroupSummary.Enabled, err = strconv.Atoi(fields[3]) if err != nil { - return nil, fmt.Errorf("failed to parse Enabled") + return nil, fmt.Errorf("%w: Unable to parse Enabled from %q", ErrFileParse, fields[3]) } return CgroupSummary, nil } diff --git a/vendor/github.com/prometheus/procfs/proc_fdinfo.go b/vendor/github.com/prometheus/procfs/proc_fdinfo.go index 1bbdd4a8e..fa761b352 100644 --- a/vendor/github.com/prometheus/procfs/proc_fdinfo.go +++ b/vendor/github.com/prometheus/procfs/proc_fdinfo.go @@ -26,6 +26,7 @@ var ( rPos = regexp.MustCompile(`^pos:\s+(\d+)$`) rFlags = regexp.MustCompile(`^flags:\s+(\d+)$`) rMntID = regexp.MustCompile(`^mnt_id:\s+(\d+)$`) + rIno = regexp.MustCompile(`^ino:\s+(\d+)$`) rInotify = regexp.MustCompile(`^inotify`) rInotifyParts = regexp.MustCompile(`^inotify\s+wd:([0-9a-f]+)\s+ino:([0-9a-f]+)\s+sdev:([0-9a-f]+)(?:\s+mask:([0-9a-f]+))?`) ) @@ -40,6 +41,8 @@ type ProcFDInfo struct { Flags string // Mount point ID MntID string + // Inode number + Ino string // List of inotify lines (structured) in the fdinfo file (kernel 3.8+ only) InotifyInfos []InotifyInfo } @@ -51,7 +54,7 @@ func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { return nil, err } - var text, pos, flags, mntid string + var text, pos, flags, mntid, ino string var inotify []InotifyInfo scanner := bufio.NewScanner(bytes.NewReader(data)) @@ -63,6 +66,8 @@ func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { flags = rFlags.FindStringSubmatch(text)[1] } else if rMntID.MatchString(text) { mntid = rMntID.FindStringSubmatch(text)[1] + } else if rIno.MatchString(text) { + ino = rIno.FindStringSubmatch(text)[1] } else if rInotify.MatchString(text) { newInotify, err := parseInotifyInfo(text) if err != nil { @@ -77,6 +82,7 @@ func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { Pos: pos, Flags: flags, MntID: mntid, + Ino: ino, InotifyInfos: inotify, } @@ -111,7 +117,7 @@ func parseInotifyInfo(line string) (*InotifyInfo, error) { } return i, nil } - return nil, fmt.Errorf("invalid inode entry: %q", line) + return nil, fmt.Errorf("%w: invalid inode entry: %q", ErrFileParse, line) } // ProcFDInfos represents a list of ProcFDInfo structs. diff --git a/vendor/github.com/prometheus/procfs/proc_interrupts.go b/vendor/github.com/prometheus/procfs/proc_interrupts.go new file mode 100644 index 000000000..86b4b4524 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/proc_interrupts.go @@ -0,0 +1,98 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// Interrupt represents a single interrupt line. +type Interrupt struct { + // Info is the type of interrupt. + Info string + // Devices is the name of the device that is located at that IRQ + Devices string + // Values is the number of interrupts per CPU. + Values []string +} + +// Interrupts models the content of /proc/interrupts. Key is the IRQ number. +// - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-interrupts +// - https://raspberrypi.stackexchange.com/questions/105802/explanation-of-proc-interrupts-output +type Interrupts map[string]Interrupt + +// Interrupts creates a new instance from a given Proc instance. +func (p Proc) Interrupts() (Interrupts, error) { + data, err := util.ReadFileNoStat(p.path("interrupts")) + if err != nil { + return nil, err + } + return parseInterrupts(bytes.NewReader(data)) +} + +func parseInterrupts(r io.Reader) (Interrupts, error) { + var ( + interrupts = Interrupts{} + scanner = bufio.NewScanner(r) + ) + + if !scanner.Scan() { + return nil, errors.New("interrupts empty") + } + cpuNum := len(strings.Fields(scanner.Text())) // one header per cpu + + for scanner.Scan() { + parts := strings.Fields(scanner.Text()) + if len(parts) == 0 { // skip empty lines + continue + } + if len(parts) < 2 { + return nil, fmt.Errorf("%w: Not enough fields in interrupts (expected 2+ fields but got %d): %s", ErrFileParse, len(parts), parts) + } + intName := parts[0][:len(parts[0])-1] // remove trailing : + + if len(parts) == 2 { + interrupts[intName] = Interrupt{ + Info: "", + Devices: "", + Values: []string{ + parts[1], + }, + } + continue + } + + intr := Interrupt{ + Values: parts[1 : cpuNum+1], + } + + if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt + intr.Info = parts[cpuNum+1] + intr.Devices = strings.Join(parts[cpuNum+2:], " ") + } else { + intr.Info = strings.Join(parts[cpuNum+1:], " ") + } + interrupts[intName] = intr + } + + return interrupts, scanner.Err() +} diff --git a/vendor/github.com/prometheus/procfs/proc_limits.go b/vendor/github.com/prometheus/procfs/proc_limits.go index 7a1388185..c86d815d7 100644 --- a/vendor/github.com/prometheus/procfs/proc_limits.go +++ b/vendor/github.com/prometheus/procfs/proc_limits.go @@ -103,7 +103,7 @@ func (p Proc) Limits() (ProcLimits, error) { //fields := limitsMatch.Split(s.Text(), limitsFields) fields := limitsMatch.FindStringSubmatch(s.Text()) if len(fields) != limitsFields { - return ProcLimits{}, fmt.Errorf("couldn't parse %q line %q", f.Name(), s.Text()) + return ProcLimits{}, fmt.Errorf("%w: couldn't parse %q line %q", ErrFileParse, f.Name(), s.Text()) } switch fields[1] { @@ -154,7 +154,7 @@ func parseUint(s string) (uint64, error) { } i, err := strconv.ParseUint(s, 10, 64) if err != nil { - return 0, fmt.Errorf("couldn't parse value %q: %w", s, err) + return 0, fmt.Errorf("%s: couldn't parse value %q: %w", ErrFileParse, s, err) } return i, nil } diff --git a/vendor/github.com/prometheus/procfs/proc_maps.go b/vendor/github.com/prometheus/procfs/proc_maps.go index f1bcbf32b..7e75c286b 100644 --- a/vendor/github.com/prometheus/procfs/proc_maps.go +++ b/vendor/github.com/prometheus/procfs/proc_maps.go @@ -63,17 +63,17 @@ type ProcMap struct { // parseDevice parses the device token of a line and converts it to a dev_t // (mkdev) like structure. func parseDevice(s string) (uint64, error) { - toks := strings.Split(s, ":") - if len(toks) < 2 { - return 0, fmt.Errorf("unexpected number of fields") + i := strings.Index(s, ":") + if i == -1 { + return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, s) } - major, err := strconv.ParseUint(toks[0], 16, 0) + major, err := strconv.ParseUint(s[0:i], 16, 0) if err != nil { return 0, err } - minor, err := strconv.ParseUint(toks[1], 16, 0) + minor, err := strconv.ParseUint(s[i+1:], 16, 0) if err != nil { return 0, err } @@ -93,17 +93,17 @@ func parseAddress(s string) (uintptr, error) { // parseAddresses parses the start-end address. func parseAddresses(s string) (uintptr, uintptr, error) { - toks := strings.Split(s, "-") - if len(toks) < 2 { - return 0, 0, fmt.Errorf("invalid address") + idx := strings.Index(s, "-") + if idx == -1 { + return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, s) } - saddr, err := parseAddress(toks[0]) + saddr, err := parseAddress(s[0:idx]) if err != nil { return 0, 0, err } - eaddr, err := parseAddress(toks[1]) + eaddr, err := parseAddress(s[idx+1:]) if err != nil { return 0, 0, err } @@ -114,7 +114,7 @@ func parseAddresses(s string) (uintptr, uintptr, error) { // parsePermissions parses a token and returns any that are set. func parsePermissions(s string) (*ProcMapPermissions, error) { if len(s) < 4 { - return nil, fmt.Errorf("invalid permissions token") + return nil, fmt.Errorf("%w: invalid permissions token", ErrFileParse) } perms := ProcMapPermissions{} @@ -141,7 +141,7 @@ func parsePermissions(s string) (*ProcMapPermissions, error) { func parseProcMap(text string) (*ProcMap, error) { fields := strings.Fields(text) if len(fields) < 5 { - return nil, fmt.Errorf("truncated procmap entry") + return nil, fmt.Errorf("%w: truncated procmap entry", ErrFileParse) } saddr, eaddr, err := parseAddresses(fields[0]) diff --git a/vendor/github.com/prometheus/procfs/proc_netstat.go b/vendor/github.com/prometheus/procfs/proc_netstat.go index 48b523819..8e3ff4d79 100644 --- a/vendor/github.com/prometheus/procfs/proc_netstat.go +++ b/vendor/github.com/prometheus/procfs/proc_netstat.go @@ -33,139 +33,140 @@ type ProcNetstat struct { } type TcpExt struct { // nolint:revive - SyncookiesSent float64 - SyncookiesRecv float64 - SyncookiesFailed float64 - EmbryonicRsts float64 - PruneCalled float64 - RcvPruned float64 - OfoPruned float64 - OutOfWindowIcmps float64 - LockDroppedIcmps float64 - ArpFilter float64 - TW float64 - TWRecycled float64 - TWKilled float64 - PAWSActive float64 - PAWSEstab float64 - DelayedACKs float64 - DelayedACKLocked float64 - DelayedACKLost float64 - ListenOverflows float64 - ListenDrops float64 - TCPHPHits float64 - TCPPureAcks float64 - TCPHPAcks float64 - TCPRenoRecovery float64 - TCPSackRecovery float64 - TCPSACKReneging float64 - TCPSACKReorder float64 - TCPRenoReorder float64 - TCPTSReorder float64 - TCPFullUndo float64 - TCPPartialUndo float64 - TCPDSACKUndo float64 - TCPLossUndo float64 - TCPLostRetransmit float64 - TCPRenoFailures float64 - TCPSackFailures float64 - TCPLossFailures float64 - TCPFastRetrans float64 - TCPSlowStartRetrans float64 - TCPTimeouts float64 - TCPLossProbes float64 - TCPLossProbeRecovery float64 - TCPRenoRecoveryFail float64 - TCPSackRecoveryFail float64 - TCPRcvCollapsed float64 - TCPDSACKOldSent float64 - TCPDSACKOfoSent float64 - TCPDSACKRecv float64 - TCPDSACKOfoRecv float64 - TCPAbortOnData float64 - TCPAbortOnClose float64 - TCPAbortOnMemory float64 - TCPAbortOnTimeout float64 - TCPAbortOnLinger float64 - TCPAbortFailed float64 - TCPMemoryPressures float64 - TCPMemoryPressuresChrono float64 - TCPSACKDiscard float64 - TCPDSACKIgnoredOld float64 - TCPDSACKIgnoredNoUndo float64 - TCPSpuriousRTOs float64 - TCPMD5NotFound float64 - TCPMD5Unexpected float64 - TCPMD5Failure float64 - TCPSackShifted float64 - TCPSackMerged float64 - TCPSackShiftFallback float64 - TCPBacklogDrop float64 - PFMemallocDrop float64 - TCPMinTTLDrop float64 - TCPDeferAcceptDrop float64 - IPReversePathFilter float64 - TCPTimeWaitOverflow float64 - TCPReqQFullDoCookies float64 - TCPReqQFullDrop float64 - TCPRetransFail float64 - TCPRcvCoalesce float64 - TCPOFOQueue float64 - TCPOFODrop float64 - TCPOFOMerge float64 - TCPChallengeACK float64 - TCPSYNChallenge float64 - TCPFastOpenActive float64 - TCPFastOpenActiveFail float64 - TCPFastOpenPassive float64 - TCPFastOpenPassiveFail float64 - TCPFastOpenListenOverflow float64 - TCPFastOpenCookieReqd float64 - TCPFastOpenBlackhole float64 - TCPSpuriousRtxHostQueues float64 - BusyPollRxPackets float64 - TCPAutoCorking float64 - TCPFromZeroWindowAdv float64 - TCPToZeroWindowAdv float64 - TCPWantZeroWindowAdv float64 - TCPSynRetrans float64 - TCPOrigDataSent float64 - TCPHystartTrainDetect float64 - TCPHystartTrainCwnd float64 - TCPHystartDelayDetect float64 - TCPHystartDelayCwnd float64 - TCPACKSkippedSynRecv float64 - TCPACKSkippedPAWS float64 - TCPACKSkippedSeq float64 - TCPACKSkippedFinWait2 float64 - TCPACKSkippedTimeWait float64 - TCPACKSkippedChallenge float64 - TCPWinProbe float64 - TCPKeepAlive float64 - TCPMTUPFail float64 - TCPMTUPSuccess float64 - TCPWqueueTooBig float64 + SyncookiesSent *float64 + SyncookiesRecv *float64 + SyncookiesFailed *float64 + EmbryonicRsts *float64 + PruneCalled *float64 + RcvPruned *float64 + OfoPruned *float64 + OutOfWindowIcmps *float64 + LockDroppedIcmps *float64 + ArpFilter *float64 + TW *float64 + TWRecycled *float64 + TWKilled *float64 + PAWSActive *float64 + PAWSEstab *float64 + DelayedACKs *float64 + DelayedACKLocked *float64 + DelayedACKLost *float64 + ListenOverflows *float64 + ListenDrops *float64 + TCPHPHits *float64 + TCPPureAcks *float64 + TCPHPAcks *float64 + TCPRenoRecovery *float64 + TCPSackRecovery *float64 + TCPSACKReneging *float64 + TCPSACKReorder *float64 + TCPRenoReorder *float64 + TCPTSReorder *float64 + TCPFullUndo *float64 + TCPPartialUndo *float64 + TCPDSACKUndo *float64 + TCPLossUndo *float64 + TCPLostRetransmit *float64 + TCPRenoFailures *float64 + TCPSackFailures *float64 + TCPLossFailures *float64 + TCPFastRetrans *float64 + TCPSlowStartRetrans *float64 + TCPTimeouts *float64 + TCPLossProbes *float64 + TCPLossProbeRecovery *float64 + TCPRenoRecoveryFail *float64 + TCPSackRecoveryFail *float64 + TCPRcvCollapsed *float64 + TCPDSACKOldSent *float64 + TCPDSACKOfoSent *float64 + TCPDSACKRecv *float64 + TCPDSACKOfoRecv *float64 + TCPAbortOnData *float64 + TCPAbortOnClose *float64 + TCPAbortOnMemory *float64 + TCPAbortOnTimeout *float64 + TCPAbortOnLinger *float64 + TCPAbortFailed *float64 + TCPMemoryPressures *float64 + TCPMemoryPressuresChrono *float64 + TCPSACKDiscard *float64 + TCPDSACKIgnoredOld *float64 + TCPDSACKIgnoredNoUndo *float64 + TCPSpuriousRTOs *float64 + TCPMD5NotFound *float64 + TCPMD5Unexpected *float64 + TCPMD5Failure *float64 + TCPSackShifted *float64 + TCPSackMerged *float64 + TCPSackShiftFallback *float64 + TCPBacklogDrop *float64 + PFMemallocDrop *float64 + TCPMinTTLDrop *float64 + TCPDeferAcceptDrop *float64 + IPReversePathFilter *float64 + TCPTimeWaitOverflow *float64 + TCPReqQFullDoCookies *float64 + TCPReqQFullDrop *float64 + TCPRetransFail *float64 + TCPRcvCoalesce *float64 + TCPRcvQDrop *float64 + TCPOFOQueue *float64 + TCPOFODrop *float64 + TCPOFOMerge *float64 + TCPChallengeACK *float64 + TCPSYNChallenge *float64 + TCPFastOpenActive *float64 + TCPFastOpenActiveFail *float64 + TCPFastOpenPassive *float64 + TCPFastOpenPassiveFail *float64 + TCPFastOpenListenOverflow *float64 + TCPFastOpenCookieReqd *float64 + TCPFastOpenBlackhole *float64 + TCPSpuriousRtxHostQueues *float64 + BusyPollRxPackets *float64 + TCPAutoCorking *float64 + TCPFromZeroWindowAdv *float64 + TCPToZeroWindowAdv *float64 + TCPWantZeroWindowAdv *float64 + TCPSynRetrans *float64 + TCPOrigDataSent *float64 + TCPHystartTrainDetect *float64 + TCPHystartTrainCwnd *float64 + TCPHystartDelayDetect *float64 + TCPHystartDelayCwnd *float64 + TCPACKSkippedSynRecv *float64 + TCPACKSkippedPAWS *float64 + TCPACKSkippedSeq *float64 + TCPACKSkippedFinWait2 *float64 + TCPACKSkippedTimeWait *float64 + TCPACKSkippedChallenge *float64 + TCPWinProbe *float64 + TCPKeepAlive *float64 + TCPMTUPFail *float64 + TCPMTUPSuccess *float64 + TCPWqueueTooBig *float64 } type IpExt struct { // nolint:revive - InNoRoutes float64 - InTruncatedPkts float64 - InMcastPkts float64 - OutMcastPkts float64 - InBcastPkts float64 - OutBcastPkts float64 - InOctets float64 - OutOctets float64 - InMcastOctets float64 - OutMcastOctets float64 - InBcastOctets float64 - OutBcastOctets float64 - InCsumErrors float64 - InNoECTPkts float64 - InECT1Pkts float64 - InECT0Pkts float64 - InCEPkts float64 - ReasmOverlaps float64 + InNoRoutes *float64 + InTruncatedPkts *float64 + InMcastPkts *float64 + OutMcastPkts *float64 + InBcastPkts *float64 + OutBcastPkts *float64 + InOctets *float64 + OutOctets *float64 + InMcastOctets *float64 + OutMcastOctets *float64 + InBcastOctets *float64 + OutBcastOctets *float64 + InCsumErrors *float64 + InNoECTPkts *float64 + InECT1Pkts *float64 + InECT0Pkts *float64 + InCEPkts *float64 + ReasmOverlaps *float64 } func (p Proc) Netstat() (ProcNetstat, error) { @@ -174,14 +175,14 @@ func (p Proc) Netstat() (ProcNetstat, error) { if err != nil { return ProcNetstat{PID: p.PID}, err } - procNetstat, err := parseNetstat(bytes.NewReader(data), filename) + procNetstat, err := parseProcNetstat(bytes.NewReader(data), filename) procNetstat.PID = p.PID return procNetstat, err } -// parseNetstat parses the metrics from proc//net/netstat file +// parseProcNetstat parses the metrics from proc//net/netstat file // and returns a ProcNetstat structure. -func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { +func parseProcNetstat(r io.Reader, fileName string) (ProcNetstat, error) { var ( scanner = bufio.NewScanner(r) procNetstat = ProcNetstat{} @@ -194,8 +195,8 @@ func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { // Remove trailing :. protocol := strings.TrimSuffix(nameParts[0], ":") if len(nameParts) != len(valueParts) { - return procNetstat, fmt.Errorf("mismatch field count mismatch in %s: %s", - fileName, protocol) + return procNetstat, fmt.Errorf("%w: mismatch field count mismatch in %s: %s", + ErrFileParse, fileName, protocol) } for i := 1; i < len(nameParts); i++ { value, err := strconv.ParseFloat(valueParts[i], 64) @@ -208,230 +209,232 @@ func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { case "TcpExt": switch key { case "SyncookiesSent": - procNetstat.TcpExt.SyncookiesSent = value + procNetstat.TcpExt.SyncookiesSent = &value case "SyncookiesRecv": - procNetstat.TcpExt.SyncookiesRecv = value + procNetstat.TcpExt.SyncookiesRecv = &value case "SyncookiesFailed": - procNetstat.TcpExt.SyncookiesFailed = value + procNetstat.TcpExt.SyncookiesFailed = &value case "EmbryonicRsts": - procNetstat.TcpExt.EmbryonicRsts = value + procNetstat.TcpExt.EmbryonicRsts = &value case "PruneCalled": - procNetstat.TcpExt.PruneCalled = value + procNetstat.TcpExt.PruneCalled = &value case "RcvPruned": - procNetstat.TcpExt.RcvPruned = value + procNetstat.TcpExt.RcvPruned = &value case "OfoPruned": - procNetstat.TcpExt.OfoPruned = value + procNetstat.TcpExt.OfoPruned = &value case "OutOfWindowIcmps": - procNetstat.TcpExt.OutOfWindowIcmps = value + procNetstat.TcpExt.OutOfWindowIcmps = &value case "LockDroppedIcmps": - procNetstat.TcpExt.LockDroppedIcmps = value + procNetstat.TcpExt.LockDroppedIcmps = &value case "ArpFilter": - procNetstat.TcpExt.ArpFilter = value + procNetstat.TcpExt.ArpFilter = &value case "TW": - procNetstat.TcpExt.TW = value + procNetstat.TcpExt.TW = &value case "TWRecycled": - procNetstat.TcpExt.TWRecycled = value + procNetstat.TcpExt.TWRecycled = &value case "TWKilled": - procNetstat.TcpExt.TWKilled = value + procNetstat.TcpExt.TWKilled = &value case "PAWSActive": - procNetstat.TcpExt.PAWSActive = value + procNetstat.TcpExt.PAWSActive = &value case "PAWSEstab": - procNetstat.TcpExt.PAWSEstab = value + procNetstat.TcpExt.PAWSEstab = &value case "DelayedACKs": - procNetstat.TcpExt.DelayedACKs = value + procNetstat.TcpExt.DelayedACKs = &value case "DelayedACKLocked": - procNetstat.TcpExt.DelayedACKLocked = value + procNetstat.TcpExt.DelayedACKLocked = &value case "DelayedACKLost": - procNetstat.TcpExt.DelayedACKLost = value + procNetstat.TcpExt.DelayedACKLost = &value case "ListenOverflows": - procNetstat.TcpExt.ListenOverflows = value + procNetstat.TcpExt.ListenOverflows = &value case "ListenDrops": - procNetstat.TcpExt.ListenDrops = value + procNetstat.TcpExt.ListenDrops = &value case "TCPHPHits": - procNetstat.TcpExt.TCPHPHits = value + procNetstat.TcpExt.TCPHPHits = &value case "TCPPureAcks": - procNetstat.TcpExt.TCPPureAcks = value + procNetstat.TcpExt.TCPPureAcks = &value case "TCPHPAcks": - procNetstat.TcpExt.TCPHPAcks = value + procNetstat.TcpExt.TCPHPAcks = &value case "TCPRenoRecovery": - procNetstat.TcpExt.TCPRenoRecovery = value + procNetstat.TcpExt.TCPRenoRecovery = &value case "TCPSackRecovery": - procNetstat.TcpExt.TCPSackRecovery = value + procNetstat.TcpExt.TCPSackRecovery = &value case "TCPSACKReneging": - procNetstat.TcpExt.TCPSACKReneging = value + procNetstat.TcpExt.TCPSACKReneging = &value case "TCPSACKReorder": - procNetstat.TcpExt.TCPSACKReorder = value + procNetstat.TcpExt.TCPSACKReorder = &value case "TCPRenoReorder": - procNetstat.TcpExt.TCPRenoReorder = value + procNetstat.TcpExt.TCPRenoReorder = &value case "TCPTSReorder": - procNetstat.TcpExt.TCPTSReorder = value + procNetstat.TcpExt.TCPTSReorder = &value case "TCPFullUndo": - procNetstat.TcpExt.TCPFullUndo = value + procNetstat.TcpExt.TCPFullUndo = &value case "TCPPartialUndo": - procNetstat.TcpExt.TCPPartialUndo = value + procNetstat.TcpExt.TCPPartialUndo = &value case "TCPDSACKUndo": - procNetstat.TcpExt.TCPDSACKUndo = value + procNetstat.TcpExt.TCPDSACKUndo = &value case "TCPLossUndo": - procNetstat.TcpExt.TCPLossUndo = value + procNetstat.TcpExt.TCPLossUndo = &value case "TCPLostRetransmit": - procNetstat.TcpExt.TCPLostRetransmit = value + procNetstat.TcpExt.TCPLostRetransmit = &value case "TCPRenoFailures": - procNetstat.TcpExt.TCPRenoFailures = value + procNetstat.TcpExt.TCPRenoFailures = &value case "TCPSackFailures": - procNetstat.TcpExt.TCPSackFailures = value + procNetstat.TcpExt.TCPSackFailures = &value case "TCPLossFailures": - procNetstat.TcpExt.TCPLossFailures = value + procNetstat.TcpExt.TCPLossFailures = &value case "TCPFastRetrans": - procNetstat.TcpExt.TCPFastRetrans = value + procNetstat.TcpExt.TCPFastRetrans = &value case "TCPSlowStartRetrans": - procNetstat.TcpExt.TCPSlowStartRetrans = value + procNetstat.TcpExt.TCPSlowStartRetrans = &value case "TCPTimeouts": - procNetstat.TcpExt.TCPTimeouts = value + procNetstat.TcpExt.TCPTimeouts = &value case "TCPLossProbes": - procNetstat.TcpExt.TCPLossProbes = value + procNetstat.TcpExt.TCPLossProbes = &value case "TCPLossProbeRecovery": - procNetstat.TcpExt.TCPLossProbeRecovery = value + procNetstat.TcpExt.TCPLossProbeRecovery = &value case "TCPRenoRecoveryFail": - procNetstat.TcpExt.TCPRenoRecoveryFail = value + procNetstat.TcpExt.TCPRenoRecoveryFail = &value case "TCPSackRecoveryFail": - procNetstat.TcpExt.TCPSackRecoveryFail = value + procNetstat.TcpExt.TCPSackRecoveryFail = &value case "TCPRcvCollapsed": - procNetstat.TcpExt.TCPRcvCollapsed = value + procNetstat.TcpExt.TCPRcvCollapsed = &value case "TCPDSACKOldSent": - procNetstat.TcpExt.TCPDSACKOldSent = value + procNetstat.TcpExt.TCPDSACKOldSent = &value case "TCPDSACKOfoSent": - procNetstat.TcpExt.TCPDSACKOfoSent = value + procNetstat.TcpExt.TCPDSACKOfoSent = &value case "TCPDSACKRecv": - procNetstat.TcpExt.TCPDSACKRecv = value + procNetstat.TcpExt.TCPDSACKRecv = &value case "TCPDSACKOfoRecv": - procNetstat.TcpExt.TCPDSACKOfoRecv = value + procNetstat.TcpExt.TCPDSACKOfoRecv = &value case "TCPAbortOnData": - procNetstat.TcpExt.TCPAbortOnData = value + procNetstat.TcpExt.TCPAbortOnData = &value case "TCPAbortOnClose": - procNetstat.TcpExt.TCPAbortOnClose = value + procNetstat.TcpExt.TCPAbortOnClose = &value case "TCPDeferAcceptDrop": - procNetstat.TcpExt.TCPDeferAcceptDrop = value + procNetstat.TcpExt.TCPDeferAcceptDrop = &value case "IPReversePathFilter": - procNetstat.TcpExt.IPReversePathFilter = value + procNetstat.TcpExt.IPReversePathFilter = &value case "TCPTimeWaitOverflow": - procNetstat.TcpExt.TCPTimeWaitOverflow = value + procNetstat.TcpExt.TCPTimeWaitOverflow = &value case "TCPReqQFullDoCookies": - procNetstat.TcpExt.TCPReqQFullDoCookies = value + procNetstat.TcpExt.TCPReqQFullDoCookies = &value case "TCPReqQFullDrop": - procNetstat.TcpExt.TCPReqQFullDrop = value + procNetstat.TcpExt.TCPReqQFullDrop = &value case "TCPRetransFail": - procNetstat.TcpExt.TCPRetransFail = value + procNetstat.TcpExt.TCPRetransFail = &value case "TCPRcvCoalesce": - procNetstat.TcpExt.TCPRcvCoalesce = value + procNetstat.TcpExt.TCPRcvCoalesce = &value + case "TCPRcvQDrop": + procNetstat.TcpExt.TCPRcvQDrop = &value case "TCPOFOQueue": - procNetstat.TcpExt.TCPOFOQueue = value + procNetstat.TcpExt.TCPOFOQueue = &value case "TCPOFODrop": - procNetstat.TcpExt.TCPOFODrop = value + procNetstat.TcpExt.TCPOFODrop = &value case "TCPOFOMerge": - procNetstat.TcpExt.TCPOFOMerge = value + procNetstat.TcpExt.TCPOFOMerge = &value case "TCPChallengeACK": - procNetstat.TcpExt.TCPChallengeACK = value + procNetstat.TcpExt.TCPChallengeACK = &value case "TCPSYNChallenge": - procNetstat.TcpExt.TCPSYNChallenge = value + procNetstat.TcpExt.TCPSYNChallenge = &value case "TCPFastOpenActive": - procNetstat.TcpExt.TCPFastOpenActive = value + procNetstat.TcpExt.TCPFastOpenActive = &value case "TCPFastOpenActiveFail": - procNetstat.TcpExt.TCPFastOpenActiveFail = value + procNetstat.TcpExt.TCPFastOpenActiveFail = &value case "TCPFastOpenPassive": - procNetstat.TcpExt.TCPFastOpenPassive = value + procNetstat.TcpExt.TCPFastOpenPassive = &value case "TCPFastOpenPassiveFail": - procNetstat.TcpExt.TCPFastOpenPassiveFail = value + procNetstat.TcpExt.TCPFastOpenPassiveFail = &value case "TCPFastOpenListenOverflow": - procNetstat.TcpExt.TCPFastOpenListenOverflow = value + procNetstat.TcpExt.TCPFastOpenListenOverflow = &value case "TCPFastOpenCookieReqd": - procNetstat.TcpExt.TCPFastOpenCookieReqd = value + procNetstat.TcpExt.TCPFastOpenCookieReqd = &value case "TCPFastOpenBlackhole": - procNetstat.TcpExt.TCPFastOpenBlackhole = value + procNetstat.TcpExt.TCPFastOpenBlackhole = &value case "TCPSpuriousRtxHostQueues": - procNetstat.TcpExt.TCPSpuriousRtxHostQueues = value + procNetstat.TcpExt.TCPSpuriousRtxHostQueues = &value case "BusyPollRxPackets": - procNetstat.TcpExt.BusyPollRxPackets = value + procNetstat.TcpExt.BusyPollRxPackets = &value case "TCPAutoCorking": - procNetstat.TcpExt.TCPAutoCorking = value + procNetstat.TcpExt.TCPAutoCorking = &value case "TCPFromZeroWindowAdv": - procNetstat.TcpExt.TCPFromZeroWindowAdv = value + procNetstat.TcpExt.TCPFromZeroWindowAdv = &value case "TCPToZeroWindowAdv": - procNetstat.TcpExt.TCPToZeroWindowAdv = value + procNetstat.TcpExt.TCPToZeroWindowAdv = &value case "TCPWantZeroWindowAdv": - procNetstat.TcpExt.TCPWantZeroWindowAdv = value + procNetstat.TcpExt.TCPWantZeroWindowAdv = &value case "TCPSynRetrans": - procNetstat.TcpExt.TCPSynRetrans = value + procNetstat.TcpExt.TCPSynRetrans = &value case "TCPOrigDataSent": - procNetstat.TcpExt.TCPOrigDataSent = value + procNetstat.TcpExt.TCPOrigDataSent = &value case "TCPHystartTrainDetect": - procNetstat.TcpExt.TCPHystartTrainDetect = value + procNetstat.TcpExt.TCPHystartTrainDetect = &value case "TCPHystartTrainCwnd": - procNetstat.TcpExt.TCPHystartTrainCwnd = value + procNetstat.TcpExt.TCPHystartTrainCwnd = &value case "TCPHystartDelayDetect": - procNetstat.TcpExt.TCPHystartDelayDetect = value + procNetstat.TcpExt.TCPHystartDelayDetect = &value case "TCPHystartDelayCwnd": - procNetstat.TcpExt.TCPHystartDelayCwnd = value + procNetstat.TcpExt.TCPHystartDelayCwnd = &value case "TCPACKSkippedSynRecv": - procNetstat.TcpExt.TCPACKSkippedSynRecv = value + procNetstat.TcpExt.TCPACKSkippedSynRecv = &value case "TCPACKSkippedPAWS": - procNetstat.TcpExt.TCPACKSkippedPAWS = value + procNetstat.TcpExt.TCPACKSkippedPAWS = &value case "TCPACKSkippedSeq": - procNetstat.TcpExt.TCPACKSkippedSeq = value + procNetstat.TcpExt.TCPACKSkippedSeq = &value case "TCPACKSkippedFinWait2": - procNetstat.TcpExt.TCPACKSkippedFinWait2 = value + procNetstat.TcpExt.TCPACKSkippedFinWait2 = &value case "TCPACKSkippedTimeWait": - procNetstat.TcpExt.TCPACKSkippedTimeWait = value + procNetstat.TcpExt.TCPACKSkippedTimeWait = &value case "TCPACKSkippedChallenge": - procNetstat.TcpExt.TCPACKSkippedChallenge = value + procNetstat.TcpExt.TCPACKSkippedChallenge = &value case "TCPWinProbe": - procNetstat.TcpExt.TCPWinProbe = value + procNetstat.TcpExt.TCPWinProbe = &value case "TCPKeepAlive": - procNetstat.TcpExt.TCPKeepAlive = value + procNetstat.TcpExt.TCPKeepAlive = &value case "TCPMTUPFail": - procNetstat.TcpExt.TCPMTUPFail = value + procNetstat.TcpExt.TCPMTUPFail = &value case "TCPMTUPSuccess": - procNetstat.TcpExt.TCPMTUPSuccess = value + procNetstat.TcpExt.TCPMTUPSuccess = &value case "TCPWqueueTooBig": - procNetstat.TcpExt.TCPWqueueTooBig = value + procNetstat.TcpExt.TCPWqueueTooBig = &value } case "IpExt": switch key { case "InNoRoutes": - procNetstat.IpExt.InNoRoutes = value + procNetstat.IpExt.InNoRoutes = &value case "InTruncatedPkts": - procNetstat.IpExt.InTruncatedPkts = value + procNetstat.IpExt.InTruncatedPkts = &value case "InMcastPkts": - procNetstat.IpExt.InMcastPkts = value + procNetstat.IpExt.InMcastPkts = &value case "OutMcastPkts": - procNetstat.IpExt.OutMcastPkts = value + procNetstat.IpExt.OutMcastPkts = &value case "InBcastPkts": - procNetstat.IpExt.InBcastPkts = value + procNetstat.IpExt.InBcastPkts = &value case "OutBcastPkts": - procNetstat.IpExt.OutBcastPkts = value + procNetstat.IpExt.OutBcastPkts = &value case "InOctets": - procNetstat.IpExt.InOctets = value + procNetstat.IpExt.InOctets = &value case "OutOctets": - procNetstat.IpExt.OutOctets = value + procNetstat.IpExt.OutOctets = &value case "InMcastOctets": - procNetstat.IpExt.InMcastOctets = value + procNetstat.IpExt.InMcastOctets = &value case "OutMcastOctets": - procNetstat.IpExt.OutMcastOctets = value + procNetstat.IpExt.OutMcastOctets = &value case "InBcastOctets": - procNetstat.IpExt.InBcastOctets = value + procNetstat.IpExt.InBcastOctets = &value case "OutBcastOctets": - procNetstat.IpExt.OutBcastOctets = value + procNetstat.IpExt.OutBcastOctets = &value case "InCsumErrors": - procNetstat.IpExt.InCsumErrors = value + procNetstat.IpExt.InCsumErrors = &value case "InNoECTPkts": - procNetstat.IpExt.InNoECTPkts = value + procNetstat.IpExt.InNoECTPkts = &value case "InECT1Pkts": - procNetstat.IpExt.InECT1Pkts = value + procNetstat.IpExt.InECT1Pkts = &value case "InECT0Pkts": - procNetstat.IpExt.InECT0Pkts = value + procNetstat.IpExt.InECT0Pkts = &value case "InCEPkts": - procNetstat.IpExt.InCEPkts = value + procNetstat.IpExt.InCEPkts = &value case "ReasmOverlaps": - procNetstat.IpExt.ReasmOverlaps = value + procNetstat.IpExt.ReasmOverlaps = &value } } } diff --git a/vendor/github.com/prometheus/procfs/proc_ns.go b/vendor/github.com/prometheus/procfs/proc_ns.go index 391b4cbd1..c22666750 100644 --- a/vendor/github.com/prometheus/procfs/proc_ns.go +++ b/vendor/github.com/prometheus/procfs/proc_ns.go @@ -40,7 +40,7 @@ func (p Proc) Namespaces() (Namespaces, error) { names, err := d.Readdirnames(-1) if err != nil { - return nil, fmt.Errorf("failed to read contents of ns dir: %w", err) + return nil, fmt.Errorf("%s: failed to read contents of ns dir: %w", ErrFileRead, err) } ns := make(Namespaces, len(names)) @@ -52,13 +52,13 @@ func (p Proc) Namespaces() (Namespaces, error) { fields := strings.SplitN(target, ":", 2) if len(fields) != 2 { - return nil, fmt.Errorf("failed to parse namespace type and inode from %q", target) + return nil, fmt.Errorf("%w: namespace type and inode from %q", ErrFileParse, target) } typ := fields[0] inode, err := strconv.ParseUint(strings.Trim(fields[1], "[]"), 10, 32) if err != nil { - return nil, fmt.Errorf("failed to parse inode from %q: %w", fields[1], err) + return nil, fmt.Errorf("%s: inode from %q: %w", ErrFileParse, fields[1], err) } ns[name] = Namespace{typ, uint32(inode)} diff --git a/vendor/github.com/prometheus/procfs/proc_psi.go b/vendor/github.com/prometheus/procfs/proc_psi.go index a68fe1529..fe9dbb425 100644 --- a/vendor/github.com/prometheus/procfs/proc_psi.go +++ b/vendor/github.com/prometheus/procfs/proc_psi.go @@ -61,14 +61,14 @@ type PSIStats struct { func (fs FS) PSIStatsForResource(resource string) (PSIStats, error) { data, err := util.ReadFileNoStat(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource))) if err != nil { - return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %q: %w", resource, err) + return PSIStats{}, fmt.Errorf("%s: psi_stats: unavailable for %q: %w", ErrFileRead, resource, err) } - return parsePSIStats(resource, bytes.NewReader(data)) + return parsePSIStats(bytes.NewReader(data)) } // parsePSIStats parses the specified file for pressure stall information. -func parsePSIStats(resource string, r io.Reader) (PSIStats, error) { +func parsePSIStats(r io.Reader) (PSIStats, error) { psiStats := PSIStats{} scanner := bufio.NewScanner(r) diff --git a/vendor/github.com/prometheus/procfs/proc_smaps.go b/vendor/github.com/prometheus/procfs/proc_smaps.go index 0e97d9957..ad8785a40 100644 --- a/vendor/github.com/prometheus/procfs/proc_smaps.go +++ b/vendor/github.com/prometheus/procfs/proc_smaps.go @@ -135,12 +135,12 @@ func (s *ProcSMapsRollup) parseLine(line string) error { } vBytes := vKBytes * 1024 - s.addValue(k, v, vKBytes, vBytes) + s.addValue(k, vBytes) return nil } -func (s *ProcSMapsRollup) addValue(k string, vString string, vUint uint64, vUintBytes uint64) { +func (s *ProcSMapsRollup) addValue(k string, vUintBytes uint64) { switch k { case "Rss": s.Rss += vUintBytes diff --git a/vendor/github.com/prometheus/procfs/proc_snmp.go b/vendor/github.com/prometheus/procfs/proc_snmp.go index ae191896c..b9d2cf642 100644 --- a/vendor/github.com/prometheus/procfs/proc_snmp.go +++ b/vendor/github.com/prometheus/procfs/proc_snmp.go @@ -37,100 +37,100 @@ type ProcSnmp struct { } type Ip struct { // nolint:revive - Forwarding float64 - DefaultTTL float64 - InReceives float64 - InHdrErrors float64 - InAddrErrors float64 - ForwDatagrams float64 - InUnknownProtos float64 - InDiscards float64 - InDelivers float64 - OutRequests float64 - OutDiscards float64 - OutNoRoutes float64 - ReasmTimeout float64 - ReasmReqds float64 - ReasmOKs float64 - ReasmFails float64 - FragOKs float64 - FragFails float64 - FragCreates float64 + Forwarding *float64 + DefaultTTL *float64 + InReceives *float64 + InHdrErrors *float64 + InAddrErrors *float64 + ForwDatagrams *float64 + InUnknownProtos *float64 + InDiscards *float64 + InDelivers *float64 + OutRequests *float64 + OutDiscards *float64 + OutNoRoutes *float64 + ReasmTimeout *float64 + ReasmReqds *float64 + ReasmOKs *float64 + ReasmFails *float64 + FragOKs *float64 + FragFails *float64 + FragCreates *float64 } -type Icmp struct { - InMsgs float64 - InErrors float64 - InCsumErrors float64 - InDestUnreachs float64 - InTimeExcds float64 - InParmProbs float64 - InSrcQuenchs float64 - InRedirects float64 - InEchos float64 - InEchoReps float64 - InTimestamps float64 - InTimestampReps float64 - InAddrMasks float64 - InAddrMaskReps float64 - OutMsgs float64 - OutErrors float64 - OutDestUnreachs float64 - OutTimeExcds float64 - OutParmProbs float64 - OutSrcQuenchs float64 - OutRedirects float64 - OutEchos float64 - OutEchoReps float64 - OutTimestamps float64 - OutTimestampReps float64 - OutAddrMasks float64 - OutAddrMaskReps float64 +type Icmp struct { // nolint:revive + InMsgs *float64 + InErrors *float64 + InCsumErrors *float64 + InDestUnreachs *float64 + InTimeExcds *float64 + InParmProbs *float64 + InSrcQuenchs *float64 + InRedirects *float64 + InEchos *float64 + InEchoReps *float64 + InTimestamps *float64 + InTimestampReps *float64 + InAddrMasks *float64 + InAddrMaskReps *float64 + OutMsgs *float64 + OutErrors *float64 + OutDestUnreachs *float64 + OutTimeExcds *float64 + OutParmProbs *float64 + OutSrcQuenchs *float64 + OutRedirects *float64 + OutEchos *float64 + OutEchoReps *float64 + OutTimestamps *float64 + OutTimestampReps *float64 + OutAddrMasks *float64 + OutAddrMaskReps *float64 } type IcmpMsg struct { - InType3 float64 - OutType3 float64 + InType3 *float64 + OutType3 *float64 } type Tcp struct { // nolint:revive - RtoAlgorithm float64 - RtoMin float64 - RtoMax float64 - MaxConn float64 - ActiveOpens float64 - PassiveOpens float64 - AttemptFails float64 - EstabResets float64 - CurrEstab float64 - InSegs float64 - OutSegs float64 - RetransSegs float64 - InErrs float64 - OutRsts float64 - InCsumErrors float64 + RtoAlgorithm *float64 + RtoMin *float64 + RtoMax *float64 + MaxConn *float64 + ActiveOpens *float64 + PassiveOpens *float64 + AttemptFails *float64 + EstabResets *float64 + CurrEstab *float64 + InSegs *float64 + OutSegs *float64 + RetransSegs *float64 + InErrs *float64 + OutRsts *float64 + InCsumErrors *float64 } type Udp struct { // nolint:revive - InDatagrams float64 - NoPorts float64 - InErrors float64 - OutDatagrams float64 - RcvbufErrors float64 - SndbufErrors float64 - InCsumErrors float64 - IgnoredMulti float64 + InDatagrams *float64 + NoPorts *float64 + InErrors *float64 + OutDatagrams *float64 + RcvbufErrors *float64 + SndbufErrors *float64 + InCsumErrors *float64 + IgnoredMulti *float64 } type UdpLite struct { // nolint:revive - InDatagrams float64 - NoPorts float64 - InErrors float64 - OutDatagrams float64 - RcvbufErrors float64 - SndbufErrors float64 - InCsumErrors float64 - IgnoredMulti float64 + InDatagrams *float64 + NoPorts *float64 + InErrors *float64 + OutDatagrams *float64 + RcvbufErrors *float64 + SndbufErrors *float64 + InCsumErrors *float64 + IgnoredMulti *float64 } func (p Proc) Snmp() (ProcSnmp, error) { @@ -159,8 +159,8 @@ func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) { // Remove trailing :. protocol := strings.TrimSuffix(nameParts[0], ":") if len(nameParts) != len(valueParts) { - return procSnmp, fmt.Errorf("mismatch field count mismatch in %s: %s", - fileName, protocol) + return procSnmp, fmt.Errorf("%w: mismatch field count mismatch in %s: %s", + ErrFileParse, fileName, protocol) } for i := 1; i < len(nameParts); i++ { value, err := strconv.ParseFloat(valueParts[i], 64) @@ -173,178 +173,178 @@ func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) { case "Ip": switch key { case "Forwarding": - procSnmp.Ip.Forwarding = value + procSnmp.Ip.Forwarding = &value case "DefaultTTL": - procSnmp.Ip.DefaultTTL = value + procSnmp.Ip.DefaultTTL = &value case "InReceives": - procSnmp.Ip.InReceives = value + procSnmp.Ip.InReceives = &value case "InHdrErrors": - procSnmp.Ip.InHdrErrors = value + procSnmp.Ip.InHdrErrors = &value case "InAddrErrors": - procSnmp.Ip.InAddrErrors = value + procSnmp.Ip.InAddrErrors = &value case "ForwDatagrams": - procSnmp.Ip.ForwDatagrams = value + procSnmp.Ip.ForwDatagrams = &value case "InUnknownProtos": - procSnmp.Ip.InUnknownProtos = value + procSnmp.Ip.InUnknownProtos = &value case "InDiscards": - procSnmp.Ip.InDiscards = value + procSnmp.Ip.InDiscards = &value case "InDelivers": - procSnmp.Ip.InDelivers = value + procSnmp.Ip.InDelivers = &value case "OutRequests": - procSnmp.Ip.OutRequests = value + procSnmp.Ip.OutRequests = &value case "OutDiscards": - procSnmp.Ip.OutDiscards = value + procSnmp.Ip.OutDiscards = &value case "OutNoRoutes": - procSnmp.Ip.OutNoRoutes = value + procSnmp.Ip.OutNoRoutes = &value case "ReasmTimeout": - procSnmp.Ip.ReasmTimeout = value + procSnmp.Ip.ReasmTimeout = &value case "ReasmReqds": - procSnmp.Ip.ReasmReqds = value + procSnmp.Ip.ReasmReqds = &value case "ReasmOKs": - procSnmp.Ip.ReasmOKs = value + procSnmp.Ip.ReasmOKs = &value case "ReasmFails": - procSnmp.Ip.ReasmFails = value + procSnmp.Ip.ReasmFails = &value case "FragOKs": - procSnmp.Ip.FragOKs = value + procSnmp.Ip.FragOKs = &value case "FragFails": - procSnmp.Ip.FragFails = value + procSnmp.Ip.FragFails = &value case "FragCreates": - procSnmp.Ip.FragCreates = value + procSnmp.Ip.FragCreates = &value } case "Icmp": switch key { case "InMsgs": - procSnmp.Icmp.InMsgs = value + procSnmp.Icmp.InMsgs = &value case "InErrors": - procSnmp.Icmp.InErrors = value + procSnmp.Icmp.InErrors = &value case "InCsumErrors": - procSnmp.Icmp.InCsumErrors = value + procSnmp.Icmp.InCsumErrors = &value case "InDestUnreachs": - procSnmp.Icmp.InDestUnreachs = value + procSnmp.Icmp.InDestUnreachs = &value case "InTimeExcds": - procSnmp.Icmp.InTimeExcds = value + procSnmp.Icmp.InTimeExcds = &value case "InParmProbs": - procSnmp.Icmp.InParmProbs = value + procSnmp.Icmp.InParmProbs = &value case "InSrcQuenchs": - procSnmp.Icmp.InSrcQuenchs = value + procSnmp.Icmp.InSrcQuenchs = &value case "InRedirects": - procSnmp.Icmp.InRedirects = value + procSnmp.Icmp.InRedirects = &value case "InEchos": - procSnmp.Icmp.InEchos = value + procSnmp.Icmp.InEchos = &value case "InEchoReps": - procSnmp.Icmp.InEchoReps = value + procSnmp.Icmp.InEchoReps = &value case "InTimestamps": - procSnmp.Icmp.InTimestamps = value + procSnmp.Icmp.InTimestamps = &value case "InTimestampReps": - procSnmp.Icmp.InTimestampReps = value + procSnmp.Icmp.InTimestampReps = &value case "InAddrMasks": - procSnmp.Icmp.InAddrMasks = value + procSnmp.Icmp.InAddrMasks = &value case "InAddrMaskReps": - procSnmp.Icmp.InAddrMaskReps = value + procSnmp.Icmp.InAddrMaskReps = &value case "OutMsgs": - procSnmp.Icmp.OutMsgs = value + procSnmp.Icmp.OutMsgs = &value case "OutErrors": - procSnmp.Icmp.OutErrors = value + procSnmp.Icmp.OutErrors = &value case "OutDestUnreachs": - procSnmp.Icmp.OutDestUnreachs = value + procSnmp.Icmp.OutDestUnreachs = &value case "OutTimeExcds": - procSnmp.Icmp.OutTimeExcds = value + procSnmp.Icmp.OutTimeExcds = &value case "OutParmProbs": - procSnmp.Icmp.OutParmProbs = value + procSnmp.Icmp.OutParmProbs = &value case "OutSrcQuenchs": - procSnmp.Icmp.OutSrcQuenchs = value + procSnmp.Icmp.OutSrcQuenchs = &value case "OutRedirects": - procSnmp.Icmp.OutRedirects = value + procSnmp.Icmp.OutRedirects = &value case "OutEchos": - procSnmp.Icmp.OutEchos = value + procSnmp.Icmp.OutEchos = &value case "OutEchoReps": - procSnmp.Icmp.OutEchoReps = value + procSnmp.Icmp.OutEchoReps = &value case "OutTimestamps": - procSnmp.Icmp.OutTimestamps = value + procSnmp.Icmp.OutTimestamps = &value case "OutTimestampReps": - procSnmp.Icmp.OutTimestampReps = value + procSnmp.Icmp.OutTimestampReps = &value case "OutAddrMasks": - procSnmp.Icmp.OutAddrMasks = value + procSnmp.Icmp.OutAddrMasks = &value case "OutAddrMaskReps": - procSnmp.Icmp.OutAddrMaskReps = value + procSnmp.Icmp.OutAddrMaskReps = &value } case "IcmpMsg": switch key { case "InType3": - procSnmp.IcmpMsg.InType3 = value + procSnmp.IcmpMsg.InType3 = &value case "OutType3": - procSnmp.IcmpMsg.OutType3 = value + procSnmp.IcmpMsg.OutType3 = &value } case "Tcp": switch key { case "RtoAlgorithm": - procSnmp.Tcp.RtoAlgorithm = value + procSnmp.Tcp.RtoAlgorithm = &value case "RtoMin": - procSnmp.Tcp.RtoMin = value + procSnmp.Tcp.RtoMin = &value case "RtoMax": - procSnmp.Tcp.RtoMax = value + procSnmp.Tcp.RtoMax = &value case "MaxConn": - procSnmp.Tcp.MaxConn = value + procSnmp.Tcp.MaxConn = &value case "ActiveOpens": - procSnmp.Tcp.ActiveOpens = value + procSnmp.Tcp.ActiveOpens = &value case "PassiveOpens": - procSnmp.Tcp.PassiveOpens = value + procSnmp.Tcp.PassiveOpens = &value case "AttemptFails": - procSnmp.Tcp.AttemptFails = value + procSnmp.Tcp.AttemptFails = &value case "EstabResets": - procSnmp.Tcp.EstabResets = value + procSnmp.Tcp.EstabResets = &value case "CurrEstab": - procSnmp.Tcp.CurrEstab = value + procSnmp.Tcp.CurrEstab = &value case "InSegs": - procSnmp.Tcp.InSegs = value + procSnmp.Tcp.InSegs = &value case "OutSegs": - procSnmp.Tcp.OutSegs = value + procSnmp.Tcp.OutSegs = &value case "RetransSegs": - procSnmp.Tcp.RetransSegs = value + procSnmp.Tcp.RetransSegs = &value case "InErrs": - procSnmp.Tcp.InErrs = value + procSnmp.Tcp.InErrs = &value case "OutRsts": - procSnmp.Tcp.OutRsts = value + procSnmp.Tcp.OutRsts = &value case "InCsumErrors": - procSnmp.Tcp.InCsumErrors = value + procSnmp.Tcp.InCsumErrors = &value } case "Udp": switch key { case "InDatagrams": - procSnmp.Udp.InDatagrams = value + procSnmp.Udp.InDatagrams = &value case "NoPorts": - procSnmp.Udp.NoPorts = value + procSnmp.Udp.NoPorts = &value case "InErrors": - procSnmp.Udp.InErrors = value + procSnmp.Udp.InErrors = &value case "OutDatagrams": - procSnmp.Udp.OutDatagrams = value + procSnmp.Udp.OutDatagrams = &value case "RcvbufErrors": - procSnmp.Udp.RcvbufErrors = value + procSnmp.Udp.RcvbufErrors = &value case "SndbufErrors": - procSnmp.Udp.SndbufErrors = value + procSnmp.Udp.SndbufErrors = &value case "InCsumErrors": - procSnmp.Udp.InCsumErrors = value + procSnmp.Udp.InCsumErrors = &value case "IgnoredMulti": - procSnmp.Udp.IgnoredMulti = value + procSnmp.Udp.IgnoredMulti = &value } case "UdpLite": switch key { case "InDatagrams": - procSnmp.UdpLite.InDatagrams = value + procSnmp.UdpLite.InDatagrams = &value case "NoPorts": - procSnmp.UdpLite.NoPorts = value + procSnmp.UdpLite.NoPorts = &value case "InErrors": - procSnmp.UdpLite.InErrors = value + procSnmp.UdpLite.InErrors = &value case "OutDatagrams": - procSnmp.UdpLite.OutDatagrams = value + procSnmp.UdpLite.OutDatagrams = &value case "RcvbufErrors": - procSnmp.UdpLite.RcvbufErrors = value + procSnmp.UdpLite.RcvbufErrors = &value case "SndbufErrors": - procSnmp.UdpLite.SndbufErrors = value + procSnmp.UdpLite.SndbufErrors = &value case "InCsumErrors": - procSnmp.UdpLite.InCsumErrors = value + procSnmp.UdpLite.InCsumErrors = &value case "IgnoredMulti": - procSnmp.UdpLite.IgnoredMulti = value + procSnmp.UdpLite.IgnoredMulti = &value } } } diff --git a/vendor/github.com/prometheus/procfs/proc_snmp6.go b/vendor/github.com/prometheus/procfs/proc_snmp6.go index f611992d5..3059cc6a1 100644 --- a/vendor/github.com/prometheus/procfs/proc_snmp6.go +++ b/vendor/github.com/prometheus/procfs/proc_snmp6.go @@ -36,106 +36,106 @@ type ProcSnmp6 struct { } type Ip6 struct { // nolint:revive - InReceives float64 - InHdrErrors float64 - InTooBigErrors float64 - InNoRoutes float64 - InAddrErrors float64 - InUnknownProtos float64 - InTruncatedPkts float64 - InDiscards float64 - InDelivers float64 - OutForwDatagrams float64 - OutRequests float64 - OutDiscards float64 - OutNoRoutes float64 - ReasmTimeout float64 - ReasmReqds float64 - ReasmOKs float64 - ReasmFails float64 - FragOKs float64 - FragFails float64 - FragCreates float64 - InMcastPkts float64 - OutMcastPkts float64 - InOctets float64 - OutOctets float64 - InMcastOctets float64 - OutMcastOctets float64 - InBcastOctets float64 - OutBcastOctets float64 - InNoECTPkts float64 - InECT1Pkts float64 - InECT0Pkts float64 - InCEPkts float64 + InReceives *float64 + InHdrErrors *float64 + InTooBigErrors *float64 + InNoRoutes *float64 + InAddrErrors *float64 + InUnknownProtos *float64 + InTruncatedPkts *float64 + InDiscards *float64 + InDelivers *float64 + OutForwDatagrams *float64 + OutRequests *float64 + OutDiscards *float64 + OutNoRoutes *float64 + ReasmTimeout *float64 + ReasmReqds *float64 + ReasmOKs *float64 + ReasmFails *float64 + FragOKs *float64 + FragFails *float64 + FragCreates *float64 + InMcastPkts *float64 + OutMcastPkts *float64 + InOctets *float64 + OutOctets *float64 + InMcastOctets *float64 + OutMcastOctets *float64 + InBcastOctets *float64 + OutBcastOctets *float64 + InNoECTPkts *float64 + InECT1Pkts *float64 + InECT0Pkts *float64 + InCEPkts *float64 } type Icmp6 struct { - InMsgs float64 - InErrors float64 - OutMsgs float64 - OutErrors float64 - InCsumErrors float64 - InDestUnreachs float64 - InPktTooBigs float64 - InTimeExcds float64 - InParmProblems float64 - InEchos float64 - InEchoReplies float64 - InGroupMembQueries float64 - InGroupMembResponses float64 - InGroupMembReductions float64 - InRouterSolicits float64 - InRouterAdvertisements float64 - InNeighborSolicits float64 - InNeighborAdvertisements float64 - InRedirects float64 - InMLDv2Reports float64 - OutDestUnreachs float64 - OutPktTooBigs float64 - OutTimeExcds float64 - OutParmProblems float64 - OutEchos float64 - OutEchoReplies float64 - OutGroupMembQueries float64 - OutGroupMembResponses float64 - OutGroupMembReductions float64 - OutRouterSolicits float64 - OutRouterAdvertisements float64 - OutNeighborSolicits float64 - OutNeighborAdvertisements float64 - OutRedirects float64 - OutMLDv2Reports float64 - InType1 float64 - InType134 float64 - InType135 float64 - InType136 float64 - InType143 float64 - OutType133 float64 - OutType135 float64 - OutType136 float64 - OutType143 float64 + InMsgs *float64 + InErrors *float64 + OutMsgs *float64 + OutErrors *float64 + InCsumErrors *float64 + InDestUnreachs *float64 + InPktTooBigs *float64 + InTimeExcds *float64 + InParmProblems *float64 + InEchos *float64 + InEchoReplies *float64 + InGroupMembQueries *float64 + InGroupMembResponses *float64 + InGroupMembReductions *float64 + InRouterSolicits *float64 + InRouterAdvertisements *float64 + InNeighborSolicits *float64 + InNeighborAdvertisements *float64 + InRedirects *float64 + InMLDv2Reports *float64 + OutDestUnreachs *float64 + OutPktTooBigs *float64 + OutTimeExcds *float64 + OutParmProblems *float64 + OutEchos *float64 + OutEchoReplies *float64 + OutGroupMembQueries *float64 + OutGroupMembResponses *float64 + OutGroupMembReductions *float64 + OutRouterSolicits *float64 + OutRouterAdvertisements *float64 + OutNeighborSolicits *float64 + OutNeighborAdvertisements *float64 + OutRedirects *float64 + OutMLDv2Reports *float64 + InType1 *float64 + InType134 *float64 + InType135 *float64 + InType136 *float64 + InType143 *float64 + OutType133 *float64 + OutType135 *float64 + OutType136 *float64 + OutType143 *float64 } type Udp6 struct { // nolint:revive - InDatagrams float64 - NoPorts float64 - InErrors float64 - OutDatagrams float64 - RcvbufErrors float64 - SndbufErrors float64 - InCsumErrors float64 - IgnoredMulti float64 + InDatagrams *float64 + NoPorts *float64 + InErrors *float64 + OutDatagrams *float64 + RcvbufErrors *float64 + SndbufErrors *float64 + InCsumErrors *float64 + IgnoredMulti *float64 } type UdpLite6 struct { // nolint:revive - InDatagrams float64 - NoPorts float64 - InErrors float64 - OutDatagrams float64 - RcvbufErrors float64 - SndbufErrors float64 - InCsumErrors float64 + InDatagrams *float64 + NoPorts *float64 + InErrors *float64 + OutDatagrams *float64 + RcvbufErrors *float64 + SndbufErrors *float64 + InCsumErrors *float64 } func (p Proc) Snmp6() (ProcSnmp6, error) { @@ -182,197 +182,197 @@ func parseSNMP6Stats(r io.Reader) (ProcSnmp6, error) { case "Ip6": switch key { case "InReceives": - procSnmp6.Ip6.InReceives = value + procSnmp6.Ip6.InReceives = &value case "InHdrErrors": - procSnmp6.Ip6.InHdrErrors = value + procSnmp6.Ip6.InHdrErrors = &value case "InTooBigErrors": - procSnmp6.Ip6.InTooBigErrors = value + procSnmp6.Ip6.InTooBigErrors = &value case "InNoRoutes": - procSnmp6.Ip6.InNoRoutes = value + procSnmp6.Ip6.InNoRoutes = &value case "InAddrErrors": - procSnmp6.Ip6.InAddrErrors = value + procSnmp6.Ip6.InAddrErrors = &value case "InUnknownProtos": - procSnmp6.Ip6.InUnknownProtos = value + procSnmp6.Ip6.InUnknownProtos = &value case "InTruncatedPkts": - procSnmp6.Ip6.InTruncatedPkts = value + procSnmp6.Ip6.InTruncatedPkts = &value case "InDiscards": - procSnmp6.Ip6.InDiscards = value + procSnmp6.Ip6.InDiscards = &value case "InDelivers": - procSnmp6.Ip6.InDelivers = value + procSnmp6.Ip6.InDelivers = &value case "OutForwDatagrams": - procSnmp6.Ip6.OutForwDatagrams = value + procSnmp6.Ip6.OutForwDatagrams = &value case "OutRequests": - procSnmp6.Ip6.OutRequests = value + procSnmp6.Ip6.OutRequests = &value case "OutDiscards": - procSnmp6.Ip6.OutDiscards = value + procSnmp6.Ip6.OutDiscards = &value case "OutNoRoutes": - procSnmp6.Ip6.OutNoRoutes = value + procSnmp6.Ip6.OutNoRoutes = &value case "ReasmTimeout": - procSnmp6.Ip6.ReasmTimeout = value + procSnmp6.Ip6.ReasmTimeout = &value case "ReasmReqds": - procSnmp6.Ip6.ReasmReqds = value + procSnmp6.Ip6.ReasmReqds = &value case "ReasmOKs": - procSnmp6.Ip6.ReasmOKs = value + procSnmp6.Ip6.ReasmOKs = &value case "ReasmFails": - procSnmp6.Ip6.ReasmFails = value + procSnmp6.Ip6.ReasmFails = &value case "FragOKs": - procSnmp6.Ip6.FragOKs = value + procSnmp6.Ip6.FragOKs = &value case "FragFails": - procSnmp6.Ip6.FragFails = value + procSnmp6.Ip6.FragFails = &value case "FragCreates": - procSnmp6.Ip6.FragCreates = value + procSnmp6.Ip6.FragCreates = &value case "InMcastPkts": - procSnmp6.Ip6.InMcastPkts = value + procSnmp6.Ip6.InMcastPkts = &value case "OutMcastPkts": - procSnmp6.Ip6.OutMcastPkts = value + procSnmp6.Ip6.OutMcastPkts = &value case "InOctets": - procSnmp6.Ip6.InOctets = value + procSnmp6.Ip6.InOctets = &value case "OutOctets": - procSnmp6.Ip6.OutOctets = value + procSnmp6.Ip6.OutOctets = &value case "InMcastOctets": - procSnmp6.Ip6.InMcastOctets = value + procSnmp6.Ip6.InMcastOctets = &value case "OutMcastOctets": - procSnmp6.Ip6.OutMcastOctets = value + procSnmp6.Ip6.OutMcastOctets = &value case "InBcastOctets": - procSnmp6.Ip6.InBcastOctets = value + procSnmp6.Ip6.InBcastOctets = &value case "OutBcastOctets": - procSnmp6.Ip6.OutBcastOctets = value + procSnmp6.Ip6.OutBcastOctets = &value case "InNoECTPkts": - procSnmp6.Ip6.InNoECTPkts = value + procSnmp6.Ip6.InNoECTPkts = &value case "InECT1Pkts": - procSnmp6.Ip6.InECT1Pkts = value + procSnmp6.Ip6.InECT1Pkts = &value case "InECT0Pkts": - procSnmp6.Ip6.InECT0Pkts = value + procSnmp6.Ip6.InECT0Pkts = &value case "InCEPkts": - procSnmp6.Ip6.InCEPkts = value + procSnmp6.Ip6.InCEPkts = &value } case "Icmp6": switch key { case "InMsgs": - procSnmp6.Icmp6.InMsgs = value + procSnmp6.Icmp6.InMsgs = &value case "InErrors": - procSnmp6.Icmp6.InErrors = value + procSnmp6.Icmp6.InErrors = &value case "OutMsgs": - procSnmp6.Icmp6.OutMsgs = value + procSnmp6.Icmp6.OutMsgs = &value case "OutErrors": - procSnmp6.Icmp6.OutErrors = value + procSnmp6.Icmp6.OutErrors = &value case "InCsumErrors": - procSnmp6.Icmp6.InCsumErrors = value + procSnmp6.Icmp6.InCsumErrors = &value case "InDestUnreachs": - procSnmp6.Icmp6.InDestUnreachs = value + procSnmp6.Icmp6.InDestUnreachs = &value case "InPktTooBigs": - procSnmp6.Icmp6.InPktTooBigs = value + procSnmp6.Icmp6.InPktTooBigs = &value case "InTimeExcds": - procSnmp6.Icmp6.InTimeExcds = value + procSnmp6.Icmp6.InTimeExcds = &value case "InParmProblems": - procSnmp6.Icmp6.InParmProblems = value + procSnmp6.Icmp6.InParmProblems = &value case "InEchos": - procSnmp6.Icmp6.InEchos = value + procSnmp6.Icmp6.InEchos = &value case "InEchoReplies": - procSnmp6.Icmp6.InEchoReplies = value + procSnmp6.Icmp6.InEchoReplies = &value case "InGroupMembQueries": - procSnmp6.Icmp6.InGroupMembQueries = value + procSnmp6.Icmp6.InGroupMembQueries = &value case "InGroupMembResponses": - procSnmp6.Icmp6.InGroupMembResponses = value + procSnmp6.Icmp6.InGroupMembResponses = &value case "InGroupMembReductions": - procSnmp6.Icmp6.InGroupMembReductions = value + procSnmp6.Icmp6.InGroupMembReductions = &value case "InRouterSolicits": - procSnmp6.Icmp6.InRouterSolicits = value + procSnmp6.Icmp6.InRouterSolicits = &value case "InRouterAdvertisements": - procSnmp6.Icmp6.InRouterAdvertisements = value + procSnmp6.Icmp6.InRouterAdvertisements = &value case "InNeighborSolicits": - procSnmp6.Icmp6.InNeighborSolicits = value + procSnmp6.Icmp6.InNeighborSolicits = &value case "InNeighborAdvertisements": - procSnmp6.Icmp6.InNeighborAdvertisements = value + procSnmp6.Icmp6.InNeighborAdvertisements = &value case "InRedirects": - procSnmp6.Icmp6.InRedirects = value + procSnmp6.Icmp6.InRedirects = &value case "InMLDv2Reports": - procSnmp6.Icmp6.InMLDv2Reports = value + procSnmp6.Icmp6.InMLDv2Reports = &value case "OutDestUnreachs": - procSnmp6.Icmp6.OutDestUnreachs = value + procSnmp6.Icmp6.OutDestUnreachs = &value case "OutPktTooBigs": - procSnmp6.Icmp6.OutPktTooBigs = value + procSnmp6.Icmp6.OutPktTooBigs = &value case "OutTimeExcds": - procSnmp6.Icmp6.OutTimeExcds = value + procSnmp6.Icmp6.OutTimeExcds = &value case "OutParmProblems": - procSnmp6.Icmp6.OutParmProblems = value + procSnmp6.Icmp6.OutParmProblems = &value case "OutEchos": - procSnmp6.Icmp6.OutEchos = value + procSnmp6.Icmp6.OutEchos = &value case "OutEchoReplies": - procSnmp6.Icmp6.OutEchoReplies = value + procSnmp6.Icmp6.OutEchoReplies = &value case "OutGroupMembQueries": - procSnmp6.Icmp6.OutGroupMembQueries = value + procSnmp6.Icmp6.OutGroupMembQueries = &value case "OutGroupMembResponses": - procSnmp6.Icmp6.OutGroupMembResponses = value + procSnmp6.Icmp6.OutGroupMembResponses = &value case "OutGroupMembReductions": - procSnmp6.Icmp6.OutGroupMembReductions = value + procSnmp6.Icmp6.OutGroupMembReductions = &value case "OutRouterSolicits": - procSnmp6.Icmp6.OutRouterSolicits = value + procSnmp6.Icmp6.OutRouterSolicits = &value case "OutRouterAdvertisements": - procSnmp6.Icmp6.OutRouterAdvertisements = value + procSnmp6.Icmp6.OutRouterAdvertisements = &value case "OutNeighborSolicits": - procSnmp6.Icmp6.OutNeighborSolicits = value + procSnmp6.Icmp6.OutNeighborSolicits = &value case "OutNeighborAdvertisements": - procSnmp6.Icmp6.OutNeighborAdvertisements = value + procSnmp6.Icmp6.OutNeighborAdvertisements = &value case "OutRedirects": - procSnmp6.Icmp6.OutRedirects = value + procSnmp6.Icmp6.OutRedirects = &value case "OutMLDv2Reports": - procSnmp6.Icmp6.OutMLDv2Reports = value + procSnmp6.Icmp6.OutMLDv2Reports = &value case "InType1": - procSnmp6.Icmp6.InType1 = value + procSnmp6.Icmp6.InType1 = &value case "InType134": - procSnmp6.Icmp6.InType134 = value + procSnmp6.Icmp6.InType134 = &value case "InType135": - procSnmp6.Icmp6.InType135 = value + procSnmp6.Icmp6.InType135 = &value case "InType136": - procSnmp6.Icmp6.InType136 = value + procSnmp6.Icmp6.InType136 = &value case "InType143": - procSnmp6.Icmp6.InType143 = value + procSnmp6.Icmp6.InType143 = &value case "OutType133": - procSnmp6.Icmp6.OutType133 = value + procSnmp6.Icmp6.OutType133 = &value case "OutType135": - procSnmp6.Icmp6.OutType135 = value + procSnmp6.Icmp6.OutType135 = &value case "OutType136": - procSnmp6.Icmp6.OutType136 = value + procSnmp6.Icmp6.OutType136 = &value case "OutType143": - procSnmp6.Icmp6.OutType143 = value + procSnmp6.Icmp6.OutType143 = &value } case "Udp6": switch key { case "InDatagrams": - procSnmp6.Udp6.InDatagrams = value + procSnmp6.Udp6.InDatagrams = &value case "NoPorts": - procSnmp6.Udp6.NoPorts = value + procSnmp6.Udp6.NoPorts = &value case "InErrors": - procSnmp6.Udp6.InErrors = value + procSnmp6.Udp6.InErrors = &value case "OutDatagrams": - procSnmp6.Udp6.OutDatagrams = value + procSnmp6.Udp6.OutDatagrams = &value case "RcvbufErrors": - procSnmp6.Udp6.RcvbufErrors = value + procSnmp6.Udp6.RcvbufErrors = &value case "SndbufErrors": - procSnmp6.Udp6.SndbufErrors = value + procSnmp6.Udp6.SndbufErrors = &value case "InCsumErrors": - procSnmp6.Udp6.InCsumErrors = value + procSnmp6.Udp6.InCsumErrors = &value case "IgnoredMulti": - procSnmp6.Udp6.IgnoredMulti = value + procSnmp6.Udp6.IgnoredMulti = &value } case "UdpLite6": switch key { case "InDatagrams": - procSnmp6.UdpLite6.InDatagrams = value + procSnmp6.UdpLite6.InDatagrams = &value case "NoPorts": - procSnmp6.UdpLite6.NoPorts = value + procSnmp6.UdpLite6.NoPorts = &value case "InErrors": - procSnmp6.UdpLite6.InErrors = value + procSnmp6.UdpLite6.InErrors = &value case "OutDatagrams": - procSnmp6.UdpLite6.OutDatagrams = value + procSnmp6.UdpLite6.OutDatagrams = &value case "RcvbufErrors": - procSnmp6.UdpLite6.RcvbufErrors = value + procSnmp6.UdpLite6.RcvbufErrors = &value case "SndbufErrors": - procSnmp6.UdpLite6.SndbufErrors = value + procSnmp6.UdpLite6.SndbufErrors = &value case "InCsumErrors": - procSnmp6.UdpLite6.InCsumErrors = value + procSnmp6.UdpLite6.InCsumErrors = &value } } } diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go index 06c556ef9..923e55005 100644 --- a/vendor/github.com/prometheus/procfs/proc_stat.go +++ b/vendor/github.com/prometheus/procfs/proc_stat.go @@ -18,7 +18,6 @@ import ( "fmt" "os" - "github.com/prometheus/procfs/internal/fs" "github.com/prometheus/procfs/internal/util" ) @@ -102,6 +101,8 @@ type ProcStat struct { RSS int // Soft limit in bytes on the rss of the process. RSSLimit uint64 + // CPU number last executed on. + Processor uint // Real-time scheduling priority, a number in the range 1 to 99 for processes // scheduled under a real-time policy, or 0, for non-real-time processes. RTPriority uint @@ -110,7 +111,7 @@ type ProcStat struct { // Aggregated block I/O delays, measured in clock ticks (centiseconds). DelayAcctBlkIOTicks uint64 - proc fs.FS + proc FS } // NewStat returns the current status information of the process. @@ -137,7 +138,7 @@ func (p Proc) Stat() (ProcStat, error) { ) if l < 0 || r < 0 { - return ProcStat{}, fmt.Errorf("unexpected format, couldn't extract comm %q", data) + return ProcStat{}, fmt.Errorf("%w: unexpected format, couldn't extract comm %q", ErrFileParse, data) } s.Comm = string(data[l+1 : r]) @@ -184,7 +185,7 @@ func (p Proc) Stat() (ProcStat, error) { &ignoreUint64, &ignoreUint64, &ignoreInt64, - &ignoreInt64, + &s.Processor, &s.RTPriority, &s.Policy, &s.DelayAcctBlkIOTicks, @@ -208,8 +209,7 @@ func (s ProcStat) ResidentMemory() int { // StartTime returns the unix timestamp of the process in seconds. func (s ProcStat) StartTime() (float64, error) { - fs := FS{proc: s.proc} - stat, err := fs.Stat() + stat, err := s.proc.Stat() if err != nil { return 0, err } diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go index 594022ded..46307f572 100644 --- a/vendor/github.com/prometheus/procfs/proc_status.go +++ b/vendor/github.com/prometheus/procfs/proc_status.go @@ -15,6 +15,7 @@ package procfs import ( "bytes" + "sort" "strconv" "strings" @@ -22,7 +23,7 @@ import ( ) // ProcStatus provides status information about the process, -// read from /proc/[pid]/stat. +// read from /proc/[pid]/status. type ProcStatus struct { // The process ID. PID int @@ -31,6 +32,8 @@ type ProcStatus struct { // Thread group ID. TGID int + // List of Pid namespace. + NSpids []uint64 // Peak virtual memory size. VmPeak uint64 // nolint:revive @@ -76,6 +79,9 @@ type ProcStatus struct { UIDs [4]string // GIDs of the process (Real, effective, saved set, and filesystem GIDs) GIDs [4]string + + // CpusAllowedList: List of cpu cores processes are allowed to run on. + CpusAllowedList []uint64 } // NewStatus returns the current status information of the process. @@ -96,10 +102,10 @@ func (p Proc) NewStatus() (ProcStatus, error) { kv := strings.SplitN(line, ":", 2) // removes spaces - k := string(strings.TrimSpace(kv[0])) - v := string(strings.TrimSpace(kv[1])) + k := strings.TrimSpace(kv[0]) + v := strings.TrimSpace(kv[1]) // removes "kB" - v = string(bytes.Trim([]byte(v), " kB")) + v = strings.TrimSuffix(v, " kB") // value to int when possible // we can skip error check here, 'cause vKBytes is not used when value is a string @@ -123,6 +129,8 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt copy(s.UIDs[:], strings.Split(vString, "\t")) case "Gid": copy(s.GIDs[:], strings.Split(vString, "\t")) + case "NSpid": + s.NSpids = calcNSPidsList(vString) case "VmPeak": s.VmPeak = vUintBytes case "VmSize": @@ -161,10 +169,53 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt s.VoluntaryCtxtSwitches = vUint case "nonvoluntary_ctxt_switches": s.NonVoluntaryCtxtSwitches = vUint + case "Cpus_allowed_list": + s.CpusAllowedList = calcCpusAllowedList(vString) } + } // TotalCtxtSwitches returns the total context switch. func (s ProcStatus) TotalCtxtSwitches() uint64 { return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches } + +func calcCpusAllowedList(cpuString string) []uint64 { + s := strings.Split(cpuString, ",") + + var g []uint64 + + for _, cpu := range s { + // parse cpu ranges, example: 1-3=[1,2,3] + if l := strings.Split(strings.TrimSpace(cpu), "-"); len(l) > 1 { + startCPU, _ := strconv.ParseUint(l[0], 10, 64) + endCPU, _ := strconv.ParseUint(l[1], 10, 64) + + for i := startCPU; i <= endCPU; i++ { + g = append(g, i) + } + } else if len(l) == 1 { + cpu, _ := strconv.ParseUint(l[0], 10, 64) + g = append(g, cpu) + } + + } + + sort.Slice(g, func(i, j int) bool { return g[i] < g[j] }) + return g +} + +func calcNSPidsList(nspidsString string) []uint64 { + s := strings.Split(nspidsString, " ") + var nspids []uint64 + + for _, nspid := range s { + nspid, _ := strconv.ParseUint(nspid, 10, 64) + if nspid == 0 { + continue + } + nspids = append(nspids, nspid) + } + + return nspids +} diff --git a/vendor/github.com/prometheus/procfs/proc_sys.go b/vendor/github.com/prometheus/procfs/proc_sys.go index d46533ebf..12c5bf05b 100644 --- a/vendor/github.com/prometheus/procfs/proc_sys.go +++ b/vendor/github.com/prometheus/procfs/proc_sys.go @@ -44,7 +44,7 @@ func (fs FS) SysctlInts(sysctl string) ([]int, error) { vp := util.NewValueParser(f) values[i] = vp.Int() if err := vp.Err(); err != nil { - return nil, fmt.Errorf("field %d in sysctl %s is not a valid int: %w", i, sysctl, err) + return nil, fmt.Errorf("%s: field %d in sysctl %s is not a valid int: %w", ErrFileParse, i, sysctl, err) } } return values, nil diff --git a/vendor/github.com/prometheus/procfs/slab.go b/vendor/github.com/prometheus/procfs/slab.go index bc9aaf5c2..8611c9017 100644 --- a/vendor/github.com/prometheus/procfs/slab.go +++ b/vendor/github.com/prometheus/procfs/slab.go @@ -68,7 +68,7 @@ func parseV21SlabEntry(line string) (*Slab, error) { l := slabSpace.ReplaceAllString(line, " ") s := strings.Split(l, " ") if len(s) != 16 { - return nil, fmt.Errorf("unable to parse: %q", line) + return nil, fmt.Errorf("%w: unable to parse: %q", ErrFileParse, line) } var err error i := &Slab{Name: s[0]} diff --git a/vendor/github.com/prometheus/procfs/softirqs.go b/vendor/github.com/prometheus/procfs/softirqs.go index 559129cbc..b8fad677d 100644 --- a/vendor/github.com/prometheus/procfs/softirqs.go +++ b/vendor/github.com/prometheus/procfs/softirqs.go @@ -57,7 +57,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { ) if !scanner.Scan() { - return Softirqs{}, fmt.Errorf("softirqs empty") + return Softirqs{}, fmt.Errorf("%w: softirqs empty", ErrFileRead) } for scanner.Scan() { @@ -74,7 +74,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.Hi = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.Hi[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (HI%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (HI%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "TIMER:": @@ -82,7 +82,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.Timer = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.Timer[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (TIMER%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (TIMER%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "NET_TX:": @@ -90,7 +90,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.NetTx = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.NetTx[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (NET_TX%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (NET_TX%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "NET_RX:": @@ -98,7 +98,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.NetRx = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.NetRx[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (NET_RX%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (NET_RX%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "BLOCK:": @@ -106,7 +106,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.Block = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.Block[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (BLOCK%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (BLOCK%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "IRQ_POLL:": @@ -114,7 +114,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.IRQPoll = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.IRQPoll[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (IRQ_POLL%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (IRQ_POLL%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "TASKLET:": @@ -122,7 +122,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.Tasklet = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.Tasklet[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (TASKLET%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (TASKLET%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "SCHED:": @@ -130,7 +130,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.Sched = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.Sched[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (SCHED%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (SCHED%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "HRTIMER:": @@ -138,7 +138,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.HRTimer = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.HRTimer[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (HRTIMER%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (HRTIMER%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "RCU:": @@ -146,14 +146,14 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) { softirqs.RCU = make([]uint64, len(perCPU)) for i, count := range perCPU { if softirqs.RCU[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse %q (RCU%d): %w", count, i, err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (RCU%d): %w", ErrFileParse, count, i, err) } } } } if err := scanner.Err(); err != nil { - return Softirqs{}, fmt.Errorf("couldn't parse softirqs: %w", err) + return Softirqs{}, fmt.Errorf("%s: couldn't parse softirqs: %w", ErrFileParse, err) } return softirqs, scanner.Err() diff --git a/vendor/github.com/prometheus/procfs/stat.go b/vendor/github.com/prometheus/procfs/stat.go index 33f97caa0..34fc3ee21 100644 --- a/vendor/github.com/prometheus/procfs/stat.go +++ b/vendor/github.com/prometheus/procfs/stat.go @@ -62,7 +62,7 @@ type Stat struct { // Summed up cpu statistics. CPUTotal CPUStat // Per-CPU statistics. - CPU []CPUStat + CPU map[int64]CPUStat // Number of times interrupts were handled, which contains numbered and unnumbered IRQs. IRQTotal uint64 // Number of times a numbered IRQ was triggered. @@ -93,10 +93,10 @@ func parseCPUStat(line string) (CPUStat, int64, error) { &cpuStat.Guest, &cpuStat.GuestNice) if err != nil && err != io.EOF { - return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu): %w", line, err) + return CPUStat{}, -1, fmt.Errorf("%s: couldn't parse %q (cpu): %w", ErrFileParse, line, err) } if count == 0 { - return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu): 0 elements parsed", line) + return CPUStat{}, -1, fmt.Errorf("%w: couldn't parse %q (cpu): 0 elements parsed", ErrFileParse, line) } cpuStat.User /= userHZ @@ -116,7 +116,7 @@ func parseCPUStat(line string) (CPUStat, int64, error) { cpuID, err := strconv.ParseInt(cpu[3:], 10, 64) if err != nil { - return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu/cpuid): %w", line, err) + return CPUStat{}, -1, fmt.Errorf("%s: couldn't parse %q (cpu/cpuid): %w", ErrFileParse, line, err) } return cpuStat, cpuID, nil @@ -136,7 +136,7 @@ func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) { &softIRQStat.Hrtimer, &softIRQStat.Rcu) if err != nil { - return SoftIRQStat{}, 0, fmt.Errorf("couldn't parse %q (softirq): %w", line, err) + return SoftIRQStat{}, 0, fmt.Errorf("%s: couldn't parse %q (softirq): %w", ErrFileParse, line, err) } return softIRQStat, total, nil @@ -170,10 +170,27 @@ func (fs FS) Stat() (Stat, error) { if err != nil { return Stat{}, err } + procStat, err := parseStat(bytes.NewReader(data), fileName) + if err != nil { + return Stat{}, err + } + return procStat, nil +} - stat := Stat{} +// parseStat parses the metrics from /proc/[pid]/stat. +func parseStat(r io.Reader, fileName string) (Stat, error) { + var ( + scanner = bufio.NewScanner(r) + stat = Stat{ + CPU: make(map[int64]CPUStat), + } + err error + ) + + // Increase default scanner buffer to handle very long `intr` lines. + buf := make([]byte, 0, 8*1024) + scanner.Buffer(buf, 1024*1024) - scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { line := scanner.Text() parts := strings.Fields(scanner.Text()) @@ -184,34 +201,34 @@ func (fs FS) Stat() (Stat, error) { switch { case parts[0] == "btime": if stat.BootTime, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (btime): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (btime): %w", ErrFileParse, parts[1], err) } case parts[0] == "intr": if stat.IRQTotal, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (intr): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (intr): %w", ErrFileParse, parts[1], err) } numberedIRQs := parts[2:] stat.IRQ = make([]uint64, len(numberedIRQs)) for i, count := range numberedIRQs { if stat.IRQ[i], err = strconv.ParseUint(count, 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (intr%d): %w", count, i, err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (intr%d): %w", ErrFileParse, count, i, err) } } case parts[0] == "ctxt": if stat.ContextSwitches, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (ctxt): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (ctxt): %w", ErrFileParse, parts[1], err) } case parts[0] == "processes": if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (processes): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (processes): %w", ErrFileParse, parts[1], err) } case parts[0] == "procs_running": if stat.ProcessesRunning, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (procs_running): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (procs_running): %w", ErrFileParse, parts[1], err) } case parts[0] == "procs_blocked": if stat.ProcessesBlocked, err = strconv.ParseUint(parts[1], 10, 64); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q (procs_blocked): %w", parts[1], err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q (procs_blocked): %w", ErrFileParse, parts[1], err) } case parts[0] == "softirq": softIRQStats, total, err := parseSoftIRQStat(line) @@ -228,16 +245,13 @@ func (fs FS) Stat() (Stat, error) { if cpuID == -1 { stat.CPUTotal = cpuStat } else { - for int64(len(stat.CPU)) <= cpuID { - stat.CPU = append(stat.CPU, CPUStat{}) - } stat.CPU[cpuID] = cpuStat } } } if err := scanner.Err(); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %q: %w", fileName, err) + return Stat{}, fmt.Errorf("%s: couldn't parse %q: %w", ErrFileParse, fileName, err) } return stat, nil diff --git a/vendor/github.com/prometheus/procfs/swaps.go b/vendor/github.com/prometheus/procfs/swaps.go index 15edc2212..fa00f555d 100644 --- a/vendor/github.com/prometheus/procfs/swaps.go +++ b/vendor/github.com/prometheus/procfs/swaps.go @@ -64,7 +64,7 @@ func parseSwapString(swapString string) (*Swap, error) { swapFields := strings.Fields(swapString) swapLength := len(swapFields) if swapLength < 5 { - return nil, fmt.Errorf("too few fields in swap string: %s", swapString) + return nil, fmt.Errorf("%w: too few fields in swap string: %s", ErrFileParse, swapString) } swap := &Swap{ @@ -74,15 +74,15 @@ func parseSwapString(swapString string) (*Swap, error) { swap.Size, err = strconv.Atoi(swapFields[2]) if err != nil { - return nil, fmt.Errorf("invalid swap size: %s", swapFields[2]) + return nil, fmt.Errorf("%s: invalid swap size: %s: %w", ErrFileParse, swapFields[2], err) } swap.Used, err = strconv.Atoi(swapFields[3]) if err != nil { - return nil, fmt.Errorf("invalid swap used: %s", swapFields[3]) + return nil, fmt.Errorf("%s: invalid swap used: %s: %w", ErrFileParse, swapFields[3], err) } swap.Priority, err = strconv.Atoi(swapFields[4]) if err != nil { - return nil, fmt.Errorf("invalid swap priority: %s", swapFields[4]) + return nil, fmt.Errorf("%s: invalid swap priority: %s: %w", ErrFileParse, swapFields[4], err) } return swap, nil diff --git a/vendor/github.com/prometheus/procfs/thread.go b/vendor/github.com/prometheus/procfs/thread.go new file mode 100644 index 000000000..df2215ece --- /dev/null +++ b/vendor/github.com/prometheus/procfs/thread.go @@ -0,0 +1,80 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "fmt" + "os" + "strconv" + + fsi "github.com/prometheus/procfs/internal/fs" +) + +// Provide access to /proc/PID/task/TID files, for thread specific values. Since +// such files have the same structure as /proc/PID/ ones, the data structures +// and the parsers for the latter may be reused. + +// AllThreads returns a list of all currently available threads under /proc/PID. +func AllThreads(pid int) (Procs, error) { + fs, err := NewFS(DefaultMountPoint) + if err != nil { + return Procs{}, err + } + return fs.AllThreads(pid) +} + +// AllThreads returns a list of all currently available threads for PID. +func (fs FS) AllThreads(pid int) (Procs, error) { + taskPath := fs.proc.Path(strconv.Itoa(pid), "task") + d, err := os.Open(taskPath) + if err != nil { + return Procs{}, err + } + defer d.Close() + + names, err := d.Readdirnames(-1) + if err != nil { + return Procs{}, fmt.Errorf("%s: could not read %q: %w", ErrFileRead, d.Name(), err) + } + + t := Procs{} + for _, n := range names { + tid, err := strconv.ParseInt(n, 10, 64) + if err != nil { + continue + } + + t = append(t, Proc{PID: int(tid), fs: FS{fsi.FS(taskPath), fs.isReal}}) + } + + return t, nil +} + +// Thread returns a process for a given PID, TID. +func (fs FS) Thread(pid, tid int) (Proc, error) { + taskPath := fs.proc.Path(strconv.Itoa(pid), "task") + if _, err := os.Stat(taskPath); err != nil { + return Proc{}, err + } + return Proc{PID: tid, fs: FS{fsi.FS(taskPath), fs.isReal}}, nil +} + +// Thread returns a process for a given TID of Proc. +func (proc Proc) Thread(tid int) (Proc, error) { + tfs := FS{fsi.FS(proc.path("task")), proc.fs.isReal} + if _, err := os.Stat(tfs.proc.Path(strconv.Itoa(tid))); err != nil { + return Proc{}, err + } + return Proc{PID: tid, fs: tfs}, nil +} diff --git a/vendor/github.com/prometheus/procfs/vm.go b/vendor/github.com/prometheus/procfs/vm.go index 20ceb77e2..51c49d89e 100644 --- a/vendor/github.com/prometheus/procfs/vm.go +++ b/vendor/github.com/prometheus/procfs/vm.go @@ -26,7 +26,9 @@ import ( ) // The VM interface is described at -// https://www.kernel.org/doc/Documentation/sysctl/vm.txt +// +// https://www.kernel.org/doc/Documentation/sysctl/vm.txt +// // Each setting is exposed as a single file. // Each file contains one line with a single numerical value, except lowmem_reserve_ratio which holds an array // and numa_zonelist_order (deprecated) which is a string. @@ -84,7 +86,7 @@ func (fs FS) VM() (*VM, error) { return nil, err } if !file.Mode().IsDir() { - return nil, fmt.Errorf("%s is not a directory", path) + return nil, fmt.Errorf("%w: %s is not a directory", ErrFileRead, path) } files, err := os.ReadDir(path) diff --git a/vendor/github.com/prometheus/procfs/zoneinfo.go b/vendor/github.com/prometheus/procfs/zoneinfo.go index c745a4c04..ce5fefa5b 100644 --- a/vendor/github.com/prometheus/procfs/zoneinfo.go +++ b/vendor/github.com/prometheus/procfs/zoneinfo.go @@ -75,11 +75,11 @@ var nodeZoneRE = regexp.MustCompile(`(\d+), zone\s+(\w+)`) func (fs FS) Zoneinfo() ([]Zoneinfo, error) { data, err := os.ReadFile(fs.proc.Path("zoneinfo")) if err != nil { - return nil, fmt.Errorf("error reading zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err) + return nil, fmt.Errorf("%s: error reading zoneinfo %q: %w", ErrFileRead, fs.proc.Path("zoneinfo"), err) } zoneinfo, err := parseZoneinfo(data) if err != nil { - return nil, fmt.Errorf("error parsing zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err) + return nil, fmt.Errorf("%s: error parsing zoneinfo %q: %w", ErrFileParse, fs.proc.Path("zoneinfo"), err) } return zoneinfo, nil } diff --git a/vendor/github.com/quic-go/quic-go/.gitignore b/vendor/github.com/quic-go/quic-go/.gitignore new file mode 100644 index 000000000..3cc06f240 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/.gitignore @@ -0,0 +1,17 @@ +debug +debug.test +main +mockgen_tmp.go +*.qtr +*.qlog +*.txt +race.[0-9]* + +fuzzing/*/*.zip +fuzzing/*/coverprofile +fuzzing/*/crashers +fuzzing/*/sonarprofile +fuzzing/*/suppressions +fuzzing/*/corpus/ + +gomock_reflect_*/ diff --git a/vendor/github.com/quic-go/quic-go/.golangci.yml b/vendor/github.com/quic-go/quic-go/.golangci.yml new file mode 100644 index 000000000..469d54cfb --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/.golangci.yml @@ -0,0 +1,33 @@ +run: + skip-files: + - internal/handshake/cipher_suite.go +linters-settings: + misspell: + ignore-words: + - ect + +linters: + disable-all: true + enable: + - asciicheck + - exhaustive + - exportloopref + - goimports + - gofmt # redundant, since gofmt *should* be a no-op after gofumpt + - gofumpt + - gosimple + - ineffassign + - misspell + - prealloc + - staticcheck + - stylecheck + - unconvert + - unparam + - unused + - vet + +issues: + exclude-rules: + - path: internal/qtls + linters: + - depguard diff --git a/vendor/github.com/quic-go/quic-go/Changelog.md b/vendor/github.com/quic-go/quic-go/Changelog.md new file mode 100644 index 000000000..82df5fb24 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/Changelog.md @@ -0,0 +1,109 @@ +# Changelog + +## v0.22.0 (2021-07-25) + +- Use `ReadBatch` to read multiple UDP packets from the socket with a single syscall +- Add a config option (`Config.DisableVersionNegotiationPackets`) to disable sending of Version Negotiation packets +- Drop support for QUIC draft versions 32 and 34 +- Remove the `RetireBugBackwardsCompatibilityMode`, which was intended to mitigate a bug when retiring connection IDs in quic-go in v0.17.2 and ealier + +## v0.21.2 (2021-07-15) + +- Update qtls (for Go 1.15, 1.16 and 1.17rc1) to include the fix for the crypto/tls panic (see https://groups.google.com/g/golang-dev/c/5LJ2V7rd-Ag/m/YGLHVBZ6AAAJ for details) + +## v0.21.0 (2021-06-01) + +- quic-go now supports RFC 9000! + +## v0.20.0 (2021-03-19) + +- Remove the `quic.Config.HandshakeTimeout`. Introduce a `quic.Config.HandshakeIdleTimeout`. + +## v0.17.1 (2020-06-20) + +- Supports QUIC WG draft-29. +- Improve bundling of ACK frames (#2543). + +## v0.16.0 (2020-05-31) + +- Supports QUIC WG draft-28. + +## v0.15.0 (2020-03-01) + +- Supports QUIC WG draft-27. +- Add support for 0-RTT. +- Remove `Session.Close()`. Applications need to pass an application error code to the transport using `Session.CloseWithError()`. +- Make the TLS Cipher Suites configurable (via `tls.Config.CipherSuites`). + +## v0.14.0 (2019-12-04) + +- Supports QUIC WG draft-24. + +## v0.13.0 (2019-11-05) + +- Supports QUIC WG draft-23. +- Add an `EarlyListener` that allows sending of 0.5-RTT data. +- Add a `TokenStore` to store address validation tokens. +- Issue and use new connection IDs during a connection. + +## v0.12.0 (2019-08-05) + +- Implement HTTP/3. +- Rename `quic.Cookie` to `quic.Token` and `quic.Config.AcceptCookie` to `quic.Config.AcceptToken`. +- Distinguish between Retry tokens and tokens sent in NEW_TOKEN frames. +- Enforce application protocol negotiation (via `tls.Config.NextProtos`). +- Use a varint for error codes. +- Add support for [quic-trace](https://github.com/google/quic-trace). +- Add a context to `Listener.Accept`, `Session.Accept{Uni}Stream` and `Session.Open{Uni}StreamSync`. +- Implement TLS key updates. + +## v0.11.0 (2019-04-05) + +- Drop support for gQUIC. For qQUIC support, please switch to the *gquic* branch. +- Implement QUIC WG draft-19. +- Use [qtls](https://github.com/marten-seemann/qtls) for TLS 1.3. +- Return a `tls.ConnectionState` from `quic.Session.ConnectionState()`. +- Remove the error return values from `quic.Stream.CancelRead()` and `quic.Stream.CancelWrite()` + +## v0.10.0 (2018-08-28) + +- Add support for QUIC 44, drop support for QUIC 42. + +## v0.9.0 (2018-08-15) + +- Add a `quic.Config` option for the length of the connection ID (for IETF QUIC). +- Split Session.Close into one method for regular closing and one for closing with an error. + +## v0.8.0 (2018-06-26) + +- Add support for unidirectional streams (for IETF QUIC). +- Add a `quic.Config` option for the maximum number of incoming streams. +- Add support for QUIC 42 and 43. +- Add dial functions that use a context. +- Multiplex clients on a net.PacketConn, when using Dial(conn). + +## v0.7.0 (2018-02-03) + +- The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored. +- Remove `DialNonFWSecure` and `DialAddrNonFWSecure`. +- Expose the `ConnectionState` in the `Session` (experimental API). +- Implement packet pacing. + +## v0.6.0 (2017-12-12) + +- Add support for QUIC 39, drop support for QUIC 35 - 37 +- Added `quic.Config` options for maximal flow control windows +- Add a `quic.Config` option for QUIC versions +- Add a `quic.Config` option to request omission of the connection ID from a server +- Add a `quic.Config` option to configure the source address validation +- Add a `quic.Config` option to configure the handshake timeout +- Add a `quic.Config` option to configure the idle timeout +- Add a `quic.Config` option to configure keep-alive +- Rename the STK to Cookie +- Implement `net.Conn`-style deadlines for streams +- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/quic-go/quic-go) for details. +- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/quic-go/quic-go/wiki/Logging) for more details. +- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper` +- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn` +- Drop support for Go 1.7 and 1.8. +- Various bugfixes diff --git a/vendor/github.com/quic-go/quic-go/LICENSE b/vendor/github.com/quic-go/quic-go/LICENSE new file mode 100644 index 000000000..51378befb --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 the quic-go authors & Google, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/quic-go/quic-go/README.md b/vendor/github.com/quic-go/quic-go/README.md new file mode 100644 index 000000000..faba82f3d --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/README.md @@ -0,0 +1,249 @@ +# A QUIC implementation in pure Go + + + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go)](https://pkg.go.dev/github.com/quic-go/quic-go) +[![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/quic-go/) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) + +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)). + +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)) +* QUIC Event Logging using qlog ([draft-ietf-quic-qlog-main-schema](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/) and [draft-ietf-quic-qlog-quic-events](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-quic-events/)) + +Support for WebTransport over HTTP/3 ([draft-ietf-webtrans-http3](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/)) is implemented in [webtransport-go](https://github.com/quic-go/webtransport-go). + +## Using QUIC + +### Running a Server + +The central entry point is the `quic.Transport`. A transport manages QUIC connections running on a single UDP socket. Since QUIC uses Connection IDs, it can demultiplex a listener (accepting incoming connections) and an arbitrary number of outgoing QUIC connections on the same UDP socket. + +```go +udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: 1234}) +// ... error handling +tr := quic.Transport{ + Conn: udpConn, +} +ln, err := tr.Listen(tlsConf, quicConf) +// ... error handling +go func() { + for { + conn, err := ln.Accept() + // ... error handling + // handle the connection, usually in a new Go routine + } +}() +``` + +The listener `ln` can now be used to accept incoming QUIC connections by (repeatedly) calling the `Accept` method (see below for more information on the `quic.Connection`). + +As a shortcut, `quic.Listen` and `quic.ListenAddr` can be used without explicitly initializing a `quic.Transport`: + +``` +ln, err := quic.Listen(udpConn, tlsConf, quicConf) +``` + +When using the shortcut, it's not possible to reuse the same UDP socket for outgoing connections. + +### Running a Client + +As mentioned above, multiple outgoing connections can share a single UDP socket, since QUIC uses Connection IDs to demultiplex connections. + +```go +ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // 3s handshake timeout +defer cancel() +conn, err := tr.Dial(ctx, , , ) +// ... error handling +``` + +As a shortcut, `quic.Dial` and `quic.DialAddr` can be used without explictly initializing a `quic.Transport`: + +```go +ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // 3s handshake timeout +defer cancel() +conn, err := quic.Dial(ctx, conn, , , ) +``` + +Just as we saw before when used a similar shortcut to run a server, it's also not possible to reuse the same UDP socket for other outgoing connections, or to listen for incoming connections. + +### Using a QUIC Connection + +#### Accepting Streams + +QUIC is a stream-multiplexed transport. A `quic.Connection` fundamentally differs from the `net.Conn` and the `net.PacketConn` interface defined in the standard library. Data is sent and received on (unidirectional and bidirectional) streams (and, if supported, in [datagrams](#quic-datagrams)), not on the connection itself. The stream state machine is described in detail in [Section 3 of RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000#section-3). + +Note: A unidirectional stream is a stream that the initiator can only write to (`quic.SendStream`), and the receiver can only read from (`quic.ReceiveStream`). A bidirectional stream (`quic.Stream`) allows reading from and writing to for both sides. + +On the receiver side, streams are accepted using the `AcceptStream` (for bidirectional) and `AcceptUniStream` functions. For most user cases, it makes sense to call these functions in a loop: + +```go +for { + str, err := conn.AcceptStream(context.Background()) // for bidirectional streams + // ... error handling + // handle the stream, usually in a new Go routine +} +``` + +These functions return an error when the underlying QUIC connection is closed. + +#### Opening Streams + +There are two slightly different ways to open streams, one synchronous and one (potentially) asynchronous. This API is necessary since the receiver grants us a certain number of streams that we're allowed to open. It may grant us additional streams later on (typically when existing streams are closed), but it means that at the time we want to open a new stream, we might not be able to do so. + +Using the synchronous method `OpenStreamSync` for bidirectional streams, and `OpenUniStreamSync` for unidirectional streams, an application can block until the peer allows opening additional streams. In case that we're allowed to open a new stream, these methods return right away: + +```go +ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +defer cancel() +str, err := conn.OpenStreamSync(ctx) // wait up to 5s to open a new bidirectional stream +``` + +The asynchronous version never blocks. If it's currently not possible to open a new stream, it returns a `net.Error` timeout error: + +```go +str, err := conn.OpenStream() +if nerr, ok := err.(net.Error); ok && nerr.Timeout() { + // It's currently not possible to open another stream, + // but it might be possible later, once the peer allowed us to do so. +} +``` + +These functions return an error when the underlying QUIC connection is closed. + +#### Using Streams + +Using QUIC streams is pretty straightforward. The `quic.ReceiveStream` implements the `io.Reader` interface, and the `quic.SendStream` implements the `io.Writer` interface. A bidirectional stream (`quic.Stream`) implements both these interfaces. Conceptually, a bidirectional stream can be thought of as the composition of two unidirectional streams in opposite directions. + +Calling `Close` on a `quic.SendStream` or a `quic.Stream` closes the send side of the stream. On the receiver side, this will be surfaced as an `io.EOF` returned from the `io.Reader` once all data has been consumed. Note that for bidirectional streams, `Close` _only_ closes the send side of the stream. It is still possible to read from the stream until the peer closes or resets the stream. + +In case the application wishes to abort sending on a `quic.SendStream` or a `quic.Stream` , it can reset the send side by calling `CancelWrite` with an application-defined error code (an unsigned 62-bit number). On the receiver side, this surfaced as a `quic.StreamError` containing that error code on the `io.Reader`. Note that for bidirectional streams, `CancelWrite` _only_ resets the send side of the stream. It is still possible to read from the stream until the peer closes or resets the stream. + +Conversely, in case the application wishes to abort receiving from a `quic.ReceiveStream` or a `quic.Stream`, it can ask the sender to abort data transmission by calling `CancelRead` with an application-defined error code (an unsigned 62-bit number). On the receiver side, this surfaced as a `quic.StreamError` containing that error code on the `io.Writer`. Note that for bidirectional streams, `CancelWrite` _only_ resets the receive side of the stream. It is still possible to write to the stream. + +A bidirectional stream is only closed once both the read and the write side of the stream have been either closed or reset. Only then the peer is granted a new stream according to the maximum number of concurrent streams configured via `quic.Config.MaxIncomingStreams`. + +### Configuring QUIC + +The `quic.Config` struct passed to both the listen and dial calls (see above) contains a wide range of configuration options for QUIC connections, incl. the ability to fine-tune flow control limits, the number of streams that the peer is allowed to open concurrently, keep-alives, idle timeouts, and many more. Please refer to the documentation for the `quic.Config` for details. + +The `quic.Transport` contains a few configuration options that don't apply to any single QUIC connection, but to all connections handled by that transport. It is highly recommend to set the `StatelessResetToken`, which allows endpoints to quickly recover from crashes / reboots of our node (see [Section 10.3 of RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000#section-10.3)). + +### Closing a Connection + +#### When the remote Peer closes the Connection + +In case the peer closes the QUIC connection, all calls to open streams, accept streams, as well as all methods on streams immediately return an error. Additionally, it is set as cancellation cause of the connection context. Users can use errors assertions to find out what exactly went wrong: + +* `quic.VersionNegotiationError`: Happens during the handshake, if there is no overlap between our and the remote's supported QUIC versions. +* `quic.HandshakeTimeoutError`: Happens if the QUIC handshake doesn't complete within the time specified in `quic.Config.HandshakeTimeout`. +* `quic.IdleTimeoutError`: Happens after completion of the handshake if the connection is idle for longer than the minimum of both peers idle timeouts (as configured by `quic.Config.IdleTimeout`). The connection is considered idle when no stream data (and datagrams, if applicable) are exchanged for that period. The QUIC connection can be instructed to regularly send a packet to prevent a connection from going idle by setting `quic.Config.KeepAlive`. However, this is no guarantee that the peer doesn't suddenly go away (e.g. by abruptly shutting down the node or by crashing), or by a NAT binding expiring, in which case this error might still occur. +* `quic.StatelessResetError`: Happens when the remote peer lost the state required to decrypt the packet. This requires the `quic.Transport.StatelessResetToken` to be configured by the peer. +* `quic.TransportError`: Happens if when the QUIC protocol is violated. Unless the error code is `APPLICATION_ERROR`, this will not happen unless one of the QUIC stacks involved is misbehaving. Please open an issue if you encounter this error. +* `quic.ApplicationError`: Happens when the remote decides to close the connection, see below. + +#### Initiated by the Application + +A `quic.Connection` can be closed using `CloseWithError`: + +```go +conn.CloseWithError(0x42, "error 0x42 occurred") +``` + +Applications can transmit both an error code (an unsigned 62-bit number) as well as a UTF-8 encoded human-readable reason. The error code allows the receiver to learn why the connection was closed, and the reason can be useful for debugging purposes. + +On the receiver side, this is surfaced as a `quic.ApplicationError`. + +### QUIC Datagrams + +Unreliable datagrams are a QUIC extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221)) that is negotiated during the handshake. Support can be enabled by setting the `quic.Config.EnableDatagram` flag. Note that this doesn't guarantee that the peer also supports datagrams. Whether or not the feature negotiation succeeded can be learned from the `quic.ConnectionState.SupportsDatagrams` obtained from `quic.Connection.ConnectionState()`. + +QUIC DATAGRAMs are a new QUIC frame type sent in QUIC 1-RTT packets (i.e. after completion of the handshake). Therefore, they're end-to-end encrypted and congestion-controlled. However, if a DATAGRAM frame is deemed lost by QUIC's loss detection mechanism, they are not retransmitted. + +Datagrams are sent using the `SendDatagram` method on the `quic.Connection`: + +```go +conn.SendDatagram([]byte("foobar")) +``` + +And received using `ReceiveDatagram`: + +```go +msg, err := conn.ReceiveDatagram() +``` + +Note that this code path is currently not optimized. It works for datagrams that are sent occasionally, but it doesn't achieve the same throughput as writing data on a stream. Please get in touch on issue #3766 if your use case relies on high datagram throughput, or if you'd like to help fix this issue. There are also some restrictions regarding the maximum message size (see #3599). + +### QUIC Event Logging using qlog + +quic-go logs a wide range of events defined in [draft-ietf-quic-qlog-quic-events](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-quic-events/), providing comprehensive insights in the internals of a QUIC connection. + +qlog files can be processed by a number of 3rd-party tools. [qviz](https://qvis.quictools.info/) has proven very useful for debugging all kinds of QUIC connection failures. + +qlog can be activated by setting the `Tracer` callback on the `Config`. It is called as soon as quic-go decides to start the QUIC handshake on a new connection. +`qlog.DefaultTracer` provides a tracer implementation which writes qlog files to a directory specified by the `QLOGDIR` environment variable, if set. +The default qlog tracer can be used like this: +```go +quic.Config{ + Tracer: qlog.DefaultTracer, +} +``` + +This example creates a new qlog file under `/_.qlog`, e.g. `qlogs/2e0407da_client.qlog`. + + +For custom qlog behavior, `qlog.NewConnectionTracer` can be used. + +## Using HTTP/3 + +### As a server + +See the [example server](example/main.go). Starting a QUIC server is very similar to the standard library http package in Go: + +```go +http.Handle("/", http.FileServer(http.Dir(wwwDir))) +http3.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil) +``` + +### As a client + +See the [example client](example/client/main.go). Use a `http3.RoundTripper` as a `Transport` in a `http.Client`. + +```go +http.Client{ + Transport: &http3.RoundTripper{}, +} +``` + +## Projects using quic-go + +| Project | Description | Stars | +| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) | +| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | +| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | +| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | +| [frp](https://github.com/fatedier/frp) | A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet | ![GitHub Repo stars](https://img.shields.io/github/stars/fatedier/frp?style=flat-square) | +| [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) | +| [gost](https://github.com/go-gost/gost) | A simple security tunnel written in Go | ![GitHub Repo stars](https://img.shields.io/github/stars/go-gost/gost?style=flat-square) | +| [Hysteria](https://github.com/apernet/hysteria) | A powerful, lightning fast and censorship resistant proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/apernet/hysteria?style=flat-square) | +| [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications | ![GitHub Repo stars](https://img.shields.io/github/stars/dunglas/mercure?style=flat-square) | +| [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | +| [RoadRunner](https://github.com/roadrunner-server/roadrunner) | High-performance PHP application server, process manager written in Go and powered with plugins | ![GitHub Repo stars](https://img.shields.io/github/stars/roadrunner-server/roadrunner?style=flat-square) | +| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | +| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) | +| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) | +| [YoMo](https://github.com/yomorun/yomo) | Streaming Serverless Framework for Geo-distributed System | ![GitHub Repo stars](https://img.shields.io/github/stars/yomorun/yomo?style=flat-square) | + +If you'd like to see your project added to this list, please send us a PR. + +## Release Policy + +quic-go always aims to support the latest two Go releases. + +## Contributing + +We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with [help wanted](https://github.com/quic-go/quic-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). If you have any questions, please feel free to reach out by opening an issue or leaving a comment. diff --git a/vendor/github.com/quic-go/quic-go/SECURITY.md b/vendor/github.com/quic-go/quic-go/SECURITY.md new file mode 100644 index 000000000..c24c08f86 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +quic-go still in development. This means that there may be problems in our protocols, +or there may be mistakes in our implementations. +We take security vulnerabilities very seriously. If you discover a security issue, +please bring it to our attention right away! + +## Reporting a Vulnerability + +If you find a vulnerability that may affect live deployments -- for example, by exposing +a remote execution exploit -- please [**report privately**](https://github.com/quic-go/quic-go/security/advisories/new). +Please **DO NOT file a public issue**. + +If the issue is an implementation weakness that cannot be immediately exploited or +something not yet deployed, just discuss it openly. + +## Reporting a non security bug + +For non-security bugs, please simply file a GitHub [issue](https://github.com/quic-go/quic-go/issues/new). diff --git a/vendor/github.com/quic-go/quic-go/buffer_pool.go b/vendor/github.com/quic-go/quic-go/buffer_pool.go new file mode 100644 index 000000000..48589e123 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/buffer_pool.go @@ -0,0 +1,92 @@ +package quic + +import ( + "sync" + + "github.com/quic-go/quic-go/internal/protocol" +) + +type packetBuffer struct { + Data []byte + + // refCount counts how many packets Data is used in. + // It doesn't support concurrent use. + // It is > 1 when used for coalesced packet. + refCount int +} + +// Split increases the refCount. +// It must be called when a packet buffer is used for more than one packet, +// e.g. when splitting coalesced packets. +func (b *packetBuffer) Split() { + b.refCount++ +} + +// Decrement decrements the reference counter. +// It doesn't put the buffer back into the pool. +func (b *packetBuffer) Decrement() { + b.refCount-- + if b.refCount < 0 { + panic("negative packetBuffer refCount") + } +} + +// MaybeRelease puts the packet buffer back into the pool, +// if the reference counter already reached 0. +func (b *packetBuffer) MaybeRelease() { + // only put the packetBuffer back if it's not used any more + if b.refCount == 0 { + b.putBack() + } +} + +// Release puts back the packet buffer into the pool. +// It should be called when processing is definitely finished. +func (b *packetBuffer) Release() { + b.Decrement() + if b.refCount != 0 { + panic("packetBuffer refCount not zero") + } + b.putBack() +} + +// Len returns the length of Data +func (b *packetBuffer) Len() protocol.ByteCount { return protocol.ByteCount(len(b.Data)) } +func (b *packetBuffer) Cap() protocol.ByteCount { return protocol.ByteCount(cap(b.Data)) } + +func (b *packetBuffer) putBack() { + if cap(b.Data) == protocol.MaxPacketBufferSize { + bufferPool.Put(b) + return + } + if cap(b.Data) == protocol.MaxLargePacketBufferSize { + largeBufferPool.Put(b) + return + } + panic("putPacketBuffer called with packet of wrong size!") +} + +var bufferPool, largeBufferPool sync.Pool + +func getPacketBuffer() *packetBuffer { + buf := bufferPool.Get().(*packetBuffer) + buf.refCount = 1 + buf.Data = buf.Data[:0] + return buf +} + +func getLargePacketBuffer() *packetBuffer { + buf := largeBufferPool.Get().(*packetBuffer) + buf.refCount = 1 + buf.Data = buf.Data[:0] + return buf +} + +func init() { + bufferPool.New = func() any { + return &packetBuffer{Data: make([]byte, 0, protocol.MaxPacketBufferSize)} + } + largeBufferPool.New = func() any { + return &packetBuffer{Data: make([]byte, 0, protocol.MaxLargePacketBufferSize)} + } +} diff --git a/vendor/github.com/quic-go/quic-go/client.go b/vendor/github.com/quic-go/quic-go/client.go new file mode 100644 index 000000000..70dd5e19c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/client.go @@ -0,0 +1,251 @@ +package quic + +import ( + "context" + "crypto/tls" + "errors" + "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 uint64 + 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. +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 { + return nil, err + } + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + tr, err := setupTransport(udpConn, tlsConf, true) + if err != nil { + return nil, err + } + return tr.dial(ctx, udpAddr, addr, tlsConf, conf, false) +} + +// DialAddrEarly establishes a new 0-RTT QUIC connection to a server. +// 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 { + return nil, err + } + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + tr, err := setupTransport(udpConn, tlsConf, true) + if err != nil { + return nil, err + } + conn, err := tr.dial(ctx, udpAddr, addr, tlsConf, conf, true) + if err != nil { + tr.Close() + return nil, err + } + return conn, nil +} + +// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn. +// 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 { + return nil, err + } + conn, err := dl.DialEarly(ctx, addr, tlsConf, conf) + if err != nil { + dl.Close() + return nil, err + } + return conn, nil +} + +// 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), +// 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, +// 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) { + dl, err := setupTransport(c, tlsConf, false) + if err != nil { + return nil, err + } + conn, err := dl.Dial(ctx, addr, tlsConf, conf) + if err != nil { + dl.Close() + return nil, err + } + return conn, nil +} + +func setupTransport(c net.PacketConn, tlsConf *tls.Config, createdPacketConn bool) (*Transport, error) { + if tlsConf == nil { + return nil, errors.New("quic: tls.Config not set") + } + return &Transport{ + Conn: c, + createdConn: createdPacketConn, + 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( + c.sendConn, + c.packetHandlers, + c.destConnID, + c.srcConnID, + c.connIDGenerator, + c.config, + c.tlsConf, + c.initialPacketNumber, + c.use0RTT, + c.hasNegotiatedVersion, + c.tracer, + c.tracingID, + 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 + } +} diff --git a/vendor/github.com/quic-go/quic-go/closed_conn.go b/vendor/github.com/quic-go/quic-go/closed_conn.go new file mode 100644 index 000000000..833385327 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/closed_conn.go @@ -0,0 +1,57 @@ +package quic + +import ( + "math/bits" + "net" + + "github.com/quic-go/quic-go/internal/utils" +) + +// A closedLocalConn is a connection that we closed locally. +// 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 + logger utils.Logger + + sendPacket func(net.Addr, packetInfo) +} + +var _ packetHandler = &closedLocalConn{} + +// newClosedLocalConn creates a new closedLocalConn and runs it. +func newClosedLocalConn(sendPacket func(net.Addr, packetInfo), logger utils.Logger) packetHandler { + return &closedLocalConn{ + sendPacket: sendPacket, + logger: logger, + } +} + +func (c *closedLocalConn) handlePacket(p receivedPacket) { + c.counter++ + // exponential backoff + // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving + if bits.OnesCount32(c.counter) != 1 { + return + } + c.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", c.counter) + c.sendPacket(p.remoteAddr, p.info) +} + +func (c *closedLocalConn) destroy(error) {} +func (c *closedLocalConn) closeWithTransportError(TransportErrorCode) {} + +// A closedRemoteConn is a connection that was closed remotely. +// For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE. +// We can just ignore those packets. +type closedRemoteConn struct{} + +var _ packetHandler = &closedRemoteConn{} + +func newClosedRemoteConn() packetHandler { + return &closedRemoteConn{} +} + +func (c *closedRemoteConn) handlePacket(receivedPacket) {} +func (c *closedRemoteConn) destroy(error) {} +func (c *closedRemoteConn) closeWithTransportError(TransportErrorCode) {} diff --git a/vendor/github.com/quic-go/quic-go/codecov.yml b/vendor/github.com/quic-go/quic-go/codecov.yml new file mode 100644 index 000000000..59e4b58f6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/codecov.yml @@ -0,0 +1,16 @@ +coverage: + round: nearest + ignore: + - http3/gzip_reader.go + - interop/ + - internal/handshake/cipher_suite.go + - internal/utils/linkedlist/linkedlist.go + - internal/testdata + - testutils/ + - fuzzing/ + - metrics/ + status: + project: + default: + threshold: 0.5 + patch: false diff --git a/vendor/github.com/quic-go/quic-go/config.go b/vendor/github.com/quic-go/quic-go/config.go new file mode 100644 index 000000000..ee032e6ea --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/config.go @@ -0,0 +1,117 @@ +package quic + +import ( + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// Clone clones a Config +func (c *Config) Clone() *Config { + copy := *c + return © +} + +func (c *Config) handshakeTimeout() time.Duration { + return 2 * c.HandshakeIdleTimeout +} + +func (c *Config) maxRetryTokenAge() time.Duration { + return c.handshakeTimeout() +} + +func validateConfig(config *Config) error { + if config == nil { + return nil + } + const maxStreams = 1 << 60 + if config.MaxIncomingStreams > maxStreams { + config.MaxIncomingStreams = maxStreams + } + if config.MaxIncomingUniStreams > maxStreams { + config.MaxIncomingUniStreams = maxStreams + } + if config.MaxStreamReceiveWindow > quicvarint.Max { + config.MaxStreamReceiveWindow = quicvarint.Max + } + if config.MaxConnectionReceiveWindow > quicvarint.Max { + config.MaxConnectionReceiveWindow = quicvarint.Max + } + // check that all QUIC versions are actually supported + for _, v := range config.Versions { + if !protocol.IsValidVersion(v) { + return fmt.Errorf("invalid QUIC version: %s", v) + } + } + return nil +} + +// populateConfig populates fields in the quic.Config with their default values, if none are set +// it may be called with nil +func populateConfig(config *Config) *Config { + if config == nil { + config = &Config{} + } + versions := config.Versions + if len(versions) == 0 { + versions = protocol.SupportedVersions + } + handshakeIdleTimeout := protocol.DefaultHandshakeIdleTimeout + if config.HandshakeIdleTimeout != 0 { + handshakeIdleTimeout = config.HandshakeIdleTimeout + } + idleTimeout := protocol.DefaultIdleTimeout + if config.MaxIdleTimeout != 0 { + idleTimeout = config.MaxIdleTimeout + } + initialStreamReceiveWindow := config.InitialStreamReceiveWindow + if initialStreamReceiveWindow == 0 { + initialStreamReceiveWindow = protocol.DefaultInitialMaxStreamData + } + maxStreamReceiveWindow := config.MaxStreamReceiveWindow + if maxStreamReceiveWindow == 0 { + maxStreamReceiveWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow + } + initialConnectionReceiveWindow := config.InitialConnectionReceiveWindow + if initialConnectionReceiveWindow == 0 { + initialConnectionReceiveWindow = protocol.DefaultInitialMaxData + } + maxConnectionReceiveWindow := config.MaxConnectionReceiveWindow + if maxConnectionReceiveWindow == 0 { + maxConnectionReceiveWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow + } + maxIncomingStreams := config.MaxIncomingStreams + if maxIncomingStreams == 0 { + maxIncomingStreams = protocol.DefaultMaxIncomingStreams + } else if maxIncomingStreams < 0 { + maxIncomingStreams = 0 + } + maxIncomingUniStreams := config.MaxIncomingUniStreams + if maxIncomingUniStreams == 0 { + maxIncomingUniStreams = protocol.DefaultMaxIncomingUniStreams + } else if maxIncomingUniStreams < 0 { + maxIncomingUniStreams = 0 + } + + return &Config{ + GetConfigForClient: config.GetConfigForClient, + Versions: versions, + HandshakeIdleTimeout: handshakeIdleTimeout, + MaxIdleTimeout: idleTimeout, + KeepAlivePeriod: config.KeepAlivePeriod, + InitialStreamReceiveWindow: initialStreamReceiveWindow, + MaxStreamReceiveWindow: maxStreamReceiveWindow, + InitialConnectionReceiveWindow: initialConnectionReceiveWindow, + MaxConnectionReceiveWindow: maxConnectionReceiveWindow, + AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, + MaxIncomingStreams: maxIncomingStreams, + MaxIncomingUniStreams: maxIncomingUniStreams, + TokenStore: config.TokenStore, + EnableDatagrams: config.EnableDatagrams, + DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, + Allow0RTT: config.Allow0RTT, + Tracer: config.Tracer, + } +} diff --git a/vendor/github.com/quic-go/quic-go/conn_id_generator.go b/vendor/github.com/quic-go/quic-go/conn_id_generator.go new file mode 100644 index 000000000..d7be6540e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/conn_id_generator.go @@ -0,0 +1,138 @@ +package quic + +import ( + "fmt" + + "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 connIDGenerator struct { + generator ConnectionIDGenerator + highestSeq uint64 + + 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) +} + +func newConnIDGenerator( + 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), + 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, + } + m.activeSrcConnIDs[0] = initialConnectionID + m.initialClientDestConnID = initialClientDestConnID + return m +} + +func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error { + if m.generator.ConnectionIDLen() == 0 { + return nil + } + // The active_connection_id_limit transport parameter is the number of + // connection IDs the peer will store. This limit includes the connection ID + // used during the handshake, and the one sent in the preferred_address + // transport parameter. + // We currently don't send the preferred_address transport parameter, + // so we can issue (limit - 1) connection IDs. + for i := uint64(len(m.activeSrcConnIDs)); i < min(limit, protocol.MaxIssuedConnectionIDs); i++ { + if err := m.issueNewConnID(); err != nil { + return err + } + } + return nil +} + +func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error { + if seq > m.highestSeq { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq), + } + } + connID, ok := m.activeSrcConnIDs[seq] + // We might already have deleted this connection ID, if this is a duplicate frame. + if !ok { + return nil + } + if connID == sentWithDestConnID { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID), + } + } + m.retireConnectionID(connID) + delete(m.activeSrcConnIDs, seq) + // Don't issue a replacement for the initial connection ID. + if seq == 0 { + return nil + } + return m.issueNewConnID() +} + +func (m *connIDGenerator) issueNewConnID() error { + connID, err := m.generator.GenerateConnectionID() + if err != nil { + return err + } + m.activeSrcConnIDs[m.highestSeq+1] = connID + m.addConnectionID(connID) + m.queueControlFrame(&wire.NewConnectionIDFrame{ + SequenceNumber: m.highestSeq + 1, + ConnectionID: connID, + StatelessResetToken: m.getStatelessResetToken(connID), + }) + m.highestSeq++ + return nil +} + +func (m *connIDGenerator) SetHandshakeComplete() { + if m.initialClientDestConnID != nil { + m.retireConnectionID(*m.initialClientDestConnID) + m.initialClientDestConnID = nil + } +} + +func (m *connIDGenerator) RemoveAll() { + if m.initialClientDestConnID != nil { + m.removeConnectionID(*m.initialClientDestConnID) + } + for _, connID := range m.activeSrcConnIDs { + m.removeConnectionID(connID) + } +} + +func (m *connIDGenerator) ReplaceWithClosed(connClose []byte) { + connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+1) + if m.initialClientDestConnID != nil { + connIDs = append(connIDs, *m.initialClientDestConnID) + } + for _, connID := range m.activeSrcConnIDs { + connIDs = append(connIDs, connID) + } + m.replaceWithClosed(connIDs, connClose) +} diff --git a/vendor/github.com/quic-go/quic-go/conn_id_manager.go b/vendor/github.com/quic-go/quic-go/conn_id_manager.go new file mode 100644 index 000000000..4aa3f749e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/conn_id_manager.go @@ -0,0 +1,214 @@ +package quic + +import ( + "fmt" + + "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" +) + +type newConnID struct { + SequenceNumber uint64 + ConnectionID protocol.ConnectionID + StatelessResetToken protocol.StatelessResetToken +} + +type connIDManager struct { + queue list.List[newConnID] + + handshakeComplete bool + activeSequenceNumber uint64 + highestRetired uint64 + activeConnectionID protocol.ConnectionID + activeStatelessResetToken *protocol.StatelessResetToken + + // We change the connection ID after sending on average + // protocol.PacketsPerConnectionID packets. The actual value is randomized + // hide the packet loss rate from on-path observers. + rand utils.Rand + packetsSinceLastChange uint32 + packetsPerConnectionID uint32 + + addStatelessResetToken func(protocol.StatelessResetToken) + removeStatelessResetToken func(protocol.StatelessResetToken) + queueControlFrame func(wire.Frame) +} + +func newConnIDManager( + initialDestConnID protocol.ConnectionID, + addStatelessResetToken func(protocol.StatelessResetToken), + removeStatelessResetToken func(protocol.StatelessResetToken), + queueControlFrame func(wire.Frame), +) *connIDManager { + return &connIDManager{ + activeConnectionID: initialDestConnID, + addStatelessResetToken: addStatelessResetToken, + removeStatelessResetToken: removeStatelessResetToken, + queueControlFrame: queueControlFrame, + } +} + +func (h *connIDManager) AddFromPreferredAddress(connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error { + return h.addConnectionID(1, connID, resetToken) +} + +func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error { + if err := h.add(f); err != nil { + return err + } + if h.queue.Len() >= protocol.MaxActiveConnectionIDs { + return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError} + } + return nil +} + +func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error { + // 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 { + h.queueControlFrame(&wire.RetireConnectionIDFrame{ + SequenceNumber: f.SequenceNumber, + }) + return nil + } + + // 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 + } + next = el.Next() + h.queueControlFrame(&wire.RetireConnectionIDFrame{ + SequenceNumber: el.Value.SequenceNumber, + }) + h.queue.Remove(el) + } + h.highestRetired = f.RetirePriorTo + } + + if f.SequenceNumber == h.activeSequenceNumber { + return nil + } + + if err := h.addConnectionID(f.SequenceNumber, f.ConnectionID, f.StatelessResetToken); err != nil { + return err + } + + // Retire the active connection ID, if necessary. + if h.activeSequenceNumber < f.RetirePriorTo { + // The queue is guaranteed to have at least one element at this point. + h.updateConnectionID() + } + return nil +} + +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{ + 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 { + return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq) + } + if el.Value.StatelessResetToken != resetToken { + return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq) + } + break + } + if el.Value.SequenceNumber > seq { + h.queue.InsertBefore(newConnID{ + SequenceNumber: seq, + ConnectionID: connID, + StatelessResetToken: resetToken, + }, el) + break + } + } + return nil +} + +func (h *connIDManager) updateConnectionID() { + h.queueControlFrame(&wire.RetireConnectionIDFrame{ + SequenceNumber: h.activeSequenceNumber, + }) + h.highestRetired = max(h.highestRetired, h.activeSequenceNumber) + if h.activeStatelessResetToken != nil { + h.removeStatelessResetToken(*h.activeStatelessResetToken) + } + + front := h.queue.Remove(h.queue.Front()) + h.activeSequenceNumber = front.SequenceNumber + h.activeConnectionID = front.ConnectionID + h.activeStatelessResetToken = &front.StatelessResetToken + h.packetsSinceLastChange = 0 + h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(h.rand.Int31n(protocol.PacketsPerConnectionID)) + h.addStatelessResetToken(*h.activeStatelessResetToken) +} + +func (h *connIDManager) Close() { + if h.activeStatelessResetToken != nil { + h.removeStatelessResetToken(*h.activeStatelessResetToken) + } +} + +// is called when the server performs a Retry +// and when the server changes the connection ID in the first Initial sent +func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) { + if h.activeSequenceNumber != 0 { + panic("expected first connection ID to have sequence number 0") + } + h.activeConnectionID = newConnID +} + +// is called when the server provides a stateless reset token in the transport parameters +func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) { + if h.activeSequenceNumber != 0 { + panic("expected first connection ID to have sequence number 0") + } + h.activeStatelessResetToken = &token + h.addStatelessResetToken(token) +} + +func (h *connIDManager) SentPacket() { + h.packetsSinceLastChange++ +} + +func (h *connIDManager) shouldUpdateConnID() bool { + if !h.handshakeComplete { + return false + } + // initiate the first change as early as possible (after handshake completion) + if h.queue.Len() > 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 && + h.packetsSinceLastChange >= h.packetsPerConnectionID +} + +func (h *connIDManager) Get() protocol.ConnectionID { + if h.shouldUpdateConnID() { + h.updateConnectionID() + } + return h.activeConnectionID +} + +func (h *connIDManager) SetHandshakeComplete() { + h.handshakeComplete = true +} diff --git a/vendor/github.com/quic-go/quic-go/connection.go b/vendor/github.com/quic-go/quic-go/connection.go new file mode 100644 index 000000000..f8bcd613c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/connection.go @@ -0,0 +1,2393 @@ +package quic + +import ( + "bytes" + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "reflect" + "sync" + "sync/atomic" + "time" + + "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/flowcontrol" + "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/logutils" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/logging" +) + +type unpacker interface { + UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte, v protocol.Version) (*unpackedPacket, error) + UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) +} + +type streamGetter interface { + GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) + GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) +} + +type streamManager interface { + GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) + GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) + OpenStream() (Stream, error) + OpenUniStream() (SendStream, error) + OpenStreamSync(context.Context) (Stream, error) + OpenUniStreamSync(context.Context) (SendStream, error) + AcceptStream(context.Context) (Stream, error) + AcceptUniStream(context.Context) (ReceiveStream, error) + DeleteStream(protocol.StreamID) error + UpdateLimits(*wire.TransportParameters) + HandleMaxStreamsFrame(*wire.MaxStreamsFrame) + CloseWithError(error) + ResetFor0RTT() + UseResetMaps() +} + +type cryptoStreamHandler interface { + StartHandshake() error + ChangeConnectionID(protocol.ConnectionID) + SetLargest1RTTAcked(protocol.PacketNumber) error + SetHandshakeConfirmed() + GetSessionTicket() ([]byte, error) + NextEvent() handshake.Event + DiscardInitialKeys() + io.Closer + ConnectionState() handshake.ConnectionState +} + +type receivedPacket struct { + buffer *packetBuffer + + remoteAddr net.Addr + rcvTime time.Time + data []byte + + ecn protocol.ECN + + info packetInfo // only valid if the contained IP address is valid +} + +func (p *receivedPacket) Size() protocol.ByteCount { return protocol.ByteCount(len(p.data)) } + +func (p *receivedPacket) Clone() *receivedPacket { + return &receivedPacket{ + remoteAddr: p.remoteAddr, + rcvTime: p.rcvTime, + data: p.data, + buffer: p.buffer, + ecn: p.ecn, + info: p.info, + } +} + +type connRunner interface { + Add(protocol.ConnectionID, packetHandler) bool + GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken + Retire(protocol.ConnectionID) + Remove(protocol.ConnectionID) + ReplaceWithClosed([]protocol.ConnectionID, []byte) + AddResetToken(protocol.StatelessResetToken, packetHandler) + RemoveResetToken(protocol.StatelessResetToken) +} + +type closeError struct { + err error + remote bool + immediate bool +} + +type errCloseForRecreating struct { + nextPacketNumber protocol.PacketNumber + nextVersion protocol.Version +} + +func (e *errCloseForRecreating) Error() string { + return "closing connection in order to recreate it" +} + +var connTracingID uint64 // to be accessed atomically +func nextConnTracingID() uint64 { return atomic.AddUint64(&connTracingID, 1) } + +// A Connection is a QUIC connection +type connection struct { + // Destination connection ID used during the handshake. + // Used to check source connection ID on incoming packets. + handshakeDestConnID protocol.ConnectionID + // Set for the client. Destination connection ID used on the first Initial sent. + origDestConnID protocol.ConnectionID + retrySrcConnID *protocol.ConnectionID // only set for the client (and if a Retry was performed) + + srcConnIDLen int + + perspective protocol.Perspective + version protocol.Version + config *Config + + conn sendConn + sendQueue sender + + streamsMap streamManager + connIDManager *connIDManager + connIDGenerator *connIDGenerator + + rttStats *utils.RTTStats + + cryptoStreamManager *cryptoStreamManager + sentPacketHandler ackhandler.SentPacketHandler + receivedPacketHandler ackhandler.ReceivedPacketHandler + retransmissionQueue *retransmissionQueue + framer framer + windowUpdateQueue *windowUpdateQueue + connFlowController flowcontrol.ConnectionFlowController + tokenStoreKey string // only set for the client + tokenGenerator *handshake.TokenGenerator // only set for the server + + unpacker unpacker + frameParser wire.FrameParser + packer packer + mtuDiscoverer mtuDiscoverer // initialized when the handshake completes + + initialStream cryptoStream + handshakeStream cryptoStream + oneRTTStream cryptoStream // only set for the server + cryptoStreamHandler cryptoStreamHandler + + receivedPackets chan receivedPacket + sendingScheduled chan struct{} + + closeOnce sync.Once + // closeChan is used to notify the run loop that it should terminate + closeChan chan closeError + + ctx context.Context + ctxCancel context.CancelCauseFunc + handshakeCtx context.Context + handshakeCtxCancel context.CancelFunc + + undecryptablePackets []receivedPacket // undecryptable packets, waiting for a change in encryption level + undecryptablePacketsToProcess []receivedPacket + + earlyConnReadyChan chan struct{} + sentFirstPacket bool + droppedInitialKeys bool + handshakeComplete bool + handshakeConfirmed bool + + receivedRetry bool + versionNegotiated bool + receivedFirstPacket bool + + // the minimum of the max_idle_timeout values advertised by both endpoints + idleTimeout time.Duration + creationTime time.Time + // The idle timeout is set based on the max of the time we received the last packet... + lastPacketReceivedTime time.Time + // ... and the time we sent a new ack-eliciting packet after receiving a packet. + firstAckElicitingPacketAfterIdleSentTime time.Time + // pacingDeadline is the time when the next packet should be sent + pacingDeadline time.Time + + peerParams *wire.TransportParameters + + timer connectionTimer + // keepAlivePingSent stores whether a keep alive PING is in flight. + // It is reset as soon as we receive a packet from the peer. + keepAlivePingSent bool + keepAliveInterval time.Duration + + datagramQueue *datagramQueue + + connStateMutex sync.Mutex + connState ConnectionState + + logID string + tracer *logging.ConnectionTracer + logger utils.Logger +} + +var ( + _ Connection = &connection{} + _ EarlyConnection = &connection{} + _ streamSender = &connection{} +) + +var newConnection = func( + conn sendConn, + runner connRunner, + origDestConnID protocol.ConnectionID, + retrySrcConnID *protocol.ConnectionID, + clientDestConnID protocol.ConnectionID, + destConnID protocol.ConnectionID, + srcConnID protocol.ConnectionID, + connIDGenerator ConnectionIDGenerator, + statelessResetToken protocol.StatelessResetToken, + conf *Config, + tlsConf *tls.Config, + tokenGenerator *handshake.TokenGenerator, + clientAddressValidated bool, + tracer *logging.ConnectionTracer, + tracingID uint64, + logger utils.Logger, + v protocol.Version, +) quicConn { + s := &connection{ + conn: conn, + config: conf, + handshakeDestConnID: destConnID, + srcConnIDLen: srcConnID.Len(), + tokenGenerator: tokenGenerator, + oneRTTStream: newCryptoStream(), + perspective: protocol.PerspectiveServer, + tracer: tracer, + logger: logger, + version: v, + } + if origDestConnID.Len() > 0 { + s.logID = origDestConnID.String() + } else { + s.logID = destConnID.String() + } + s.connIDManager = newConnIDManager( + destConnID, + func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) }, + runner.RemoveResetToken, + s.queueControlFrame, + ) + s.connIDGenerator = newConnIDGenerator( + srcConnID, + &clientDestConnID, + func(connID protocol.ConnectionID) { runner.Add(connID, s) }, + runner.GetStatelessResetToken, + runner.Remove, + runner.Retire, + runner.ReplaceWithClosed, + s.queueControlFrame, + connIDGenerator, + ) + s.preSetup() + s.ctx, s.ctxCancel = context.WithCancelCause(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) + s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( + 0, + getMaxPacketSize(s.conn.RemoteAddr()), + s.rttStats, + clientAddressValidated, + s.conn.capabilities().ECN, + s.perspective, + s.tracer, + s.logger, + ) + s.mtuDiscoverer = newMTUDiscoverer(s.rttStats, getMaxPacketSize(s.conn.RemoteAddr()), s.sentPacketHandler.SetMaxDatagramSize) + params := &wire.TransportParameters{ + InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow), + MaxIdleTimeout: s.config.MaxIdleTimeout, + MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams), + MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams), + MaxAckDelay: protocol.MaxAckDelayInclGranularity, + AckDelayExponent: protocol.AckDelayExponent, + DisableActiveMigration: true, + StatelessResetToken: &statelessResetToken, + OriginalDestinationConnectionID: origDestConnID, + // For interoperability with quic-go versions before May 2023, this value must be set to a value + // different from protocol.DefaultActiveConnectionIDLimit. + // If set to the default value, it will be omitted from the transport parameters, which will make + // old quic-go versions interpret it as 0, instead of the default value of 2. + // See https://github.com/quic-go/quic-go/pull/3806. + ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, + InitialSourceConnectionID: srcConnID, + RetrySourceConnectionID: retrySrcConnID, + } + if s.config.EnableDatagrams { + params.MaxDatagramFrameSize = wire.MaxDatagramSize + } else { + params.MaxDatagramFrameSize = protocol.InvalidByteCount + } + if s.tracer != nil && s.tracer.SentTransportParameters != nil { + s.tracer.SentTransportParameters(params) + } + cs := handshake.NewCryptoSetupServer( + clientDestConnID, + conn.LocalAddr(), + conn.RemoteAddr(), + params, + tlsConf, + conf.Allow0RTT, + s.rttStats, + tracer, + logger, + s.version, + ) + s.cryptoStreamHandler = cs + s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) + s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) + s.cryptoStreamManager = newCryptoStreamManager(cs, s.initialStream, s.handshakeStream, s.oneRTTStream) + return s +} + +// declare this as a variable, such that we can it mock it in the tests +var newClientConnection = func( + conn sendConn, + runner connRunner, + destConnID protocol.ConnectionID, + srcConnID protocol.ConnectionID, + connIDGenerator ConnectionIDGenerator, + conf *Config, + tlsConf *tls.Config, + initialPacketNumber protocol.PacketNumber, + enable0RTT bool, + hasNegotiatedVersion bool, + tracer *logging.ConnectionTracer, + tracingID uint64, + logger utils.Logger, + v protocol.Version, +) quicConn { + s := &connection{ + conn: conn, + config: conf, + origDestConnID: destConnID, + handshakeDestConnID: destConnID, + srcConnIDLen: srcConnID.Len(), + perspective: protocol.PerspectiveClient, + logID: destConnID.String(), + logger: logger, + tracer: tracer, + versionNegotiated: hasNegotiatedVersion, + version: v, + } + s.connIDManager = newConnIDManager( + destConnID, + func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) }, + runner.RemoveResetToken, + s.queueControlFrame, + ) + s.connIDGenerator = newConnIDGenerator( + srcConnID, + nil, + func(connID protocol.ConnectionID) { runner.Add(connID, s) }, + runner.GetStatelessResetToken, + runner.Remove, + runner.Retire, + runner.ReplaceWithClosed, + s.queueControlFrame, + connIDGenerator, + ) + s.preSetup() + s.ctx, s.ctxCancel = context.WithCancelCause(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) + s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( + initialPacketNumber, + getMaxPacketSize(s.conn.RemoteAddr()), + s.rttStats, + false, // has no effect + s.conn.capabilities().ECN, + s.perspective, + s.tracer, + s.logger, + ) + s.mtuDiscoverer = newMTUDiscoverer(s.rttStats, getMaxPacketSize(s.conn.RemoteAddr()), s.sentPacketHandler.SetMaxDatagramSize) + oneRTTStream := newCryptoStream() + params := &wire.TransportParameters{ + InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow), + InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow), + MaxIdleTimeout: s.config.MaxIdleTimeout, + MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams), + MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams), + MaxAckDelay: protocol.MaxAckDelayInclGranularity, + AckDelayExponent: protocol.AckDelayExponent, + DisableActiveMigration: true, + // For interoperability with quic-go versions before May 2023, this value must be set to a value + // different from protocol.DefaultActiveConnectionIDLimit. + // If set to the default value, it will be omitted from the transport parameters, which will make + // old quic-go versions interpret it as 0, instead of the default value of 2. + // See https://github.com/quic-go/quic-go/pull/3806. + ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, + InitialSourceConnectionID: srcConnID, + } + if s.config.EnableDatagrams { + params.MaxDatagramFrameSize = wire.MaxDatagramSize + } else { + params.MaxDatagramFrameSize = protocol.InvalidByteCount + } + if s.tracer != nil && s.tracer.SentTransportParameters != nil { + s.tracer.SentTransportParameters(params) + } + cs := handshake.NewCryptoSetupClient( + destConnID, + params, + tlsConf, + enable0RTT, + s.rttStats, + tracer, + logger, + s.version, + ) + s.cryptoStreamHandler = cs + s.cryptoStreamManager = newCryptoStreamManager(cs, s.initialStream, s.handshakeStream, oneRTTStream) + s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) + s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) + if len(tlsConf.ServerName) > 0 { + s.tokenStoreKey = tlsConf.ServerName + } else { + s.tokenStoreKey = conn.RemoteAddr().String() + } + if s.config.TokenStore != nil { + if token := s.config.TokenStore.Pop(s.tokenStoreKey); token != nil { + s.packer.SetToken(token.data) + } + } + return s +} + +func (s *connection) preSetup() { + s.initialStream = newCryptoStream() + s.handshakeStream = newCryptoStream() + s.sendQueue = newSendQueue(s.conn) + s.retransmissionQueue = newRetransmissionQueue() + s.frameParser = *wire.NewFrameParser(s.config.EnableDatagrams) + s.rttStats = &utils.RTTStats{} + s.connFlowController = flowcontrol.NewConnectionFlowController( + protocol.ByteCount(s.config.InitialConnectionReceiveWindow), + protocol.ByteCount(s.config.MaxConnectionReceiveWindow), + s.onHasConnectionWindowUpdate, + func(size protocol.ByteCount) bool { + if s.config.AllowConnectionWindowIncrease == nil { + return true + } + return s.config.AllowConnectionWindowIncrease(s, uint64(size)) + }, + s.rttStats, + s.logger, + ) + s.earlyConnReadyChan = make(chan struct{}) + s.streamsMap = newStreamsMap( + s, + s.newFlowController, + uint64(s.config.MaxIncomingStreams), + uint64(s.config.MaxIncomingUniStreams), + s.perspective, + ) + s.framer = newFramer(s.streamsMap) + s.receivedPackets = make(chan receivedPacket, protocol.MaxConnUnprocessedPackets) + s.closeChan = make(chan closeError, 1) + s.sendingScheduled = make(chan struct{}, 1) + s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background()) + + now := time.Now() + s.lastPacketReceivedTime = now + s.creationTime = now + + s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame) + s.datagramQueue = newDatagramQueue(s.scheduleSending, s.logger) + s.connState.Version = s.version +} + +// run the connection main loop +func (s *connection) run() error { + var closeErr closeError + defer func() { + s.ctxCancel(closeErr.err) + }() + + s.timer = *newTimer() + + if err := s.cryptoStreamHandler.StartHandshake(); err != nil { + return err + } + if err := s.handleHandshakeEvents(); err != nil { + return err + } + go func() { + if err := s.sendQueue.Run(); err != nil { + s.destroyImpl(err) + } + }() + + if s.perspective == protocol.PerspectiveClient { + s.scheduleSending() // so the ClientHello actually gets sent + } + + var sendQueueAvailable <-chan struct{} + +runLoop: + for { + if s.framer.QueuedTooManyControlFrames() { + s.closeLocal(&qerr.TransportError{ErrorCode: InternalError}) + } + // Close immediately if requested + select { + case closeErr = <-s.closeChan: + break runLoop + default: + } + + s.maybeResetTimer() + + var processedUndecryptablePacket bool + if len(s.undecryptablePacketsToProcess) > 0 { + queue := s.undecryptablePacketsToProcess + s.undecryptablePacketsToProcess = nil + for _, p := range queue { + if processed := s.handlePacketImpl(p); processed { + processedUndecryptablePacket = true + } + // Don't set timers and send packets if the packet made us close the connection. + select { + case closeErr = <-s.closeChan: + break runLoop + default: + } + } + } + // If we processed any undecryptable packets, jump to the resetting of the timers directly. + if !processedUndecryptablePacket { + select { + case closeErr = <-s.closeChan: + break runLoop + case <-s.timer.Chan(): + s.timer.SetRead() + // We do all the interesting stuff after the switch statement, so + // nothing to see here. + case <-s.sendingScheduled: + // We do all the interesting stuff after the switch statement, so + // nothing to see here. + case <-sendQueueAvailable: + case firstPacket := <-s.receivedPackets: + wasProcessed := s.handlePacketImpl(firstPacket) + // Don't set timers and send packets if the packet made us close the connection. + select { + case closeErr = <-s.closeChan: + break runLoop + default: + } + if s.handshakeComplete { + // Now process all packets in the receivedPackets channel. + // Limit the number of packets to the length of the receivedPackets channel, + // so we eventually get a chance to send out an ACK when receiving a lot of packets. + numPackets := len(s.receivedPackets) + receiveLoop: + for i := 0; i < numPackets; i++ { + select { + case p := <-s.receivedPackets: + if processed := s.handlePacketImpl(p); processed { + wasProcessed = true + } + select { + case closeErr = <-s.closeChan: + break runLoop + default: + } + default: + break receiveLoop + } + } + } + // Only reset the timers if this packet was actually processed. + // This avoids modifying any state when handling undecryptable packets, + // which could be injected by an attacker. + if !wasProcessed { + continue + } + } + } + + now := time.Now() + if timeout := s.sentPacketHandler.GetLossDetectionTimeout(); !timeout.IsZero() && timeout.Before(now) { + // This could cause packets to be retransmitted. + // Check it before trying to send packets. + if err := s.sentPacketHandler.OnLossDetectionTimeout(); err != nil { + s.closeLocal(err) + } + } + + if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) { + // send a PING frame since there is no activity in the connection + s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.") + s.framer.QueueControlFrame(&wire.PingFrame{}) + s.keepAlivePingSent = true + } else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() { + s.destroyImpl(qerr.ErrHandshakeTimeout) + continue + } else { + idleTimeoutStartTime := s.idleTimeoutStartTime() + if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) || + (s.handshakeComplete && now.After(s.nextIdleTimeoutTime())) { + s.destroyImpl(qerr.ErrIdleTimeout) + continue + } + } + + if s.sendQueue.WouldBlock() { + // The send queue is still busy sending out packets. + // Wait until there's space to enqueue new packets. + sendQueueAvailable = s.sendQueue.Available() + continue + } + if err := s.triggerSending(now); err != nil { + s.closeLocal(err) + } + if s.sendQueue.WouldBlock() { + sendQueueAvailable = s.sendQueue.Available() + } else { + sendQueueAvailable = nil + } + } + + s.cryptoStreamHandler.Close() + s.sendQueue.Close() // close the send queue before sending the CONNECTION_CLOSE + s.handleCloseError(&closeErr) + if s.tracer != nil && s.tracer.Close != nil { + if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) { + s.tracer.Close() + } + } + s.logger.Infof("Connection %s closed.", s.logID) + s.timer.Stop() + return closeErr.err +} + +// blocks until the early connection can be used +func (s *connection) earlyConnReady() <-chan struct{} { + return s.earlyConnReadyChan +} + +func (s *connection) HandshakeComplete() <-chan struct{} { + return s.handshakeCtx.Done() +} + +func (s *connection) Context() context.Context { + return s.ctx +} + +func (s *connection) supportsDatagrams() bool { + return s.peerParams.MaxDatagramFrameSize > 0 +} + +func (s *connection) ConnectionState() ConnectionState { + s.connStateMutex.Lock() + defer s.connStateMutex.Unlock() + cs := s.cryptoStreamHandler.ConnectionState() + s.connState.TLS = cs.ConnectionState + s.connState.Used0RTT = cs.Used0RTT + s.connState.GSO = s.conn.capabilities().GSO + return s.connState +} + +// Time when the connection should time out +func (s *connection) nextIdleTimeoutTime() time.Time { + idleTimeout := max(s.idleTimeout, s.rttStats.PTO(true)*3) + return s.idleTimeoutStartTime().Add(idleTimeout) +} + +// Time when the next keep-alive packet should be sent. +// It returns a zero time if no keep-alive should be sent. +func (s *connection) nextKeepAliveTime() time.Time { + if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() { + return time.Time{} + } + keepAliveInterval := max(s.keepAliveInterval, s.rttStats.PTO(true)*3/2) + return s.lastPacketReceivedTime.Add(keepAliveInterval) +} + +func (s *connection) maybeResetTimer() { + var deadline time.Time + if !s.handshakeComplete { + deadline = utils.MinTime( + s.creationTime.Add(s.config.handshakeTimeout()), + s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout), + ) + } else { + if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() { + deadline = keepAliveTime + } else { + deadline = s.nextIdleTimeoutTime() + } + } + + s.timer.SetTimer( + deadline, + s.receivedPacketHandler.GetAlarmTimeout(), + s.sentPacketHandler.GetLossDetectionTimeout(), + s.pacingDeadline, + ) +} + +func (s *connection) idleTimeoutStartTime() time.Time { + return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime) +} + +func (s *connection) handleHandshakeComplete() error { + defer s.handshakeCtxCancel() + // Once the handshake completes, we have derived 1-RTT keys. + // There's no point in queueing undecryptable packets for later decryption anymore. + s.undecryptablePackets = nil + + s.connIDManager.SetHandshakeComplete() + s.connIDGenerator.SetHandshakeComplete() + + if s.tracer != nil && s.tracer.ChoseALPN != nil { + s.tracer.ChoseALPN(s.cryptoStreamHandler.ConnectionState().NegotiatedProtocol) + } + + // The server applies transport parameters right away, but the client side has to wait for handshake completion. + // During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets. + if s.perspective == protocol.PerspectiveClient { + s.applyTransportParameters() + return nil + } + + // All these only apply to the server side. + if err := s.handleHandshakeConfirmed(); err != nil { + return err + } + + ticket, err := s.cryptoStreamHandler.GetSessionTicket() + if err != nil { + return err + } + if ticket != nil { // may be nil if session tickets are disabled via tls.Config.SessionTicketsDisabled + s.oneRTTStream.Write(ticket) + for s.oneRTTStream.HasData() { + s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize)) + } + } + token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr()) + if err != nil { + return err + } + s.queueControlFrame(&wire.NewTokenFrame{Token: token}) + s.queueControlFrame(&wire.HandshakeDoneFrame{}) + return nil +} + +func (s *connection) handleHandshakeConfirmed() error { + if err := s.dropEncryptionLevel(protocol.EncryptionHandshake); err != nil { + return err + } + + s.handshakeConfirmed = true + s.sentPacketHandler.SetHandshakeConfirmed() + s.cryptoStreamHandler.SetHandshakeConfirmed() + + if !s.config.DisablePathMTUDiscovery && s.conn.capabilities().DF { + maxPacketSize := s.peerParams.MaxUDPPayloadSize + if maxPacketSize == 0 { + maxPacketSize = protocol.MaxByteCount + } + s.mtuDiscoverer.Start(min(maxPacketSize, protocol.MaxPacketBufferSize)) + } + return nil +} + +func (s *connection) handlePacketImpl(rp receivedPacket) bool { + s.sentPacketHandler.ReceivedBytes(rp.Size()) + + if wire.IsVersionNegotiationPacket(rp.data) { + s.handleVersionNegotiationPacket(rp) + return false + } + + var counter uint8 + var lastConnID protocol.ConnectionID + var processed bool + data := rp.data + p := rp + for len(data) > 0 { + var destConnID protocol.ConnectionID + if counter > 0 { + p = *(p.Clone()) + p.data = data + + var err error + destConnID, err = wire.ParseConnectionID(p.data, s.srcConnIDLen) + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError) + } + s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err) + break + } + if destConnID != lastConnID { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID) + } + s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID) + break + } + } + + if wire.IsLongHeaderPacket(p.data[0]) { + hdr, packetData, rest, err := wire.ParsePacket(p.data) + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + dropReason := logging.PacketDropHeaderParseError + if err == wire.ErrUnsupportedVersion { + dropReason = logging.PacketDropUnsupportedVersion + } + s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), dropReason) + } + s.logger.Debugf("error parsing packet: %s", err) + break + } + lastConnID = hdr.DestConnectionID + + if hdr.Version != s.version { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion) + } + s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version) + break + } + + if counter > 0 { + p.buffer.Split() + } + counter++ + + // only log if this actually a coalesced packet + if s.logger.Debug() && (counter > 1 || len(rest) > 0) { + s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest)) + } + + p.data = packetData + + if wasProcessed := s.handleLongHeaderPacket(p, hdr); wasProcessed { + processed = true + } + data = rest + } else { + if counter > 0 { + p.buffer.Split() + } + processed = s.handleShortHeaderPacket(p, destConnID) + break + } + } + + p.buffer.MaybeRelease() + return processed +} + +func (s *connection) handleShortHeaderPacket(p receivedPacket, destConnID protocol.ConnectionID) bool { + var wasQueued bool + + defer func() { + // Put back the packet buffer if the packet wasn't queued for later decryption. + if !wasQueued { + p.buffer.Decrement() + } + }() + + pn, pnLen, keyPhase, data, err := s.unpacker.UnpackShortHeader(p.rcvTime, p.data) + if err != nil { + wasQueued = s.handleUnpackError(err, p, logging.PacketType1RTT) + return false + } + + if s.logger.Debug() { + s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID) + wire.LogShortHeader(s.logger, destConnID, pn, pnLen, keyPhase) + } + + if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) { + s.logger.Debugf("Dropping (potentially) duplicate packet.") + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketType1RTT, pn, p.Size(), logging.PacketDropDuplicate) + } + return false + } + + var log func([]logging.Frame) + if s.tracer != nil && s.tracer.ReceivedShortHeaderPacket != nil { + log = func(frames []logging.Frame) { + s.tracer.ReceivedShortHeaderPacket( + &logging.ShortHeader{ + DestConnectionID: destConnID, + PacketNumber: pn, + PacketNumberLen: pnLen, + KeyPhase: keyPhase, + }, + p.Size(), + p.ecn, + frames, + ) + } + } + if err := s.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log); err != nil { + s.closeLocal(err) + return false + } + return true +} + +func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ { + var wasQueued bool + + defer func() { + // Put back the packet buffer if the packet wasn't queued for later decryption. + if !wasQueued { + p.buffer.Decrement() + } + }() + + if hdr.Type == protocol.PacketTypeRetry { + return s.handleRetryPacket(hdr, p.data, p.rcvTime) + } + + // The server can change the source connection ID with the first Handshake packet. + // After this, all packets with a different source connection have to be ignored. + if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeInitial, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnknownConnectionID) + } + s.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, s.handshakeDestConnID) + return false + } + // drop 0-RTT packets, if we are a client + if s.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketType0RTT, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropKeyUnavailable) + } + return false + } + + packet, err := s.unpacker.UnpackLongHeader(hdr, p.rcvTime, p.data, s.version) + if err != nil { + wasQueued = s.handleUnpackError(err, p, logging.PacketTypeFromHeader(hdr)) + return false + } + + if s.logger.Debug() { + s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel) + packet.hdr.Log(s.logger) + } + + if pn := packet.hdr.PacketNumber; s.receivedPacketHandler.IsPotentiallyDuplicate(pn, packet.encryptionLevel) { + s.logger.Debugf("Dropping (potentially) duplicate packet.") + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), pn, p.Size(), logging.PacketDropDuplicate) + } + return false + } + + if err := s.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil { + s.closeLocal(err) + return false + } + return true +} + +func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.PacketType) (wasQueued bool) { + switch err { + case handshake.ErrKeysDropped: + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropKeyUnavailable) + } + s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size()) + case handshake.ErrKeysNotYetAvailable: + // Sealer for this encryption level not yet available. + // Try again later. + s.tryQueueingUndecryptablePacket(p, pt) + return true + case wire.ErrInvalidReservedBits: + s.closeLocal(&qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: err.Error(), + }) + case handshake.ErrDecryptionFailed: + // This might be a packet injected by an attacker. Drop it. + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropPayloadDecryptError) + } + s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err) + default: + var headerErr *headerParseError + if errors.As(err, &headerErr) { + // This might be a packet injected by an attacker. Drop it. + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) + } + s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err) + } else { + // This is an error returned by the AEAD (other than ErrDecryptionFailed). + // For example, a PROTOCOL_VIOLATION due to key updates. + s.closeLocal(err) + } + } + return false +} + +func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte, rcvTime time.Time) bool /* was this a valid Retry */ { + if s.perspective == protocol.PerspectiveServer { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) + } + s.logger.Debugf("Ignoring Retry.") + return false + } + if s.receivedFirstPacket { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) + } + s.logger.Debugf("Ignoring Retry, since we already received a packet.") + return false + } + destConnID := s.connIDManager.Get() + if hdr.SrcConnectionID == destConnID { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) + } + s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") + return false + } + // If a token is already set, this means that we already received a Retry from the server. + // Ignore this Retry packet. + if s.receivedRetry { + s.logger.Debugf("Ignoring Retry, since a Retry was already received.") + return false + } + + tag := handshake.GetRetryIntegrityTag(data[:len(data)-16], destConnID, hdr.Version) + if !bytes.Equal(data[len(data)-16:], tag[:]) { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropPayloadDecryptError) + } + s.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.") + return false + } + + if s.logger.Debug() { + s.logger.Debugf("<- Received Retry:") + (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) + s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) + } + if s.tracer != nil && s.tracer.ReceivedRetry != nil { + s.tracer.ReceivedRetry(hdr) + } + newDestConnID := hdr.SrcConnectionID + s.receivedRetry = true + if err := s.sentPacketHandler.ResetForRetry(rcvTime); err != nil { + s.closeLocal(err) + return false + } + s.handshakeDestConnID = newDestConnID + s.retrySrcConnID = &newDestConnID + s.cryptoStreamHandler.ChangeConnectionID(newDestConnID) + s.packer.SetToken(hdr.Token) + s.connIDManager.ChangeInitialConnID(newDestConnID) + s.scheduleSending() + return true +} + +func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { + if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets + s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) + } + return + } + + src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data) + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) + } + s.logger.Debugf("Error parsing Version Negotiation packet: %s", err) + return + } + + for _, v := range supportedVersions { + if v == s.version { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedVersion) + } + // The Version Negotiation packet contains the version that we offered. + // This might be a packet sent by an attacker, or it was corrupted. + return + } + } + + s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions) + if s.tracer != nil && s.tracer.ReceivedVersionNegotiationPacket != nil { + s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions) + } + newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions) + if !ok { + s.destroyImpl(&VersionNegotiationError{ + Ours: s.config.Versions, + Theirs: supportedVersions, + }) + s.logger.Infof("No compatible QUIC version found.") + return + } + if s.tracer != nil && s.tracer.NegotiatedVersion != nil { + s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions) + } + + s.logger.Infof("Switching to QUIC version %s.", newVersion) + nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial) + s.destroyImpl(&errCloseForRecreating{ + nextPacketNumber: nextPN, + nextVersion: newVersion, + }) +} + +func (s *connection) handleUnpackedLongHeaderPacket( + packet *unpackedPacket, + ecn protocol.ECN, + rcvTime time.Time, + packetSize protocol.ByteCount, // only for logging +) error { + if !s.receivedFirstPacket { + s.receivedFirstPacket = true + if !s.versionNegotiated && s.tracer != nil && s.tracer.NegotiatedVersion != nil { + var clientVersions, serverVersions []protocol.Version + switch s.perspective { + case protocol.PerspectiveClient: + clientVersions = s.config.Versions + case protocol.PerspectiveServer: + serverVersions = s.config.Versions + } + s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions) + } + // The server can change the source connection ID with the first Handshake packet. + if s.perspective == protocol.PerspectiveClient && packet.hdr.SrcConnectionID != s.handshakeDestConnID { + cid := packet.hdr.SrcConnectionID + s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid) + s.handshakeDestConnID = cid + s.connIDManager.ChangeInitialConnID(cid) + } + // We create the connection as soon as we receive the first packet from the client. + // We do that before authenticating the packet. + // That means that if the source connection ID was corrupted, + // we might have created a connection with an incorrect source connection ID. + // Once we authenticate the first packet, we need to update it. + if s.perspective == protocol.PerspectiveServer { + if packet.hdr.SrcConnectionID != s.handshakeDestConnID { + s.handshakeDestConnID = packet.hdr.SrcConnectionID + s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) + } + if s.tracer != nil && s.tracer.StartedConnection != nil { + s.tracer.StartedConnection( + s.conn.LocalAddr(), + s.conn.RemoteAddr(), + packet.hdr.SrcConnectionID, + packet.hdr.DestConnectionID, + ) + } + } + } + + if s.perspective == protocol.PerspectiveServer && packet.encryptionLevel == protocol.EncryptionHandshake && + !s.droppedInitialKeys { + // On the server side, Initial keys are dropped as soon as the first Handshake packet is received. + // See Section 4.9.1 of RFC 9001. + if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { + return err + } + } + + s.lastPacketReceivedTime = rcvTime + s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} + s.keepAlivePingSent = false + + var log func([]logging.Frame) + if s.tracer != nil && s.tracer.ReceivedLongHeaderPacket != nil { + log = func(frames []logging.Frame) { + s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, ecn, frames) + } + } + isAckEliciting, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log) + if err != nil { + return err + } + return s.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) +} + +func (s *connection) handleUnpackedShortHeaderPacket( + destConnID protocol.ConnectionID, + pn protocol.PacketNumber, + data []byte, + ecn protocol.ECN, + rcvTime time.Time, + log func([]logging.Frame), +) error { + s.lastPacketReceivedTime = rcvTime + s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} + s.keepAlivePingSent = false + + isAckEliciting, err := s.handleFrames(data, destConnID, protocol.Encryption1RTT, log) + if err != nil { + return err + } + return s.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting) +} + +func (s *connection) handleFrames( + data []byte, + destConnID protocol.ConnectionID, + encLevel protocol.EncryptionLevel, + log func([]logging.Frame), +) (isAckEliciting bool, _ error) { + // Only used for tracing. + // If we're not tracing, this slice will always remain empty. + var frames []logging.Frame + if log != nil { + frames = make([]logging.Frame, 0, 4) + } + handshakeWasComplete := s.handshakeComplete + var handleErr error + for len(data) > 0 { + l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version) + if err != nil { + return false, err + } + data = data[l:] + if frame == nil { + break + } + if ackhandler.IsFrameAckEliciting(frame) { + isAckEliciting = true + } + if log != nil { + frames = append(frames, logutils.ConvertFrame(frame)) + } + // An error occurred handling a previous frame. + // Don't handle the current frame. + if handleErr != nil { + continue + } + if err := s.handleFrame(frame, encLevel, destConnID); err != nil { + if log == nil { + return false, err + } + // If we're logging, we need to keep parsing (but not handling) all frames. + handleErr = err + } + } + + if log != nil { + log(frames) + if handleErr != nil { + return false, handleErr + } + } + + // Handle completion of the handshake after processing all the frames. + // This ensures that we correctly handle the following case on the server side: + // We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake, + // and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame. + if !handshakeWasComplete && s.handshakeComplete { + if err := s.handleHandshakeComplete(); err != nil { + return false, err + } + } + + return +} + +func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error { + var err error + wire.LogFrame(s.logger, f, false) + switch frame := f.(type) { + case *wire.CryptoFrame: + err = s.handleCryptoFrame(frame, encLevel) + case *wire.StreamFrame: + err = s.handleStreamFrame(frame) + case *wire.AckFrame: + err = s.handleAckFrame(frame, encLevel) + case *wire.ConnectionCloseFrame: + s.handleConnectionCloseFrame(frame) + case *wire.ResetStreamFrame: + err = s.handleResetStreamFrame(frame) + case *wire.MaxDataFrame: + s.handleMaxDataFrame(frame) + case *wire.MaxStreamDataFrame: + err = s.handleMaxStreamDataFrame(frame) + case *wire.MaxStreamsFrame: + s.handleMaxStreamsFrame(frame) + case *wire.DataBlockedFrame: + case *wire.StreamDataBlockedFrame: + case *wire.StreamsBlockedFrame: + case *wire.StopSendingFrame: + err = s.handleStopSendingFrame(frame) + case *wire.PingFrame: + case *wire.PathChallengeFrame: + s.handlePathChallengeFrame(frame) + case *wire.PathResponseFrame: + // since we don't send PATH_CHALLENGEs, we don't expect PATH_RESPONSEs + err = errors.New("unexpected PATH_RESPONSE frame") + case *wire.NewTokenFrame: + err = s.handleNewTokenFrame(frame) + case *wire.NewConnectionIDFrame: + err = s.handleNewConnectionIDFrame(frame) + case *wire.RetireConnectionIDFrame: + err = s.handleRetireConnectionIDFrame(frame, destConnID) + case *wire.HandshakeDoneFrame: + err = s.handleHandshakeDoneFrame() + case *wire.DatagramFrame: + err = s.handleDatagramFrame(frame) + default: + err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name()) + } + return err +} + +// handlePacket is called by the server with a new packet +func (s *connection) handlePacket(p receivedPacket) { + // Discard packets once the amount of queued packets is larger than + // the channel size, protocol.MaxConnUnprocessedPackets + select { + case s.receivedPackets <- p: + default: + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) + } + } +} + +func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) { + if frame.IsApplicationError { + s.closeRemote(&qerr.ApplicationError{ + Remote: true, + ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode), + ErrorMessage: frame.ReasonPhrase, + }) + return + } + s.closeRemote(&qerr.TransportError{ + Remote: true, + ErrorCode: qerr.TransportErrorCode(frame.ErrorCode), + FrameType: frame.FrameType, + ErrorMessage: frame.ReasonPhrase, + }) +} + +func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { + if err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel); err != nil { + return err + } + return s.handleHandshakeEvents() +} + +func (s *connection) handleHandshakeEvents() error { + for { + ev := s.cryptoStreamHandler.NextEvent() + var err error + switch ev.Kind { + case handshake.EventNoEvent: + return nil + case handshake.EventHandshakeComplete: + // Don't call handleHandshakeComplete yet. + // It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first. + s.handshakeComplete = true + case handshake.EventReceivedTransportParameters: + err = s.handleTransportParameters(ev.TransportParameters) + case handshake.EventRestoredTransportParameters: + s.restoreTransportParameters(ev.TransportParameters) + close(s.earlyConnReadyChan) + case handshake.EventReceivedReadKeys: + // Queue all packets for decryption that have been undecryptable so far. + s.undecryptablePacketsToProcess = s.undecryptablePackets + s.undecryptablePackets = nil + case handshake.EventDiscard0RTTKeys: + err = s.dropEncryptionLevel(protocol.Encryption0RTT) + case handshake.EventWriteInitialData: + _, err = s.initialStream.Write(ev.Data) + case handshake.EventWriteHandshakeData: + _, err = s.handshakeStream.Write(ev.Data) + } + if err != nil { + return err + } + } +} + +func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error { + str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) + if err != nil { + return err + } + if str == nil { + // Stream is closed and already garbage collected + // ignore this StreamFrame + return nil + } + return str.handleStreamFrame(frame) +} + +func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) { + s.connFlowController.UpdateSendWindow(frame.MaximumData) +} + +func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { + str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) + if err != nil { + return err + } + if str == nil { + // stream is closed and already garbage collected + return nil + } + str.updateSendWindow(frame.MaximumStreamData) + return nil +} + +func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { + s.streamsMap.HandleMaxStreamsFrame(frame) +} + +func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { + str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) + if err != nil { + return err + } + if str == nil { + // stream is closed and already garbage collected + return nil + } + return str.handleResetStreamFrame(frame) +} + +func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error { + str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) + if err != nil { + return err + } + if str == nil { + // stream is closed and already garbage collected + return nil + } + str.handleStopSendingFrame(frame) + return nil +} + +func (s *connection) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { + s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data}) +} + +func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error { + if s.perspective == protocol.PerspectiveServer { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "received NEW_TOKEN frame from the client", + } + } + if s.config.TokenStore != nil { + s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token}) + } + return nil +} + +func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { + return s.connIDManager.Add(f) +} + +func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { + return s.connIDGenerator.Retire(f.SequenceNumber, destConnID) +} + +func (s *connection) handleHandshakeDoneFrame() error { + if s.perspective == protocol.PerspectiveServer { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "received a HANDSHAKE_DONE frame", + } + } + if !s.handshakeConfirmed { + return s.handleHandshakeConfirmed() + } + return nil +} + +func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error { + acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime) + if err != nil { + return err + } + if !acked1RTTPacket { + return nil + } + // On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake. + // This is only possible if the ACK was sent in a 1-RTT packet. + // This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001. + if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed { + if err := s.handleHandshakeConfirmed(); err != nil { + return err + } + } + return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) +} + +func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error { + if f.Length(s.version) > wire.MaxDatagramSize { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "DATAGRAM frame too large", + } + } + s.datagramQueue.HandleDatagramFrame(f) + return nil +} + +// closeLocal closes the connection and send a CONNECTION_CLOSE containing the error +func (s *connection) closeLocal(e error) { + s.closeOnce.Do(func() { + if e == nil { + s.logger.Infof("Closing connection.") + } else { + s.logger.Errorf("Closing connection with error: %s", e) + } + s.closeChan <- closeError{err: e, immediate: false, remote: false} + }) +} + +// destroy closes the connection without sending the error on the wire +func (s *connection) destroy(e error) { + s.destroyImpl(e) + <-s.ctx.Done() +} + +func (s *connection) destroyImpl(e error) { + s.closeOnce.Do(func() { + if nerr, ok := e.(net.Error); ok && nerr.Timeout() { + s.logger.Errorf("Destroying connection: %s", e) + } else { + s.logger.Errorf("Destroying connection with error: %s", e) + } + s.closeChan <- closeError{err: e, immediate: true, remote: false} + }) +} + +func (s *connection) closeRemote(e error) { + s.closeOnce.Do(func() { + s.logger.Errorf("Peer closed connection with error: %s", e) + s.closeChan <- closeError{err: e, immediate: true, remote: true} + }) +} + +func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error { + s.closeLocal(&qerr.ApplicationError{ + ErrorCode: code, + ErrorMessage: desc, + }) + <-s.ctx.Done() + return nil +} + +func (s *connection) closeWithTransportError(code TransportErrorCode) { + s.closeLocal(&qerr.TransportError{ErrorCode: code}) + <-s.ctx.Done() +} + +func (s *connection) handleCloseError(closeErr *closeError) { + e := closeErr.err + if e == nil { + e = &qerr.ApplicationError{} + } else { + defer func() { + closeErr.err = e + }() + } + + var ( + statelessResetErr *StatelessResetError + versionNegotiationErr *VersionNegotiationError + recreateErr *errCloseForRecreating + applicationErr *ApplicationError + transportErr *TransportError + ) + switch { + case errors.Is(e, qerr.ErrIdleTimeout), + errors.Is(e, qerr.ErrHandshakeTimeout), + errors.As(e, &statelessResetErr), + errors.As(e, &versionNegotiationErr), + errors.As(e, &recreateErr), + errors.As(e, &applicationErr), + errors.As(e, &transportErr): + default: + e = &qerr.TransportError{ + ErrorCode: qerr.InternalError, + ErrorMessage: e.Error(), + } + } + + s.streamsMap.CloseWithError(e) + s.connIDManager.Close() + if s.datagramQueue != nil { + s.datagramQueue.CloseWithError(e) + } + + if s.tracer != nil && s.tracer.ClosedConnection != nil && !errors.As(e, &recreateErr) { + s.tracer.ClosedConnection(e) + } + + // If this is a remote close we're done here + if closeErr.remote { + s.connIDGenerator.ReplaceWithClosed(nil) + return + } + if closeErr.immediate { + s.connIDGenerator.RemoveAll() + return + } + // Don't send out any CONNECTION_CLOSE if this is an error that occurred + // before we even sent out the first packet. + if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket { + s.connIDGenerator.RemoveAll() + return + } + connClosePacket, err := s.sendConnectionClose(e) + if err != nil { + s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) + } + s.connIDGenerator.ReplaceWithClosed(connClosePacket) +} + +func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) error { + if s.tracer != nil && s.tracer.DroppedEncryptionLevel != nil { + s.tracer.DroppedEncryptionLevel(encLevel) + } + s.sentPacketHandler.DropPackets(encLevel) + s.receivedPacketHandler.DropPackets(encLevel) + //nolint:exhaustive // only Initial and 0-RTT need special treatment + switch encLevel { + case protocol.EncryptionInitial: + s.droppedInitialKeys = true + s.cryptoStreamHandler.DiscardInitialKeys() + case protocol.Encryption0RTT: + s.streamsMap.ResetFor0RTT() + if err := s.connFlowController.Reset(); err != nil { + return err + } + return s.framer.Handle0RTTRejection() + } + return s.cryptoStreamManager.Drop(encLevel) +} + +// is called for the client, when restoring transport parameters saved for 0-RTT +func (s *connection) restoreTransportParameters(params *wire.TransportParameters) { + if s.logger.Debug() { + s.logger.Debugf("Restoring Transport Parameters: %s", params) + } + + s.peerParams = params + s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) + s.connFlowController.UpdateSendWindow(params.InitialMaxData) + s.streamsMap.UpdateLimits(params) + s.connStateMutex.Lock() + s.connState.SupportsDatagrams = s.supportsDatagrams() + s.connStateMutex.Unlock() +} + +func (s *connection) handleTransportParameters(params *wire.TransportParameters) error { + if s.tracer != nil && s.tracer.ReceivedTransportParameters != nil { + s.tracer.ReceivedTransportParameters(params) + } + if err := s.checkTransportParameters(params); err != nil { + return &qerr.TransportError{ + ErrorCode: qerr.TransportParameterError, + ErrorMessage: err.Error(), + } + } + + if s.perspective == protocol.PerspectiveClient && s.peerParams != nil && s.ConnectionState().Used0RTT && !params.ValidForUpdate(s.peerParams) { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "server sent reduced limits after accepting 0-RTT data", + } + } + + s.peerParams = params + // On the client side we have to wait for handshake completion. + // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets. + if s.perspective == protocol.PerspectiveServer { + s.applyTransportParameters() + // On the server side, the early connection is ready as soon as we processed + // the client's transport parameters. + close(s.earlyConnReadyChan) + } + + s.connStateMutex.Lock() + s.connState.SupportsDatagrams = s.supportsDatagrams() + s.connStateMutex.Unlock() + return nil +} + +func (s *connection) checkTransportParameters(params *wire.TransportParameters) error { + if s.logger.Debug() { + s.logger.Debugf("Processed Transport Parameters: %s", params) + } + + // check the initial_source_connection_id + if params.InitialSourceConnectionID != s.handshakeDestConnID { + return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID) + } + + if s.perspective == protocol.PerspectiveServer { + return nil + } + // check the original_destination_connection_id + if params.OriginalDestinationConnectionID != s.origDestConnID { + return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID) + } + if s.retrySrcConnID != nil { // a Retry was performed + if params.RetrySourceConnectionID == nil { + return errors.New("missing retry_source_connection_id") + } + if *params.RetrySourceConnectionID != *s.retrySrcConnID { + return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID) + } + } else if params.RetrySourceConnectionID != nil { + return errors.New("received retry_source_connection_id, although no Retry was performed") + } + return nil +} + +func (s *connection) applyTransportParameters() { + params := s.peerParams + // Our local idle timeout will always be > 0. + s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout) + s.keepAliveInterval = min(s.config.KeepAlivePeriod, min(s.idleTimeout/2, protocol.MaxKeepAliveInterval)) + s.streamsMap.UpdateLimits(params) + s.frameParser.SetAckDelayExponent(params.AckDelayExponent) + s.connFlowController.UpdateSendWindow(params.InitialMaxData) + s.rttStats.SetMaxAckDelay(params.MaxAckDelay) + s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) + if params.StatelessResetToken != nil { + s.connIDManager.SetStatelessResetToken(*params.StatelessResetToken) + } + // We don't support connection migration yet, so we don't have any use for the preferred_address. + if params.PreferredAddress != nil { + // Retire the connection ID. + s.connIDManager.AddFromPreferredAddress(params.PreferredAddress.ConnectionID, params.PreferredAddress.StatelessResetToken) + } +} + +func (s *connection) triggerSending(now time.Time) error { + s.pacingDeadline = time.Time{} + + sendMode := s.sentPacketHandler.SendMode(now) + //nolint:exhaustive // No need to handle pacing limited here. + switch sendMode { + case ackhandler.SendAny: + return s.sendPackets(now) + case ackhandler.SendNone: + return nil + case ackhandler.SendPacingLimited: + deadline := s.sentPacketHandler.TimeUntilSend() + if deadline.IsZero() { + deadline = deadlineSendImmediately + } + s.pacingDeadline = deadline + // Allow sending of an ACK if we're pacing limit. + // This makes sure that a peer that is mostly receiving data (and thus has an inaccurate cwnd estimate) + // sends enough ACKs to allow its peer to utilize the bandwidth. + fallthrough + case ackhandler.SendAck: + // We can at most send a single ACK only packet. + // There will only be a new ACK after receiving new packets. + // SendAck is only returned when we're congestion limited, so we don't need to set the pacinggs timer. + return s.maybeSendAckOnlyPacket(now) + case ackhandler.SendPTOInitial: + if err := s.sendProbePacket(protocol.EncryptionInitial, now); err != nil { + return err + } + if s.sendQueue.WouldBlock() { + s.scheduleSending() + return nil + } + return s.triggerSending(now) + case ackhandler.SendPTOHandshake: + if err := s.sendProbePacket(protocol.EncryptionHandshake, now); err != nil { + return err + } + if s.sendQueue.WouldBlock() { + s.scheduleSending() + return nil + } + return s.triggerSending(now) + case ackhandler.SendPTOAppData: + if err := s.sendProbePacket(protocol.Encryption1RTT, now); err != nil { + return err + } + if s.sendQueue.WouldBlock() { + s.scheduleSending() + return nil + } + return s.triggerSending(now) + default: + return fmt.Errorf("BUG: invalid send mode %d", sendMode) + } +} + +func (s *connection) sendPackets(now time.Time) error { + // Path MTU Discovery + // Can't use GSO, since we need to send a single packet that's larger than our current maximum size. + // Performance-wise, this doesn't matter, since we only send a very small (<10) number of + // MTU probe packets per connection. + if s.handshakeConfirmed && s.mtuDiscoverer != nil && s.mtuDiscoverer.ShouldSendProbe(now) { + ping, size := s.mtuDiscoverer.GetPing() + p, buf, err := s.packer.PackMTUProbePacket(ping, size, s.version) + if err != nil { + return err + } + ecn := s.sentPacketHandler.ECNMode(true) + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + s.registerPackedShortHeaderPacket(p, ecn, now) + s.sendQueue.Send(buf, 0, ecn) + // This is kind of a hack. We need to trigger sending again somehow. + s.pacingDeadline = deadlineSendImmediately + return nil + } + + if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked { + s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset}) + } + s.windowUpdateQueue.QueueAll() + if cf := s.cryptoStreamManager.GetPostHandshakeData(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { + s.queueControlFrame(cf) + } + + if !s.handshakeConfirmed { + packet, err := s.packer.PackCoalescedPacket(false, s.mtuDiscoverer.CurrentSize(), s.version) + if err != nil || packet == nil { + return err + } + s.sentFirstPacket = true + if err := s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now); err != nil { + return err + } + sendMode := s.sentPacketHandler.SendMode(now) + if sendMode == ackhandler.SendPacingLimited { + s.resetPacingDeadline() + } else if sendMode == ackhandler.SendAny { + s.pacingDeadline = deadlineSendImmediately + } + return nil + } + + if s.conn.capabilities().GSO { + return s.sendPacketsWithGSO(now) + } + return s.sendPacketsWithoutGSO(now) +} + +func (s *connection) sendPacketsWithoutGSO(now time.Time) error { + for { + buf := getPacketBuffer() + ecn := s.sentPacketHandler.ECNMode(true) + if _, err := s.appendOneShortHeaderPacket(buf, s.mtuDiscoverer.CurrentSize(), ecn, now); err != nil { + if err == errNothingToPack { + buf.Release() + return nil + } + return err + } + + s.sendQueue.Send(buf, 0, ecn) + + if s.sendQueue.WouldBlock() { + return nil + } + sendMode := s.sentPacketHandler.SendMode(now) + if sendMode == ackhandler.SendPacingLimited { + s.resetPacingDeadline() + return nil + } + if sendMode != ackhandler.SendAny { + return nil + } + // Prioritize receiving of packets over sending out more packets. + if len(s.receivedPackets) > 0 { + s.pacingDeadline = deadlineSendImmediately + return nil + } + } +} + +func (s *connection) sendPacketsWithGSO(now time.Time) error { + buf := getLargePacketBuffer() + maxSize := s.mtuDiscoverer.CurrentSize() + + ecn := s.sentPacketHandler.ECNMode(true) + for { + var dontSendMore bool + size, err := s.appendOneShortHeaderPacket(buf, maxSize, ecn, now) + if err != nil { + if err != errNothingToPack { + return err + } + if buf.Len() == 0 { + buf.Release() + return nil + } + dontSendMore = true + } + + if !dontSendMore { + sendMode := s.sentPacketHandler.SendMode(now) + if sendMode == ackhandler.SendPacingLimited { + s.resetPacingDeadline() + } + if sendMode != ackhandler.SendAny { + dontSendMore = true + } + } + + // Don't send more packets in this batch if they require a different ECN marking than the previous ones. + nextECN := s.sentPacketHandler.ECNMode(true) + + // Append another packet if + // 1. The congestion controller and pacer allow sending more + // 2. The last packet appended was a full-size packet + // 3. The next packet will have the same ECN marking + // 4. We still have enough space for another full-size packet in the buffer + if !dontSendMore && size == maxSize && nextECN == ecn && buf.Len()+maxSize <= buf.Cap() { + continue + } + + s.sendQueue.Send(buf, uint16(maxSize), ecn) + + if dontSendMore { + return nil + } + if s.sendQueue.WouldBlock() { + return nil + } + + // Prioritize receiving of packets over sending out more packets. + if len(s.receivedPackets) > 0 { + s.pacingDeadline = deadlineSendImmediately + return nil + } + + buf = getLargePacketBuffer() + } +} + +func (s *connection) resetPacingDeadline() { + deadline := s.sentPacketHandler.TimeUntilSend() + if deadline.IsZero() { + deadline = deadlineSendImmediately + } + s.pacingDeadline = deadline +} + +func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { + if !s.handshakeConfirmed { + ecn := s.sentPacketHandler.ECNMode(false) + packet, err := s.packer.PackCoalescedPacket(true, s.mtuDiscoverer.CurrentSize(), s.version) + if err != nil { + return err + } + if packet == nil { + return nil + } + return s.sendPackedCoalescedPacket(packet, ecn, now) + } + + ecn := s.sentPacketHandler.ECNMode(true) + p, buf, err := s.packer.PackAckOnlyPacket(s.mtuDiscoverer.CurrentSize(), s.version) + if err != nil { + if err == errNothingToPack { + return nil + } + return err + } + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + s.registerPackedShortHeaderPacket(p, ecn, now) + s.sendQueue.Send(buf, 0, ecn) + return nil +} + +func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel, now time.Time) error { + // Queue probe packets until we actually send out a packet, + // or until there are no more packets to queue. + var packet *coalescedPacket + for { + if wasQueued := s.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued { + break + } + var err error + packet, err = s.packer.MaybePackProbePacket(encLevel, s.mtuDiscoverer.CurrentSize(), s.version) + if err != nil { + return err + } + if packet != nil { + break + } + } + if packet == nil { + s.retransmissionQueue.AddPing(encLevel) + var err error + packet, err = s.packer.MaybePackProbePacket(encLevel, s.mtuDiscoverer.CurrentSize(), s.version) + if err != nil { + return err + } + } + if packet == nil || (len(packet.longHdrPackets) == 0 && packet.shortHdrPacket == nil) { + return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel) + } + return s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now) +} + +// appendOneShortHeaderPacket appends a new packet to the given packetBuffer. +// If there was nothing to pack, the returned size is 0. +func (s *connection) appendOneShortHeaderPacket(buf *packetBuffer, maxSize protocol.ByteCount, ecn protocol.ECN, now time.Time) (protocol.ByteCount, error) { + startLen := buf.Len() + p, err := s.packer.AppendPacket(buf, maxSize, s.version) + if err != nil { + return 0, err + } + size := buf.Len() - startLen + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false) + s.registerPackedShortHeaderPacket(p, ecn, now) + return size, nil +} + +func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, now time.Time) { + if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && (len(p.StreamFrames) > 0 || ackhandler.HasAckElicitingFrames(p.Frames)) { + s.firstAckElicitingPacketAfterIdleSentTime = now + } + + largestAcked := protocol.InvalidPacketNumber + if p.Ack != nil { + largestAcked = p.Ack.LargestAcked() + } + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) + s.connIDManager.SentPacket() +} + +func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN, now time.Time) error { + s.logCoalescedPacket(packet, ecn) + for _, p := range packet.longHdrPackets { + if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { + s.firstAckElicitingPacketAfterIdleSentTime = now + } + largestAcked := protocol.InvalidPacketNumber + if p.ack != nil { + largestAcked = p.ack.LargestAcked() + } + s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), ecn, p.length, false) + if s.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake && + !s.droppedInitialKeys { + // On the client side, Initial keys are dropped as soon as the first Handshake packet is sent. + // See Section 4.9.1 of RFC 9001. + if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { + return err + } + } + } + if p := packet.shortHdrPacket; p != nil { + if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { + s.firstAckElicitingPacketAfterIdleSentTime = now + } + largestAcked := protocol.InvalidPacketNumber + if p.Ack != nil { + largestAcked = p.Ack.LargestAcked() + } + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) + } + s.connIDManager.SentPacket() + s.sendQueue.Send(packet.buffer, 0, ecn) + return nil +} + +func (s *connection) sendConnectionClose(e error) ([]byte, error) { + var packet *coalescedPacket + var err error + var transportErr *qerr.TransportError + var applicationErr *qerr.ApplicationError + if errors.As(e, &transportErr) { + packet, err = s.packer.PackConnectionClose(transportErr, s.mtuDiscoverer.CurrentSize(), s.version) + } else if errors.As(e, &applicationErr) { + packet, err = s.packer.PackApplicationClose(applicationErr, s.mtuDiscoverer.CurrentSize(), s.version) + } else { + packet, err = s.packer.PackConnectionClose(&qerr.TransportError{ + ErrorCode: qerr.InternalError, + ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()), + }, s.mtuDiscoverer.CurrentSize(), s.version) + } + if err != nil { + return nil, err + } + ecn := s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()) + s.logCoalescedPacket(packet, ecn) + return packet.buffer.Data, s.conn.Write(packet.buffer.Data, 0, ecn) +} + +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, logutils.ConvertFrame(f.Frame)) + } + for _, f := range p.streamFrames { + frames = append(frames, logutils.ConvertFrame(f.Frame)) + } + var ack *logging.AckFrame + if p.ack != nil { + ack = logutils.ConvertAckFrame(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, logutils.ConvertFrame(f.Frame)) + } + for _, f := range streamFrames { + fs = append(fs, logutils.ConvertFrame(f.Frame)) + } + var ack *logging.AckFrame + if ackFrame != nil { + ack = logutils.ConvertAckFrame(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) + } +} + +// AcceptStream returns the next stream openend by the peer +func (s *connection) AcceptStream(ctx context.Context) (Stream, error) { + return s.streamsMap.AcceptStream(ctx) +} + +func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { + return s.streamsMap.AcceptUniStream(ctx) +} + +// OpenStream opens a stream +func (s *connection) OpenStream() (Stream, error) { + return s.streamsMap.OpenStream() +} + +func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) { + return s.streamsMap.OpenStreamSync(ctx) +} + +func (s *connection) OpenUniStream() (SendStream, error) { + return s.streamsMap.OpenUniStream() +} + +func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) { + return s.streamsMap.OpenUniStreamSync(ctx) +} + +func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { + initialSendWindow := s.peerParams.InitialMaxStreamDataUni + if id.Type() == protocol.StreamTypeBidi { + if id.InitiatedBy() == s.perspective { + initialSendWindow = s.peerParams.InitialMaxStreamDataBidiRemote + } else { + initialSendWindow = s.peerParams.InitialMaxStreamDataBidiLocal + } + } + return flowcontrol.NewStreamFlowController( + id, + s.connFlowController, + protocol.ByteCount(s.config.InitialStreamReceiveWindow), + protocol.ByteCount(s.config.MaxStreamReceiveWindow), + initialSendWindow, + s.onHasStreamWindowUpdate, + s.rttStats, + s.logger, + ) +} + +// scheduleSending signals that we have data for sending +func (s *connection) scheduleSending() { + select { + case s.sendingScheduled <- struct{}{}: + default: + } +} + +// tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys. +// The logging.PacketType is only used for logging purposes. +func (s *connection) tryQueueingUndecryptablePacket(p receivedPacket, pt logging.PacketType) { + if s.handshakeComplete { + panic("shouldn't queue undecryptable packets after handshake completion") + } + if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) + } + s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size()) + return + } + s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size()) + if s.tracer != nil && s.tracer.BufferedPacket != nil { + s.tracer.BufferedPacket(pt, p.Size()) + } + s.undecryptablePackets = append(s.undecryptablePackets, p) +} + +func (s *connection) queueControlFrame(f wire.Frame) { + s.framer.QueueControlFrame(f) + s.scheduleSending() +} + +func (s *connection) onHasStreamWindowUpdate(id protocol.StreamID) { + s.windowUpdateQueue.AddStream(id) + s.scheduleSending() +} + +func (s *connection) onHasConnectionWindowUpdate() { + s.windowUpdateQueue.AddConnection() + s.scheduleSending() +} + +func (s *connection) onHasStreamData(id protocol.StreamID) { + s.framer.AddActiveStream(id) + s.scheduleSending() +} + +func (s *connection) onStreamCompleted(id protocol.StreamID) { + if err := s.streamsMap.DeleteStream(id); err != nil { + s.closeLocal(err) + } +} + +func (s *connection) SendDatagram(p []byte) error { + if !s.supportsDatagrams() { + return errors.New("datagram support disabled") + } + + f := &wire.DatagramFrame{DataLenPresent: true} + if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) { + return &DatagramTooLargeError{ + PeerMaxDatagramFrameSize: int64(s.peerParams.MaxDatagramFrameSize), + } + } + f.Data = make([]byte, len(p)) + copy(f.Data, p) + return s.datagramQueue.Add(f) +} + +func (s *connection) ReceiveDatagram(ctx context.Context) ([]byte, error) { + if !s.config.EnableDatagrams { + return nil, errors.New("datagram support disabled") + } + return s.datagramQueue.Receive(ctx) +} + +func (s *connection) LocalAddr() net.Addr { + return s.conn.LocalAddr() +} + +func (s *connection) RemoteAddr() net.Addr { + return s.conn.RemoteAddr() +} + +func (s *connection) GetVersion() protocol.Version { + return s.version +} + +func (s *connection) NextConnection() Connection { + <-s.HandshakeComplete() + s.streamsMap.UseResetMaps() + return s +} diff --git a/vendor/github.com/quic-go/quic-go/connection_timer.go b/vendor/github.com/quic-go/quic-go/connection_timer.go new file mode 100644 index 000000000..171fdd013 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/connection_timer.go @@ -0,0 +1,51 @@ +package quic + +import ( + "time" + + "github.com/quic-go/quic-go/internal/utils" +) + +var deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine + +type connectionTimer struct { + timer *utils.Timer + last time.Time +} + +func newTimer() *connectionTimer { + return &connectionTimer{timer: utils.NewTimer()} +} + +func (t *connectionTimer) SetRead() { + if deadline := t.timer.Deadline(); deadline != deadlineSendImmediately { + t.last = deadline + } + t.timer.SetRead() +} + +func (t *connectionTimer) Chan() <-chan time.Time { + return t.timer.Chan() +} + +// SetTimer resets the timer. +// It makes sure that the deadline is strictly increasing. +// This prevents busy-looping in cases where the timer fires, but we can't actually send out a packet. +// This doesn't apply to the pacing deadline, which can be set multiple times to deadlineSendImmediately. +func (t *connectionTimer) SetTimer(idleTimeoutOrKeepAlive, ackAlarm, lossTime, pacing time.Time) { + deadline := idleTimeoutOrKeepAlive + if !ackAlarm.IsZero() && ackAlarm.Before(deadline) && ackAlarm.After(t.last) { + deadline = ackAlarm + } + if !lossTime.IsZero() && lossTime.Before(deadline) && lossTime.After(t.last) { + deadline = lossTime + } + if !pacing.IsZero() && pacing.Before(deadline) { + deadline = pacing + } + t.timer.Reset(deadline) +} + +func (t *connectionTimer) Stop() { + t.timer.Stop() +} diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream.go b/vendor/github.com/quic-go/quic-go/crypto_stream.go new file mode 100644 index 000000000..abc7ddcf8 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/crypto_stream.go @@ -0,0 +1,106 @@ +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 + + highestOffset protocol.ByteCount + finished bool + + writeOffset protocol.ByteCount + writeBuf []byte +} + +func newCryptoStream() cryptoStream { + return &cryptoStreamImpl{queue: newFrameSorter()} +} + +func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error { + highestOffset := f.Offset + protocol.ByteCount(len(f.Data)) + if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset { + return &qerr.TransportError{ + ErrorCode: qerr.CryptoBufferExceeded, + ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset), + } + } + if s.finished { + if highestOffset > s.highestOffset { + // reject crypto data received after this stream was already finished + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "received crypto data after change of encryption level", + } + } + // ignore data with a smaller offset than the highest received + // could e.g. be a retransmission + 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...) + } +} + +// GetCryptoData retrieves data that was received in CRYPTO frames +func (s *cryptoStreamImpl) GetCryptoData() []byte { + b := s.msgBuf + s.msgBuf = nil + return b +} + +func (s *cryptoStreamImpl) Finish() error { + if s.queue.HasMoreData() { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "encryption level changed, but crypto stream has more data to read", + } + } + s.finished = true + return nil +} + +// Writes writes data that should be sent out in CRYPTO frames +func (s *cryptoStreamImpl) Write(p []byte) (int, error) { + s.writeBuf = append(s.writeBuf, p...) + return len(p), nil +} + +func (s *cryptoStreamImpl) HasData() bool { + return len(s.writeBuf) > 0 +} + +func (s *cryptoStreamImpl) 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] + s.writeBuf = s.writeBuf[n:] + s.writeOffset += n + return f +} diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go new file mode 100644 index 000000000..c48e238a7 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go @@ -0,0 +1,82 @@ +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 +} + +func newCryptoStreamManager( + cryptoHandler cryptoDataHandler, + initialStream cryptoStream, + handshakeStream cryptoStream, + oneRTTStream cryptoStream, +) *cryptoStreamManager { + return &cryptoStreamManager{ + cryptoHandler: cryptoHandler, + initialStream: initialStream, + handshakeStream: handshakeStream, + oneRTTStream: oneRTTStream, + } +} + +func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { + 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: + 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 + } + } +} + +func (m *cryptoStreamManager) GetPostHandshakeData(maxSize protocol.ByteCount) *wire.CryptoFrame { + if !m.oneRTTStream.HasData() { + return nil + } + return m.oneRTTStream.PopCryptoFrame(maxSize) +} + +func (m *cryptoStreamManager) Drop(encLevel protocol.EncryptionLevel) error { + //nolint:exhaustive // 1-RTT keys should never get dropped. + switch encLevel { + case protocol.EncryptionInitial: + return m.initialStream.Finish() + case protocol.EncryptionHandshake: + return m.handshakeStream.Finish() + default: + panic(fmt.Sprintf("dropped unexpected encryption level: %s", encLevel)) + } +} diff --git a/vendor/github.com/quic-go/quic-go/datagram_queue.go b/vendor/github.com/quic-go/quic-go/datagram_queue.go new file mode 100644 index 000000000..e26285b26 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/datagram_queue.go @@ -0,0 +1,137 @@ +package quic + +import ( + "context" + "sync" + + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/utils/ringbuffer" + "github.com/quic-go/quic-go/internal/wire" +) + +const ( + maxDatagramSendQueueLen = 32 + maxDatagramRcvQueueLen = 128 +) + +type datagramQueue struct { + sendMx sync.Mutex + sendQueue ringbuffer.RingBuffer[*wire.DatagramFrame] + sent chan struct{} // used to notify Add that a datagram was dequeued + + rcvMx sync.Mutex + rcvQueue [][]byte + rcvd chan struct{} // used to notify Receive that a new datagram was received + + closeErr error + closed chan struct{} + + hasData func() + + logger utils.Logger +} + +func newDatagramQueue(hasData func(), logger utils.Logger) *datagramQueue { + return &datagramQueue{ + hasData: hasData, + rcvd: make(chan struct{}, 1), + sent: make(chan struct{}, 1), + closed: make(chan struct{}), + logger: logger, + } +} + +// Add queues a new DATAGRAM frame for sending. +// Up to 32 DATAGRAM frames will be queued. +// Once that limit is reached, Add blocks until the queue size has reduced. +func (h *datagramQueue) Add(f *wire.DatagramFrame) error { + h.sendMx.Lock() + + for { + if h.sendQueue.Len() < maxDatagramSendQueueLen { + h.sendQueue.PushBack(f) + h.sendMx.Unlock() + h.hasData() + return nil + } + select { + case <-h.sent: // drain the queue so we don't loop immediately + default: + } + h.sendMx.Unlock() + select { + case <-h.closed: + return h.closeErr + case <-h.sent: + } + h.sendMx.Lock() + } +} + +// Peek gets the next DATAGRAM frame for sending. +// If actually sent out, Pop needs to be called before the next call to Peek. +func (h *datagramQueue) Peek() *wire.DatagramFrame { + h.sendMx.Lock() + defer h.sendMx.Unlock() + if h.sendQueue.Empty() { + return nil + } + return h.sendQueue.PeekFront() +} + +func (h *datagramQueue) Pop() { + h.sendMx.Lock() + defer h.sendMx.Unlock() + _ = h.sendQueue.PopFront() + select { + case h.sent <- struct{}{}: + default: + } +} + +// HandleDatagramFrame handles a received DATAGRAM frame. +func (h *datagramQueue) HandleDatagramFrame(f *wire.DatagramFrame) { + data := make([]byte, len(f.Data)) + copy(data, f.Data) + var queued bool + h.rcvMx.Lock() + if len(h.rcvQueue) < maxDatagramRcvQueueLen { + h.rcvQueue = append(h.rcvQueue, data) + queued = true + select { + case h.rcvd <- struct{}{}: + default: + } + } + h.rcvMx.Unlock() + if !queued && h.logger.Debug() { + h.logger.Debugf("Discarding received DATAGRAM frame (%d bytes payload)", len(f.Data)) + } +} + +// Receive gets a received DATAGRAM frame. +func (h *datagramQueue) Receive(ctx context.Context) ([]byte, error) { + for { + h.rcvMx.Lock() + if len(h.rcvQueue) > 0 { + data := h.rcvQueue[0] + h.rcvQueue = h.rcvQueue[1:] + h.rcvMx.Unlock() + return data, nil + } + h.rcvMx.Unlock() + select { + case <-h.rcvd: + continue + case <-h.closed: + return nil, h.closeErr + case <-ctx.Done(): + return nil, ctx.Err() + } + } +} + +func (h *datagramQueue) CloseWithError(e error) { + h.closeErr = e + close(h.closed) +} diff --git a/vendor/github.com/quic-go/quic-go/errors.go b/vendor/github.com/quic-go/quic-go/errors.go new file mode 100644 index 000000000..fda3c9247 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/errors.go @@ -0,0 +1,75 @@ +package quic + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/qerr" +) + +type ( + TransportError = qerr.TransportError + ApplicationError = qerr.ApplicationError + VersionNegotiationError = qerr.VersionNegotiationError + StatelessResetError = qerr.StatelessResetError + IdleTimeoutError = qerr.IdleTimeoutError + HandshakeTimeoutError = qerr.HandshakeTimeoutError +) + +type ( + TransportErrorCode = qerr.TransportErrorCode + ApplicationErrorCode = qerr.ApplicationErrorCode + StreamErrorCode = qerr.StreamErrorCode +) + +const ( + NoError = qerr.NoError + InternalError = qerr.InternalError + ConnectionRefused = qerr.ConnectionRefused + FlowControlError = qerr.FlowControlError + StreamLimitError = qerr.StreamLimitError + StreamStateError = qerr.StreamStateError + FinalSizeError = qerr.FinalSizeError + FrameEncodingError = qerr.FrameEncodingError + TransportParameterError = qerr.TransportParameterError + ConnectionIDLimitError = qerr.ConnectionIDLimitError + ProtocolViolation = qerr.ProtocolViolation + InvalidToken = qerr.InvalidToken + ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode + CryptoBufferExceeded = qerr.CryptoBufferExceeded + KeyUpdateError = qerr.KeyUpdateError + AEADLimitReached = qerr.AEADLimitReached + NoViablePathError = qerr.NoViablePathError +) + +// A StreamError is used for Stream.CancelRead and Stream.CancelWrite. +// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing. +type StreamError struct { + StreamID StreamID + ErrorCode StreamErrorCode + Remote bool +} + +func (e *StreamError) Is(target error) bool { + _, ok := target.(*StreamError) + return ok +} + +func (e *StreamError) Error() string { + pers := "local" + if e.Remote { + pers = "remote" + } + return fmt.Sprintf("stream %d canceled by %s with error code %d", e.StreamID, pers, e.ErrorCode) +} + +// DatagramTooLargeError is returned from Connection.SendDatagram if the payload is too large to be sent. +type DatagramTooLargeError struct { + PeerMaxDatagramFrameSize int64 +} + +func (e *DatagramTooLargeError) Is(target error) bool { + _, ok := target.(*DatagramTooLargeError) + return ok +} + +func (e *DatagramTooLargeError) Error() string { return "DATAGRAM frame too large" } diff --git a/vendor/github.com/quic-go/quic-go/frame_sorter.go b/vendor/github.com/quic-go/quic-go/frame_sorter.go new file mode 100644 index 000000000..bee0abadb --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/frame_sorter.go @@ -0,0 +1,237 @@ +package quic + +import ( + "errors" + "sync" + + "github.com/quic-go/quic-go/internal/protocol" + list "github.com/quic-go/quic-go/internal/utils/linkedlist" +) + +// byteInterval is an interval from one ByteCount to the other +type byteInterval struct { + Start protocol.ByteCount + End protocol.ByteCount +} + +var byteIntervalElementPool sync.Pool + +func init() { + byteIntervalElementPool = *list.NewPool[byteInterval]() +} + +type frameSorterEntry struct { + Data []byte + DoneCb func() +} + +type frameSorter struct { + queue map[protocol.ByteCount]frameSorterEntry + readPos protocol.ByteCount + gaps *list.List[byteInterval] +} + +var errDuplicateStreamData = errors.New("duplicate stream data") + +func newFrameSorter() *frameSorter { + s := frameSorter{ + gaps: list.NewWithPool[byteInterval](&byteIntervalElementPool), + queue: make(map[protocol.ByteCount]frameSorterEntry), + } + s.gaps.PushFront(byteInterval{Start: 0, End: protocol.MaxByteCount}) + return &s +} + +func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, doneCb func()) error { + err := s.push(data, offset, doneCb) + if err == errDuplicateStreamData { + if doneCb != nil { + doneCb() + } + return nil + } + return err +} + +func (s *frameSorter) push(data []byte, offset protocol.ByteCount, doneCb func()) error { + if len(data) == 0 { + return errDuplicateStreamData + } + + start := offset + end := offset + protocol.ByteCount(len(data)) + + if end <= s.gaps.Front().Value.Start { + return errDuplicateStreamData + } + + startGap, startsInGap := s.findStartGap(start) + endGap, endsInGap := s.findEndGap(startGap, end) + + startGapEqualsEndGap := startGap == endGap + + if (startGapEqualsEndGap && end <= startGap.Value.Start) || + (!startGapEqualsEndGap && startGap.Value.End >= endGap.Value.Start && end <= startGap.Value.Start) { + return errDuplicateStreamData + } + + startGapNext := startGap.Next() + startGapEnd := startGap.Value.End // save it, in case startGap is modified + endGapStart := endGap.Value.Start // save it, in case endGap is modified + endGapEnd := endGap.Value.End // save it, in case endGap is modified + var adjustedStartGapEnd bool + var wasCut bool + + pos := start + var hasReplacedAtLeastOne bool + for { + oldEntry, ok := s.queue[pos] + if !ok { + break + } + oldEntryLen := protocol.ByteCount(len(oldEntry.Data)) + if end-pos > oldEntryLen || (hasReplacedAtLeastOne && end-pos == oldEntryLen) { + // The existing frame is shorter than the new frame. Replace it. + delete(s.queue, pos) + pos += oldEntryLen + hasReplacedAtLeastOne = true + if oldEntry.DoneCb != nil { + oldEntry.DoneCb() + } + } else { + if !hasReplacedAtLeastOne { + return errDuplicateStreamData + } + // The existing frame is longer than the new frame. + // Cut the new frame such that the end aligns with the start of the existing frame. + data = data[:pos-start] + end = pos + wasCut = true + break + } + } + + if !startsInGap && !hasReplacedAtLeastOne { + // cut the frame, such that it starts at the start of the gap + data = data[startGap.Value.Start-start:] + start = startGap.Value.Start + wasCut = true + } + if start <= startGap.Value.Start { + if end >= startGap.Value.End { + // The frame covers the whole startGap. Delete the gap. + s.gaps.Remove(startGap) + } else { + startGap.Value.Start = end + } + } else if !hasReplacedAtLeastOne { + startGap.Value.End = start + adjustedStartGapEnd = true + } + + if !startGapEqualsEndGap { + s.deleteConsecutive(startGapEnd) + var nextGap *list.Element[byteInterval] + for gap := startGapNext; gap.Value.End < endGapStart; gap = nextGap { + nextGap = gap.Next() + s.deleteConsecutive(gap.Value.End) + s.gaps.Remove(gap) + } + } + + if !endsInGap && start != endGapEnd && end > endGapEnd { + // cut the frame, such that it ends at the end of the gap + data = data[:endGapEnd-start] + end = endGapEnd + wasCut = true + } + if end == endGapEnd { + if !startGapEqualsEndGap { + // The frame covers the whole endGap. Delete the gap. + s.gaps.Remove(endGap) + } + } else { + if startGapEqualsEndGap && adjustedStartGapEnd { + // The frame split the existing gap into two. + s.gaps.InsertAfter(byteInterval{Start: end, End: startGapEnd}, startGap) + } else if !startGapEqualsEndGap { + endGap.Value.Start = end + } + } + + if wasCut && len(data) < protocol.MinStreamFrameBufferSize { + newData := make([]byte, len(data)) + copy(newData, data) + data = newData + if doneCb != nil { + doneCb() + doneCb = nil + } + } + + if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { + return errors.New("too many gaps in received data") + } + + s.queue[start] = frameSorterEntry{Data: data, DoneCb: doneCb} + return nil +} + +func (s *frameSorter) findStartGap(offset protocol.ByteCount) (*list.Element[byteInterval], bool) { + for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { + if offset >= gap.Value.Start && offset <= gap.Value.End { + return gap, true + } + if offset < gap.Value.Start { + return gap, false + } + } + panic("no gap found") +} + +func (s *frameSorter) findEndGap(startGap *list.Element[byteInterval], offset protocol.ByteCount) (*list.Element[byteInterval], bool) { + for gap := startGap; gap != nil; gap = gap.Next() { + if offset >= gap.Value.Start && offset < gap.Value.End { + return gap, true + } + if offset < gap.Value.Start { + return gap.Prev(), false + } + } + panic("no gap found") +} + +// deleteConsecutive deletes consecutive frames from the queue, starting at pos +func (s *frameSorter) deleteConsecutive(pos protocol.ByteCount) { + for { + oldEntry, ok := s.queue[pos] + if !ok { + break + } + oldEntryLen := protocol.ByteCount(len(oldEntry.Data)) + delete(s.queue, pos) + if oldEntry.DoneCb != nil { + oldEntry.DoneCb() + } + pos += oldEntryLen + } +} + +func (s *frameSorter) Pop() (protocol.ByteCount, []byte, func()) { + entry, ok := s.queue[s.readPos] + if !ok { + return s.readPos, nil, nil + } + delete(s.queue, s.readPos) + offset := s.readPos + s.readPos += protocol.ByteCount(len(entry.Data)) + if s.gaps.Front().Value.End <= s.readPos { + panic("frame sorter BUG: read position higher than a gap") + } + return offset, entry.Data, entry.DoneCb +} + +// HasMoreData says if there is any more data queued at *any* offset. +func (s *frameSorter) HasMoreData() bool { + return len(s.queue) > 0 +} diff --git a/vendor/github.com/quic-go/quic-go/framer.go b/vendor/github.com/quic-go/quic-go/framer.go new file mode 100644 index 000000000..1e6219a46 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/framer.go @@ -0,0 +1,210 @@ +package quic + +import ( + "errors" + "sync" + + "github.com/quic-go/quic-go/internal/ackhandler" + "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 { + mutex sync.Mutex + + streamGetter streamGetter + + activeStreams map[protocol.StreamID]struct{} + streamQueue ringbuffer.RingBuffer[protocol.StreamID] + + controlFrameMutex sync.Mutex + controlFrames []wire.Frame + pathResponses []*wire.PathResponseFrame + queuedTooManyControlFrames bool +} + +var _ framer = &framerI{} + +func newFramer(streamGetter streamGetter) framer { + return &framerI{ + streamGetter: streamGetter, + activeStreams: make(map[protocol.StreamID]struct{}), + } +} + +func (f *framerI) HasData() bool { + f.mutex.Lock() + hasData := !f.streamQueue.Empty() + f.mutex.Unlock() + if hasData { + return true + } + f.controlFrameMutex.Lock() + defer f.controlFrameMutex.Unlock() + return len(f.controlFrames) > 0 || len(f.pathResponses) > 0 +} + +func (f *framerI) QueueControlFrame(frame wire.Frame) { + f.controlFrameMutex.Lock() + defer f.controlFrameMutex.Unlock() + + if pr, ok := frame.(*wire.PathResponseFrame); ok { + // Only queue up to maxPathResponses PATH_RESPONSE frames. + // This limit should be high enough to never be hit in practice, + // unless the peer is doing something malicious. + if len(f.pathResponses) >= maxPathResponses { + return + } + f.pathResponses = append(f.pathResponses, pr) + return + } + // This is a hack. + if len(f.controlFrames) >= maxControlFrames { + f.queuedTooManyControlFrames = true + return + } + f.controlFrames = append(f.controlFrames, frame) +} + +func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) { + f.controlFrameMutex.Lock() + defer f.controlFrameMutex.Unlock() + + var length protocol.ByteCount + // add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet + if len(f.pathResponses) > 0 { + frame := f.pathResponses[0] + frameLen := frame.Length(v) + if frameLen <= maxLen { + frames = append(frames, ackhandler.Frame{Frame: frame}) + length += frameLen + f.pathResponses = f.pathResponses[1:] + } + } + + for len(f.controlFrames) > 0 { + frame := f.controlFrames[len(f.controlFrames)-1] + frameLen := frame.Length(v) + if length+frameLen > maxLen { + break + } + frames = append(frames, ackhandler.Frame{Frame: frame}) + length += frameLen + f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] + } + return frames, length +} + +func (f *framerI) QueuedTooManyControlFrames() bool { + return f.queuedTooManyControlFrames +} + +func (f *framerI) AddActiveStream(id protocol.StreamID) { + f.mutex.Lock() + if _, ok := f.activeStreams[id]; !ok { + f.streamQueue.PushBack(id) + f.activeStreams[id] = struct{}{} + } + 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 + 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 += 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) + } + 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 { + f.mutex.Lock() + defer f.mutex.Unlock() + + f.controlFrameMutex.Lock() + f.streamQueue.Clear() + for id := range f.activeStreams { + delete(f.activeStreams, id) + } + 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: + continue + default: + f.controlFrames[j] = f.controlFrames[i] + j++ + } + } + f.controlFrames = f.controlFrames[:j] + f.controlFrameMutex.Unlock() + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/interface.go b/vendor/github.com/quic-go/quic-go/interface.go new file mode 100644 index 000000000..ca8544d8f --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/interface.go @@ -0,0 +1,362 @@ +package quic + +import ( + "context" + "crypto/tls" + "errors" + "io" + "net" + "time" + + "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/logging" +) + +// The StreamID is the ID of a QUIC stream. +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 + // Version2 is RFC 9369 + Version2 = protocol.Version2 +) + +// A ClientToken is a token received by the client. +// It can be used to skip address validation on future connection attempts. +type ClientToken struct { + data []byte +} + +type TokenStore interface { + // Pop searches for a ClientToken associated with the given key. + // Since tokens are not supposed to be reused, it must remove the token from the cache. + // It returns nil when no token is found. + Pop(key string) (token *ClientToken) + + // Put adds a token to the cache with the given key. It might get called + // multiple times in a connection. + Put(key string, token *ClientToken) +} + +// Err0RTTRejected is the returned from: +// * 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. +// It is set on the Connection.Context() context, +// as well as on the context passed to logging.Tracer.NewConnectionTracer. +var ConnectionTracingKey = connTracingCtxKey{} + +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. +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. +type Stream interface { + ReceiveStream + SendStream + // SetDeadline sets the read and write deadlines associated + // with the connection. It is equivalent to calling both + // SetReadDeadline and SetWriteDeadline. + SetDeadline(t time.Time) error +} + +// A ReceiveStream is a unidirectional Receive Stream. +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. + io.Reader + // CancelRead aborts receiving on this stream. + // It will ask the peer to stop transmitting stream data. + // Read will unblock immediately, and future Read calls will fail. + // When called multiple times or after reading the io.EOF it is a no-op. + CancelRead(StreamErrorCode) + // 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 +} + +// A SendStream is a unidirectional Send Stream. +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. + io.Writer + // Close closes the write-direction of the stream. + // Future calls to Write are not permitted after calling Close. + // It must not be called concurrently with Write. + // It must not be called after calling CancelWrite. + io.Closer + // CancelWrite aborts sending on this stream. + // Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably. + // Write will unblock immediately, and future calls to Write will fail. + // When called multiple times or after closing the stream it is a no-op. + CancelWrite(StreamErrorCode) + // The Context is canceled as soon as the write-side of the stream is closed. + // This happens when Close() or CancelWrite() is called, or when the peer + // cancels the read-side of their stream. + // The cancellation cause is set to the error that caused the stream to + // close, or `context.Canceled` in case the stream is closed without error. + Context() context.Context + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + // Even if write times out, it may return n > 0, indicating that + // some data was successfully written. + // A zero value for t means Write will not time out. + SetWriteDeadline(t time.Time) error +} + +// 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 +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. + 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. + 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. + OpenUniStreamSync(context.Context) (SendStream, error) + // LocalAddr returns the local address. + LocalAddr() net.Addr + // RemoteAddr returns the address of the peer. + RemoteAddr() net.Addr + // CloseWithError closes the connection with an error. + // The error string will be sent to the peer. + CloseWithError(ApplicationErrorCode, string) error + // Context returns a context that is cancelled when the connection is closed. + // The cancellation cause is set to the error that caused the connection to + // close, or `context.Canceled` in case the listener is closed first. + Context() context.Context + // ConnectionState returns basic details about the QUIC connection. + // Warning: This API should not be considered stable and might change soon. + ConnectionState() ConnectionState + + // SendDatagram sends a message using a QUIC datagram, as specified in RFC 9221. + // There is no delivery guarantee for DATAGRAM frames, they are not retransmitted if lost. + // The payload of the datagram needs to fit into a single QUIC packet. + // In addition, a datagram may be dropped before being sent out if the available packet size suddenly decreases. + // If the payload is too large to be sent at the current time, a DatagramTooLargeError is returned. + SendDatagram(payload []byte) error + // ReceiveDatagram gets a message received in a datagram, as specified in RFC 9221. + ReceiveDatagram(context.Context) ([]byte, error) +} + +// An EarlyConnection is a connection that is handshaking. +// Data sent during the handshake is encrypted using the forward secure keys. +// When using client certificates, the client's identity is only verified +// after completion of the handshake. +type EarlyConnection interface { + Connection + + // HandshakeComplete blocks until the handshake completes (or fails). + // For the client, data sent before completion of the handshake is encrypted with 0-RTT keys. + // For the server, data sent before completion of the handshake is encrypted with 1-RTT keys, + // however the client's identity is only verified once the handshake completes. + HandshakeComplete() <-chan struct{} + + NextConnection() Connection +} + +// StatelessResetKey is a key used to derive stateless reset tokens. +type StatelessResetKey [32]byte + +// TokenGeneratorKey is a key used to encrypt session resumption tokens. +type TokenGeneratorKey = handshake.TokenProtectorKey + +// A ConnectionID is a QUIC Connection ID, as defined in RFC 9000. +// It is not able to handle QUIC Connection IDs longer than 20 bytes, +// as they are allowed by RFC 8999. +type ConnectionID = protocol.ConnectionID + +// ConnectionIDFromBytes interprets b as a Connection ID. 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. +type ConnectionIDGenerator interface { + // GenerateConnectionID generates a new ConnectionID. + // Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs. + 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() int +} + +// Config contains all configuration data needed for a QUIC server or client. +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) + // The QUIC versions that can be negotiated. + // If not set, it uses all versions available. + Versions []Version + // HandshakeIdleTimeout is the idle timeout before completion of the handshake. + // If we don't receive any packet from the peer within this time, the connection attempt is aborted. + // Additionally, if the handshake doesn't complete in twice this time, the connection attempt is also aborted. + // If this value is zero, the timeout is set to 5 seconds. + HandshakeIdleTimeout time.Duration + // MaxIdleTimeout is the maximum duration that may pass without any incoming network activity. + // The actual value for the idle timeout is the minimum of this value and the peer's. + // This value only applies after the handshake has completed. + // If the timeout is exceeded, the connection is closed. + // If this value is zero, the timeout is set to 30 seconds. + MaxIdleTimeout time.Duration + // The TokenStore stores tokens received from the server. + // Tokens are used to skip address validation on future connection attempts. + // The key used to store tokens is the ServerName from the tls.Config, if set + // otherwise the token is associated with the server's IP address. + TokenStore TokenStore + // InitialStreamReceiveWindow is the initial size of the stream-level flow control window for receiving data. + // If the application is consuming data quickly enough, the flow control auto-tuning algorithm + // will increase the window up to MaxStreamReceiveWindow. + // If this value is zero, it will default to 512 KB. + // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. + InitialStreamReceiveWindow uint64 + // MaxStreamReceiveWindow is the maximum stream-level flow control window for receiving data. + // If this value is zero, it will default to 6 MB. + // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. + MaxStreamReceiveWindow uint64 + // InitialConnectionReceiveWindow is the initial size of the stream-level flow control window for receiving data. + // If the application is consuming data quickly enough, the flow control auto-tuning algorithm + // will increase the window up to MaxConnectionReceiveWindow. + // If this value is zero, it will default to 512 KB. + // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. + InitialConnectionReceiveWindow uint64 + // MaxConnectionReceiveWindow is the connection-level flow control window for receiving data. + // If this value is zero, it will default to 15 MB. + // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. + MaxConnectionReceiveWindow uint64 + // AllowConnectionWindowIncrease is called every time the connection flow controller attempts + // to increase the connection flow control window. + // If set, the caller can prevent an increase of the window. Typically, it would do so to + // limit the memory usage. + // To avoid deadlocks, it is not valid to call other functions on the connection or on streams + // in this callback. + AllowConnectionWindowIncrease func(conn Connection, delta uint64) bool + // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. + // If not set, it will default to 100. + // If set to a negative value, it doesn't allow any bidirectional streams. + // Values larger than 2^60 will be clipped to that value. + MaxIncomingStreams int64 + // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open. + // If not set, it will default to 100. + // If set to a negative value, it doesn't allow any unidirectional streams. + // Values larger than 2^60 will be clipped to that value. + MaxIncomingUniStreams int64 + // KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive. + // 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 + // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). + // This allows the sending of QUIC packets that fully utilize the available MTU of the path. + // Path MTU discovery is only available on systems that allow setting of the Don't Fragment (DF) bit. + // If unavailable or disabled, packets will be at most 1252 (IPv4) / 1232 (IPv6) bytes in size. + DisablePathMTUDiscovery bool + // Allow0RTT allows the application to decide if a 0-RTT connection attempt should be accepted. + // Only valid for the server. + Allow0RTT bool + // Enable QUIC datagram support (RFC 9221). + EnableDatagrams bool + Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer +} + +// ClientHelloInfo contains information about an incoming connection attempt. +type ClientHelloInfo 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 + // AddrVerified says if the remote address was verified using QUIC's Retry mechanism. + // Note that the Retry mechanism costs one network roundtrip, + // and is not performed unless Transport.MaxUnvalidatedHandshakes is surpassed. + AddrVerified bool +} + +// 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 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 bool +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go new file mode 100644 index 000000000..34506b12e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go @@ -0,0 +1,20 @@ +package ackhandler + +import "github.com/quic-go/quic-go/internal/wire" + +// IsFrameAckEliciting returns true if the frame is ack-eliciting. +func IsFrameAckEliciting(f wire.Frame) bool { + _, isAck := f.(*wire.AckFrame) + _, isConnectionClose := f.(*wire.ConnectionCloseFrame) + return !isAck && !isConnectionClose +} + +// HasAckElicitingFrames returns true if at least one frame is ack-eliciting. +func HasAckElicitingFrames(fs []Frame) bool { + for _, f := range fs { + if IsFrameAckEliciting(f.Frame) { + return true + } + } + return false +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go new file mode 100644 index 000000000..6f890b4d3 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go @@ -0,0 +1,24 @@ +package ackhandler + +import ( + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +// NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler. +// clientAddressValidated indicates whether the address was validated beforehand by an address validation token. +// clientAddressValidated has no effect for a client. +func NewAckHandler( + initialPacketNumber protocol.PacketNumber, + initialMaxDatagramSize protocol.ByteCount, + rttStats *utils.RTTStats, + clientAddressValidated bool, + enableECN bool, + pers protocol.Perspective, + tracer *logging.ConnectionTracer, + logger utils.Logger, +) (SentPacketHandler, ReceivedPacketHandler) { + sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, enableECN, pers, tracer, logger) + return sph, newReceivedPacketHandler(sph, logger) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go new file mode 100644 index 000000000..68415ac6c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go @@ -0,0 +1,296 @@ +package ackhandler + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +type ecnState uint8 + +const ( + ecnStateInitial ecnState = iota + ecnStateTesting + ecnStateUnknown + ecnStateCapable + ecnStateFailed +) + +// must fit into an uint8, otherwise numSentTesting and numLostTesting must have a larger type +const numECNTestingPackets = 10 + +type ecnHandler interface { + SentPacket(protocol.PacketNumber, protocol.ECN) + Mode() protocol.ECN + HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) + LostPacket(protocol.PacketNumber) +} + +// The ecnTracker performs ECN validation of a path. +// Once failed, it doesn't do any re-validation of the path. +// It is designed only work for 1-RTT packets, it doesn't handle multiple packet number spaces. +// In order to avoid revealing any internal state to on-path observers, +// callers should make sure to start using ECN (i.e. calling Mode) for the very first 1-RTT packet sent. +// The validation logic implemented here strictly follows the algorithm described in RFC 9000 section 13.4.2 and A.4. +type ecnTracker struct { + state ecnState + numSentTesting, numLostTesting uint8 + + firstTestingPacket protocol.PacketNumber + lastTestingPacket protocol.PacketNumber + firstCapablePacket protocol.PacketNumber + + numSentECT0, numSentECT1 int64 + numAckedECT0, numAckedECT1, numAckedECNCE int64 + + tracer *logging.ConnectionTracer + logger utils.Logger +} + +var _ ecnHandler = &ecnTracker{} + +func newECNTracker(logger utils.Logger, tracer *logging.ConnectionTracer) *ecnTracker { + return &ecnTracker{ + firstTestingPacket: protocol.InvalidPacketNumber, + lastTestingPacket: protocol.InvalidPacketNumber, + firstCapablePacket: protocol.InvalidPacketNumber, + state: ecnStateInitial, + logger: logger, + tracer: tracer, + } +} + +func (e *ecnTracker) SentPacket(pn protocol.PacketNumber, ecn protocol.ECN) { + //nolint:exhaustive // These are the only ones we need to take care of. + switch ecn { + case protocol.ECNNon: + return + case protocol.ECT0: + e.numSentECT0++ + case protocol.ECT1: + e.numSentECT1++ + case protocol.ECNUnsupported: + if e.state != ecnStateFailed { + panic("didn't expect ECN to be unsupported") + } + default: + panic(fmt.Sprintf("sent packet with unexpected ECN marking: %s", ecn)) + } + + if e.state == ecnStateCapable && e.firstCapablePacket == protocol.InvalidPacketNumber { + e.firstCapablePacket = pn + } + + if e.state != ecnStateTesting { + return + } + + e.numSentTesting++ + if e.firstTestingPacket == protocol.InvalidPacketNumber { + e.firstTestingPacket = pn + } + if e.numSentECT0+e.numSentECT1 >= numECNTestingPackets { + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateUnknown + e.lastTestingPacket = pn + } +} + +func (e *ecnTracker) Mode() protocol.ECN { + switch e.state { + case ecnStateInitial: + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateTesting + return e.Mode() + case ecnStateTesting, ecnStateCapable: + return protocol.ECT0 + case ecnStateUnknown, ecnStateFailed: + return protocol.ECNNon + default: + panic(fmt.Sprintf("unknown ECN state: %d", e.state)) + } +} + +func (e *ecnTracker) LostPacket(pn protocol.PacketNumber) { + if e.state != ecnStateTesting && e.state != ecnStateUnknown { + return + } + if !e.isTestingPacket(pn) { + return + } + e.numLostTesting++ + // Only proceed if we have sent all 10 testing packets. + if e.state != ecnStateUnknown { + return + } + if e.numLostTesting >= e.numSentTesting { + e.logger.Debugf("Disabling ECN. All testing packets were lost.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) + } + e.state = ecnStateFailed + return + } + // Path validation also fails if some testing packets are lost, and all other testing packets where CE-marked + e.failIfMangled() +} + +// HandleNewlyAcked handles the ECN counts on an ACK frame. +// It must only be called for ACK frames that increase the largest acknowledged packet number, +// see section 13.4.2.1 of RFC 9000. +func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) { + if e.state == ecnStateFailed { + return false + } + + // ECN validation can fail if the received total count for either ECT(0) or ECT(1) exceeds + // the total number of packets sent with each corresponding ECT codepoint. + if ect0 > e.numSentECT0 || ect1 > e.numSentECT1 { + e.logger.Debugf("Disabling ECN. Received more ECT(0) / ECT(1) acknowledgements than packets sent.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) + } + e.state = ecnStateFailed + return false + } + + // Count ECT0 and ECT1 marks that we used when sending the packets that are now being acknowledged. + var ackedECT0, ackedECT1 int64 + for _, p := range packets { + //nolint:exhaustive // We only ever send ECT(0) and ECT(1). + switch e.ecnMarking(p.PacketNumber) { + case protocol.ECT0: + ackedECT0++ + case protocol.ECT1: + ackedECT1++ + } + } + + // If an ACK frame newly acknowledges a packet that the endpoint sent with either the ECT(0) or ECT(1) + // codepoint set, ECN validation fails if the corresponding ECN counts are not present in the ACK frame. + // This check detects: + // * paths that bleach all ECN marks, and + // * peers that don't report any ECN counts + if (ackedECT0 > 0 || ackedECT1 > 0) && ect0 == 0 && ect1 == 0 && ecnce == 0 { + e.logger.Debugf("Disabling ECN. ECN-marked packet acknowledged, but no ECN counts on ACK frame.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) + } + e.state = ecnStateFailed + return false + } + + // Determine the increase in ECT0, ECT1 and ECNCE marks + newECT0 := ect0 - e.numAckedECT0 + newECT1 := ect1 - e.numAckedECT1 + newECNCE := ecnce - e.numAckedECNCE + + // We're only processing ACKs that increase the Largest Acked. + // Therefore, the ECN counters should only ever increase. + // Any decrease means that the peer's counting logic is broken. + if newECT0 < 0 || newECT1 < 0 || newECNCE < 0 { + e.logger.Debugf("Disabling ECN. ECN counts decreased unexpectedly.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts) + } + e.state = ecnStateFailed + return false + } + + // ECN validation also fails if the sum of the increase in ECT(0) and ECN-CE counts is less than the number + // of newly acknowledged packets that were originally sent with an ECT(0) marking. + // This could be the result of (partial) bleaching. + if newECT0+newECNCE < ackedECT0 { + e.logger.Debugf("Disabling ECN. Received less ECT(0) + ECN-CE than packets sent with ECT(0).") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + } + e.state = ecnStateFailed + return false + } + // Similarly, ECN validation fails if the sum of the increases to ECT(1) and ECN-CE counts is less than + // the number of newly acknowledged packets sent with an ECT(1) marking. + if newECT1+newECNCE < ackedECT1 { + e.logger.Debugf("Disabling ECN. Received less ECT(1) + ECN-CE than packets sent with ECT(1).") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + } + e.state = ecnStateFailed + return false + } + + // update our counters + e.numAckedECT0 = ect0 + e.numAckedECT1 = ect1 + e.numAckedECNCE = ecnce + + // Detect mangling (a path remarking all ECN-marked testing packets as CE), + // once all 10 testing packets have been sent out. + if e.state == ecnStateUnknown { + e.failIfMangled() + if e.state == ecnStateFailed { + return false + } + } + if e.state == ecnStateTesting || e.state == ecnStateUnknown { + var ackedTestingPacket bool + for _, p := range packets { + if e.isTestingPacket(p.PacketNumber) { + ackedTestingPacket = true + break + } + } + // This check won't succeed if the path is mangling ECN-marks (i.e. rewrites all ECN-marked packets to CE). + if ackedTestingPacket && (newECT0 > 0 || newECT1 > 0) { + e.logger.Debugf("ECN capability confirmed.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateCapable + } + } + + // Don't trust CE marks before having confirmed ECN capability of the path. + // Otherwise, mangling would be misinterpreted as actual congestion. + return e.state == ecnStateCapable && newECNCE > 0 +} + +// failIfMangled fails ECN validation if all testing packets are lost or CE-marked. +func (e *ecnTracker) failIfMangled() { + numAckedECNCE := e.numAckedECNCE + int64(e.numLostTesting) + if e.numSentECT0+e.numSentECT1 > numAckedECNCE { + return + } + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) + } + e.state = ecnStateFailed +} + +func (e *ecnTracker) ecnMarking(pn protocol.PacketNumber) protocol.ECN { + if pn < e.firstTestingPacket || e.firstTestingPacket == protocol.InvalidPacketNumber { + return protocol.ECNNon + } + if pn < e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber { + return protocol.ECT0 + } + if pn < e.firstCapablePacket || e.firstCapablePacket == protocol.InvalidPacketNumber { + return protocol.ECNNon + } + // We don't need to deal with the case when ECN validation fails, + // since we're ignoring any ECN counts reported in ACK frames in that case. + return protocol.ECT0 +} + +func (e *ecnTracker) isTestingPacket(pn protocol.PacketNumber) bool { + if e.firstTestingPacket == protocol.InvalidPacketNumber { + return false + } + return pn >= e.firstTestingPacket && (pn <= e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go new file mode 100644 index 000000000..e03a8080c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go @@ -0,0 +1,21 @@ +package ackhandler + +import ( + "github.com/quic-go/quic-go/internal/wire" +) + +// FrameHandler handles the acknowledgement and the loss of a frame. +type FrameHandler interface { + OnAcked(wire.Frame) + OnLost(wire.Frame) +} + +type Frame struct { + Frame wire.Frame // nil if the frame has already been acknowledged in another packet + Handler FrameHandler +} + +type StreamFrame struct { + Frame *wire.StreamFrame + Handler FrameHandler +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go new file mode 100644 index 000000000..ba8cbbdae --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go @@ -0,0 +1,53 @@ +package ackhandler + +import ( + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +// 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) + // 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() + + // The SendMode determines if and what kind of packets can be sent. + SendMode(now time.Time) SendMode + // TimeUntilSend is the time when the next packet should be sent. + // It is used for pacing packets. + TimeUntilSend() time.Time + SetMaxDatagramSize(count protocol.ByteCount) + + // only to be called once the handshake is complete + QueueProbePacket(protocol.EncryptionLevel) bool /* was a packet queued */ + + ECNMode(isShortHeaderPacket bool) protocol.ECN // isShortHeaderPacket should only be true for non-coalesced 1-RTT packets + PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) + PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber + + GetLossDetectionTimeout() time.Time + OnLossDetectionTimeout() error +} + +type sentPacketTracker interface { + GetLowestPacketNotConfirmedAcked() protocol.PacketNumber + ReceivedPacket(protocol.EncryptionLevel) +} + +// ReceivedPacketHandler handles ACKs needed to send for incoming packets +type ReceivedPacketHandler interface { + IsPotentiallyDuplicate(protocol.PacketNumber, protocol.EncryptionLevel) bool + ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, ackEliciting bool) error + DropPackets(protocol.EncryptionLevel) + + GetAlarmTimeout() time.Time + GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go new file mode 100644 index 000000000..0031e6b1c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go @@ -0,0 +1,9 @@ +//go:build gomock || generate + +package ackhandler + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" +type SentPacketTracker = sentPacketTracker + +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler" +type ECNHandler = ecnHandler diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go new file mode 100644 index 000000000..5f43689b5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go @@ -0,0 +1,55 @@ +package ackhandler + +import ( + "sync" + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// A Packet is a packet +type packet struct { + SendTime time.Time + PacketNumber protocol.PacketNumber + StreamFrames []StreamFrame + Frames []Frame + LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK + Length protocol.ByteCount + EncryptionLevel protocol.EncryptionLevel + + IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller. + + includedInBytesInFlight bool + declaredLost bool + skippedPacket bool +} + +func (p *packet) outstanding() bool { + return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket +} + +var packetPool = sync.Pool{New: func() any { return &packet{} }} + +func getPacket() *packet { + p := packetPool.Get().(*packet) + p.PacketNumber = 0 + p.StreamFrames = nil + p.Frames = nil + p.LargestAcked = 0 + p.Length = 0 + p.EncryptionLevel = protocol.EncryptionLevel(0) + p.SendTime = time.Time{} + p.IsPathMTUProbePacket = false + p.includedInBytesInFlight = false + p.declaredLost = false + p.skippedPacket = false + return p +} + +// We currently only return Packets back into the pool when they're acknowledged (not when they're lost). +// This simplifies the code, and gives the vast majority of the performance benefit we can gain from using the pool. +func putPacket(p *packet) { + p.Frames = nil + p.StreamFrames = nil + packetPool.Put(p) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go new file mode 100644 index 000000000..4a9db8635 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go @@ -0,0 +1,84 @@ +package ackhandler + +import ( + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +type packetNumberGenerator interface { + Peek() protocol.PacketNumber + // Pop pops the packet number. + // It reports if the packet number (before the one just popped) was skipped. + // It never skips more than one packet number in a row. + Pop() (skipped bool, _ protocol.PacketNumber) +} + +type sequentialPacketNumberGenerator struct { + next protocol.PacketNumber +} + +var _ packetNumberGenerator = &sequentialPacketNumberGenerator{} + +func newSequentialPacketNumberGenerator(initial protocol.PacketNumber) packetNumberGenerator { + return &sequentialPacketNumberGenerator{next: initial} +} + +func (p *sequentialPacketNumberGenerator) Peek() protocol.PacketNumber { + return p.next +} + +func (p *sequentialPacketNumberGenerator) Pop() (bool, protocol.PacketNumber) { + next := p.next + p.next++ + return false, next +} + +// The skippingPacketNumberGenerator generates the packet number for the next packet +// it randomly skips a packet number every averagePeriod packets (on average). +// It is guaranteed to never skip two consecutive packet numbers. +type skippingPacketNumberGenerator struct { + period protocol.PacketNumber + maxPeriod protocol.PacketNumber + + next protocol.PacketNumber + nextToSkip protocol.PacketNumber + + rng utils.Rand +} + +var _ packetNumberGenerator = &skippingPacketNumberGenerator{} + +func newSkippingPacketNumberGenerator(initial, initialPeriod, maxPeriod protocol.PacketNumber) packetNumberGenerator { + g := &skippingPacketNumberGenerator{ + next: initial, + period: initialPeriod, + maxPeriod: maxPeriod, + } + g.generateNewSkip() + return g +} + +func (p *skippingPacketNumberGenerator) Peek() protocol.PacketNumber { + if p.next == p.nextToSkip { + return p.next + 1 + } + return p.next +} + +func (p *skippingPacketNumberGenerator) Pop() (bool, protocol.PacketNumber) { + next := p.next + if p.next == p.nextToSkip { + next++ + p.next += 2 + p.generateNewSkip() + return true, next + } + p.next++ // generate a new packet number for the next packet + return false, next +} + +func (p *skippingPacketNumberGenerator) generateNewSkip() { + // make sure that there are never two consecutive packet numbers that are skipped + p.nextToSkip = p.next + 3 + protocol.PacketNumber(p.rng.Int31n(int32(2*p.period))) + p.period = min(2*p.period, p.maxPeriod) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go new file mode 100644 index 000000000..1175c790b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go @@ -0,0 +1,125 @@ +package ackhandler + +import ( + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +type receivedPacketHandler struct { + sentPackets sentPacketTracker + + initialPackets *receivedPacketTracker + handshakePackets *receivedPacketTracker + appDataPackets appDataReceivedPacketTracker + + lowest1RTTPacket protocol.PacketNumber +} + +var _ ReceivedPacketHandler = &receivedPacketHandler{} + +func newReceivedPacketHandler(sentPackets sentPacketTracker, logger utils.Logger) ReceivedPacketHandler { + return &receivedPacketHandler{ + sentPackets: sentPackets, + initialPackets: newReceivedPacketTracker(), + handshakePackets: newReceivedPacketTracker(), + appDataPackets: *newAppDataReceivedPacketTracker(logger), + lowest1RTTPacket: protocol.InvalidPacketNumber, + } +} + +func (h *receivedPacketHandler) ReceivedPacket( + pn protocol.PacketNumber, + ecn protocol.ECN, + encLevel protocol.EncryptionLevel, + rcvTime time.Time, + ackEliciting bool, +) error { + h.sentPackets.ReceivedPacket(encLevel) + switch encLevel { + case protocol.EncryptionInitial: + return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) + case protocol.EncryptionHandshake: + // The Handshake packet number space might already have been dropped as a result + // of processing the CRYPTO frame that was contained in this packet. + if h.handshakePackets == nil { + return nil + } + return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) + case protocol.Encryption0RTT: + if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket { + return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket) + } + return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) + case protocol.Encryption1RTT: + if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket { + h.lowest1RTTPacket = pn + } + if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { + return err + } + h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked()) + return nil + default: + panic(fmt.Sprintf("received packet with unknown encryption level: %s", encLevel)) + } +} + +func (h *receivedPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { + //nolint:exhaustive // 1-RTT packet number space is never dropped. + switch encLevel { + case protocol.EncryptionInitial: + h.initialPackets = nil + case protocol.EncryptionHandshake: + h.handshakePackets = nil + case protocol.Encryption0RTT: + // Nothing to do here. + // If we are rejecting 0-RTT, no 0-RTT packets will have been decrypted. + default: + panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) + } +} + +func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { + return h.appDataPackets.GetAlarmTimeout() +} + +func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame { + //nolint:exhaustive // 0-RTT packets can't contain ACK frames. + switch encLevel { + case protocol.EncryptionInitial: + if h.initialPackets != nil { + return h.initialPackets.GetAckFrame() + } + return nil + case protocol.EncryptionHandshake: + if h.handshakePackets != nil { + return h.handshakePackets.GetAckFrame() + } + return nil + case protocol.Encryption1RTT: + return h.appDataPackets.GetAckFrame(onlyIfQueued) + default: + // 0-RTT packets can't contain ACK frames + return nil + } +} + +func (h *receivedPacketHandler) IsPotentiallyDuplicate(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) bool { + switch encLevel { + case protocol.EncryptionInitial: + if h.initialPackets != nil { + return h.initialPackets.IsPotentiallyDuplicate(pn) + } + case protocol.EncryptionHandshake: + if h.handshakePackets != nil { + return h.handshakePackets.IsPotentiallyDuplicate(pn) + } + case protocol.Encryption0RTT, protocol.Encryption1RTT: + return h.appDataPackets.IsPotentiallyDuplicate(pn) + } + panic("unexpected encryption level") +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go new file mode 100644 index 000000000..3143bfe12 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go @@ -0,0 +1,151 @@ +package ackhandler + +import ( + "sync" + + "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" +) + +// interval is an interval from one PacketNumber to the other +type interval struct { + Start protocol.PacketNumber + 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] + + deletedBelow protocol.PacketNumber +} + +func newReceivedPacketHistory() *receivedPacketHistory { + return &receivedPacketHistory{ + ranges: list.NewWithPool[interval](&intervalElementPool), + } +} + +// ReceivedPacket registers a packet with PacketNumber p and updates the ranges +func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ { + // ignore delayed packets, if we already deleted the range + if p < h.deletedBelow { + return false + } + isNew := h.addToRanges(p) + h.maybeDeleteOldRanges() + 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}) + return true + } + + for el := h.ranges.Back(); el != nil; el = el.Prev() { + // p already included in an existing range. Nothing to do here + if p >= el.Value.Start && p <= el.Value.End { + return false + } + + if el.Value.End == p-1 { // extend a range at the end + el.Value.End = p + return true + } + if el.Value.Start == p+1 { // extend a range at the beginning + el.Value.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) + } + return true + } + + // create a new range at the end + if p > el.Value.End { + h.ranges.InsertAfter(interval{Start: p, End: p}, el) + return true + } + } + + // create a new range at the beginning + h.ranges.InsertBefore(interval{Start: p, End: p}, h.ranges.Front()) + 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 { + return + } + h.deletedBelow = p + + nextEl := h.ranges.Front() + for el := h.ranges.Front(); nextEl != nil; el = nextEl { + nextEl = el.Next() + + 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 + } else { // no ranges affected. Nothing to do + return + } + } +} + +// 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}) + } + } + 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 + } + return ackRange +} + +func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber) bool { + if p < h.deletedBelow { + return true + } + for el := h.ranges.Back(); el != nil; el = el.Prev() { + if p > el.Value.End { + return false + } + if p <= el.Value.End && p >= el.Value.Start { + return true + } + } + return false +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go new file mode 100644 index 000000000..08af6f1ee --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go @@ -0,0 +1,220 @@ +package ackhandler + +import ( + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +// The receivedPacketTracker tracks packets for the Initial and Handshake packet number space. +// Every received packet is acknowledged immediately. +type receivedPacketTracker struct { + ect0, ect1, ecnce uint64 + + packetHistory receivedPacketHistory + + lastAck *wire.AckFrame + hasNewAck bool // true as soon as we received an ack-eliciting new packet +} + +func newReceivedPacketTracker() *receivedPacketTracker { + return &receivedPacketTracker{packetHistory: *newReceivedPacketHistory()} +} + +func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { + if isNew := h.packetHistory.ReceivedPacket(pn); !isNew { + return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) + } + + //nolint:exhaustive // Only need to count ECT(0), ECT(1) and ECN-CE. + switch ecn { + case protocol.ECT0: + h.ect0++ + case protocol.ECT1: + h.ect1++ + case protocol.ECNCE: + h.ecnce++ + } + if !ackEliciting { + return nil + } + h.hasNewAck = true + return nil +} + +func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame { + if !h.hasNewAck { + return nil + } + + // This function always returns the same ACK frame struct, filled with the most recent values. + ack := h.lastAck + if ack == nil { + ack = &wire.AckFrame{} + } + ack.Reset() + ack.ECT0 = h.ect0 + ack.ECT1 = h.ect1 + ack.ECNCE = h.ecnce + ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges) + + h.lastAck = ack + h.hasNewAck = false + return ack +} + +func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool { + return h.packetHistory.IsPotentiallyDuplicate(pn) +} + +// number of ack-eliciting packets received before sending an ACK +const packetsBeforeAck = 2 + +// The appDataReceivedPacketTracker tracks packets received in the Application Data packet number space. +// It waits until at least 2 packets were received before queueing an ACK, or until the max_ack_delay was reached. +type appDataReceivedPacketTracker struct { + receivedPacketTracker + + largestObservedRcvdTime time.Time + + largestObserved protocol.PacketNumber + ignoreBelow protocol.PacketNumber + + maxAckDelay time.Duration + ackQueued bool // true if we need send a new ACK + + ackElicitingPacketsReceivedSinceLastAck int + ackAlarm time.Time + + logger utils.Logger +} + +func newAppDataReceivedPacketTracker(logger utils.Logger) *appDataReceivedPacketTracker { + h := &appDataReceivedPacketTracker{ + receivedPacketTracker: *newReceivedPacketTracker(), + maxAckDelay: protocol.MaxAckDelay, + logger: logger, + } + return h +} + +func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { + if err := h.receivedPacketTracker.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { + return err + } + if pn >= h.largestObserved { + h.largestObserved = pn + h.largestObservedRcvdTime = rcvTime + } + if !ackEliciting { + return nil + } + h.ackElicitingPacketsReceivedSinceLastAck++ + isMissing := h.isMissing(pn) + if !h.ackQueued && h.shouldQueueACK(pn, ecn, isMissing) { + h.ackQueued = true + h.ackAlarm = time.Time{} // cancel the ack alarm + } + if !h.ackQueued { + // No ACK queued, but we'll need to acknowledge the packet after max_ack_delay. + h.ackAlarm = rcvTime.Add(h.maxAckDelay) + if h.logger.Debug() { + h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay) + } + } + return nil +} + +// IgnoreBelow sets a lower limit for acknowledging packets. +// Packets with packet numbers smaller than p will not be acked. +func (h *appDataReceivedPacketTracker) IgnoreBelow(pn protocol.PacketNumber) { + if pn <= h.ignoreBelow { + return + } + h.ignoreBelow = pn + h.packetHistory.DeleteBelow(pn) + if h.logger.Debug() { + h.logger.Debugf("\tIgnoring all packets below %d.", pn) + } +} + +// isMissing says if a packet was reported missing in the last ACK. +func (h *appDataReceivedPacketTracker) isMissing(p protocol.PacketNumber) bool { + if h.lastAck == nil || p < h.ignoreBelow { + return false + } + return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p) +} + +func (h *appDataReceivedPacketTracker) hasNewMissingPackets() bool { + if h.lastAck == nil { + return false + } + highestRange := h.packetHistory.GetHighestAckRange() + return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 +} + +func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber, ecn protocol.ECN, wasMissing bool) bool { + // always acknowledge the first packet + if h.lastAck == nil { + h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") + return true + } + + // Send an ACK if this packet was reported missing in an ACK sent before. + // Ack decimation with reordering relies on the timer to send an ACK, but if + // missing packets we reported in the previous ACK, send an ACK immediately. + if wasMissing { + if h.logger.Debug() { + h.logger.Debugf("\tQueueing ACK because packet %d was missing before.", pn) + } + return true + } + + // send an ACK every 2 ack-eliciting packets + if h.ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck { + if h.logger.Debug() { + h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.ackElicitingPacketsReceivedSinceLastAck, packetsBeforeAck) + } + return true + } + + // queue an ACK if there are new missing packets to report + if h.hasNewMissingPackets() { + h.logger.Debugf("\tQueuing ACK because there's a new missing packet to report.") + return true + } + + // queue an ACK if the packet was ECN-CE marked + if ecn == protocol.ECNCE { + h.logger.Debugf("\tQueuing ACK because the packet was ECN-CE marked.") + return true + } + return false +} + +func (h *appDataReceivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame { + now := time.Now() + if onlyIfQueued && !h.ackQueued { + if h.ackAlarm.IsZero() || h.ackAlarm.After(now) { + return nil + } + if h.logger.Debug() && !h.ackAlarm.IsZero() { + h.logger.Debugf("Sending ACK because the ACK timer expired.") + } + } + ack := h.receivedPacketTracker.GetAckFrame() + if ack == nil { + return nil + } + ack.DelayTime = max(0, now.Sub(h.largestObservedRcvdTime)) + h.ackQueued = false + h.ackAlarm = time.Time{} + h.ackElicitingPacketsReceivedSinceLastAck = 0 + return ack +} + +func (h *appDataReceivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm } diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go new file mode 100644 index 000000000..c03f3a6f0 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go @@ -0,0 +1,46 @@ +package ackhandler + +import "fmt" + +// The SendMode says what kind of packets can be sent. +type SendMode uint8 + +const ( + // SendNone means that no packets should be sent + SendNone SendMode = iota + // SendAck means an ACK-only packet should be sent + SendAck + // SendPTOInitial means that an Initial probe packet should be sent + SendPTOInitial + // SendPTOHandshake means that a Handshake probe packet should be sent + SendPTOHandshake + // SendPTOAppData means that an Application data probe packet should be sent + SendPTOAppData + // SendPacingLimited means that the pacer doesn't allow sending of a packet right now, + // but will do in a little while. + // The timestamp when sending is allowed again can be obtained via the SentPacketHandler.TimeUntilSend. + SendPacingLimited + // SendAny means that any packet should be sent + SendAny +) + +func (s SendMode) String() string { + switch s { + case SendNone: + return "none" + case SendAck: + return "ack" + case SendPTOInitial: + return "pto (Initial)" + case SendPTOHandshake: + return "pto (Handshake)" + case SendPTOAppData: + return "pto (Application Data)" + case SendAny: + return "any" + case SendPacingLimited: + return "pacing limited" + default: + return fmt.Sprintf("invalid send mode: %d", s) + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go new file mode 100644 index 000000000..3cef89239 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go @@ -0,0 +1,928 @@ +package ackhandler + +import ( + "errors" + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/congestion" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/logging" +) + +const ( + // Maximum reordering in time space before time based loss detection considers a packet lost. + // Specified as an RTT multiplier. + timeThreshold = 9.0 / 8 + // Maximum reordering in packets before packet threshold loss detection considers a packet lost. + packetThreshold = 3 + // Before validating the client's address, the server won't send more than 3x bytes than it received. + amplificationFactor = 3 + // We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet. + minRTTAfterRetry = 5 * time.Millisecond + // The PTO duration uses exponential backoff, but is truncated to a maximum value, as allowed by RFC 8961, section 4.4. + maxPTODuration = 60 * time.Second +) + +type packetNumberSpace struct { + history *sentPacketHistory + pns packetNumberGenerator + + lossTime time.Time + lastAckElicitingPacketTime time.Time + + largestAcked protocol.PacketNumber + largestSent protocol.PacketNumber +} + +func newPacketNumberSpace(initialPN protocol.PacketNumber, skipPNs bool) *packetNumberSpace { + var pns packetNumberGenerator + if skipPNs { + pns = newSkippingPacketNumberGenerator(initialPN, protocol.SkipPacketInitialPeriod, protocol.SkipPacketMaxPeriod) + } else { + pns = newSequentialPacketNumberGenerator(initialPN) + } + return &packetNumberSpace{ + history: newSentPacketHistory(), + pns: pns, + largestSent: protocol.InvalidPacketNumber, + largestAcked: protocol.InvalidPacketNumber, + } +} + +type sentPacketHandler struct { + initialPackets *packetNumberSpace + handshakePackets *packetNumberSpace + appDataPackets *packetNumberSpace + + // Do we know that the peer completed address validation yet? + // Always true for the server. + peerCompletedAddressValidation bool + bytesReceived protocol.ByteCount + bytesSent protocol.ByteCount + // Have we validated the peer's address yet? + // Always true for the client. + peerAddressValidated bool + + handshakeConfirmed bool + + // lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived + // example: we send an ACK for packets 90-100 with packet number 20 + // once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101 + // Only applies to the application-data packet number space. + lowestNotConfirmedAcked protocol.PacketNumber + + ackedPackets []*packet // to avoid allocations in detectAndRemoveAckedPackets + + bytesInFlight protocol.ByteCount + + congestion congestion.SendAlgorithmWithDebugInfos + rttStats *utils.RTTStats + + // The number of times a PTO has been sent without receiving an ack. + ptoCount uint32 + ptoMode SendMode + // The number of PTO probe packets that should be sent. + // Only applies to the application-data packet number space. + numProbesToSend int + + // The alarm timeout + alarm time.Time + + enableECN bool + ecnTracker ecnHandler + + perspective protocol.Perspective + + tracer *logging.ConnectionTracer + logger utils.Logger +} + +var ( + _ SentPacketHandler = &sentPacketHandler{} + _ sentPacketTracker = &sentPacketHandler{} +) + +// clientAddressValidated indicates whether the address was validated beforehand by an address validation token. +// If the address was validated, the amplification limit doesn't apply. It has no effect for a client. +func newSentPacketHandler( + initialPN protocol.PacketNumber, + initialMaxDatagramSize protocol.ByteCount, + rttStats *utils.RTTStats, + clientAddressValidated bool, + enableECN bool, + pers protocol.Perspective, + tracer *logging.ConnectionTracer, + logger utils.Logger, +) *sentPacketHandler { + congestion := congestion.NewCubicSender( + congestion.DefaultClock{}, + rttStats, + initialMaxDatagramSize, + true, // use Reno + tracer, + ) + + h := &sentPacketHandler{ + peerCompletedAddressValidation: pers == protocol.PerspectiveServer, + peerAddressValidated: pers == protocol.PerspectiveClient || clientAddressValidated, + initialPackets: newPacketNumberSpace(initialPN, false), + handshakePackets: newPacketNumberSpace(0, false), + appDataPackets: newPacketNumberSpace(0, true), + rttStats: rttStats, + congestion: congestion, + perspective: pers, + tracer: tracer, + logger: logger, + } + if enableECN { + h.enableECN = true + h.ecnTracker = newECNTracker(logger, tracer) + } + return h +} + +func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) { + if p.includedInBytesInFlight { + if p.Length > h.bytesInFlight { + panic("negative bytes_in_flight") + } + h.bytesInFlight -= p.Length + p.includedInBytesInFlight = false + } +} + +func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { + // 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 { + h.peerCompletedAddressValidation = true + } + // remove outstanding packets from bytes_in_flight + if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake { + pnSpace := h.getPacketNumberSpace(encLevel) + // We might already have dropped this packet number space. + if pnSpace == nil { + return + } + pnSpace.history.Iterate(func(p *packet) (bool, error) { + h.removeFromBytesInFlight(p) + return true, nil + }) + } + // drop the packet history + //nolint:exhaustive // Not every packet number space can be dropped. + switch encLevel { + case protocol.EncryptionInitial: + h.initialPackets = nil + case protocol.EncryptionHandshake: + 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) { + if p.EncryptionLevel != protocol.Encryption0RTT && !p.skippedPacket { + return false, nil + } + h.removeFromBytesInFlight(p) + h.appDataPackets.history.Remove(p.PacketNumber) + return true, nil + }) + default: + panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) + } + if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { + h.tracer.UpdatedPTOCount(0) + } + h.ptoCount = 0 + h.numProbesToSend = 0 + h.ptoMode = SendNone + h.setLossDetectionTimer() +} + +func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) { + wasAmplificationLimit := h.isAmplificationLimited() + h.bytesReceived += n + if wasAmplificationLimit && !h.isAmplificationLimited() { + h.setLossDetectionTimer() + } +} + +func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) { + if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated { + h.peerAddressValidated = true + h.setLossDetectionTimer() + } +} + +func (h *sentPacketHandler) packetsInFlight() int { + packetsInFlight := h.appDataPackets.history.Len() + if h.handshakePackets != nil { + packetsInFlight += h.handshakePackets.history.Len() + } + if h.initialPackets != nil { + packetsInFlight += h.initialPackets.history.Len() + } + return packetsInFlight +} + +func (h *sentPacketHandler) SentPacket( + t time.Time, + pn, largestAcked protocol.PacketNumber, + streamFrames []StreamFrame, + frames []Frame, + encLevel protocol.EncryptionLevel, + ecn protocol.ECN, + size protocol.ByteCount, + isPathMTUProbePacket bool, +) { + h.bytesSent += size + + pnSpace := h.getPacketNumberSpace(encLevel) + if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() { + for p := max(0, pnSpace.largestSent+1); p < pn; p++ { + h.logger.Debugf("Skipping packet number %d", p) + } + } + + pnSpace.largestSent = pn + isAckEliciting := len(streamFrames) > 0 || len(frames) > 0 + + if isAckEliciting { + pnSpace.lastAckElicitingPacketTime = t + h.bytesInFlight += size + if h.numProbesToSend > 0 { + h.numProbesToSend-- + } + } + h.congestion.OnPacketSent(t, h.bytesInFlight, pn, size, isAckEliciting) + + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { + h.ecnTracker.SentPacket(pn, ecn) + } + + if !isAckEliciting { + pnSpace.history.SentNonAckElicitingPacket(pn) + if !h.peerCompletedAddressValidation { + h.setLossDetectionTimer() + } + return + } + + p := getPacket() + p.SendTime = t + p.PacketNumber = pn + p.EncryptionLevel = encLevel + p.Length = size + p.LargestAcked = largestAcked + p.StreamFrames = streamFrames + p.Frames = frames + p.IsPathMTUProbePacket = isPathMTUProbePacket + p.includedInBytesInFlight = true + + pnSpace.history.SentAckElicitingPacket(p) + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { + h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + } + h.setLossDetectionTimer() +} + +func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace { + switch encLevel { + case protocol.EncryptionInitial: + return h.initialPackets + case protocol.EncryptionHandshake: + return h.handshakePackets + case protocol.Encryption0RTT, protocol.Encryption1RTT: + return h.appDataPackets + default: + panic("invalid packet number space") + } +} + +func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) { + pnSpace := h.getPacketNumberSpace(encLevel) + + largestAcked := ack.LargestAcked() + if largestAcked > pnSpace.largestSent { + return false, &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "received ACK for an unsent packet", + } + } + + // Servers complete address validation when a protected packet is received. + if h.perspective == protocol.PerspectiveClient && !h.peerCompletedAddressValidation && + (encLevel == protocol.EncryptionHandshake || encLevel == protocol.Encryption1RTT) { + 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() + } + + priorInFlight := h.bytesInFlight + ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel) + if err != nil || len(ackedPackets) == 0 { + return false, err + } + // update the RTT, if the largest acked is newly acknowledged + if len(ackedPackets) > 0 { + if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() { + // 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) + if h.logger.Debug() { + h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) + } + h.congestion.MaybeExitSlowStart() + } + } + + // Only inform the ECN tracker about new 1-RTT ACKs if the ACK increases the largest acked. + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil && largestAcked > pnSpace.largestAcked { + congested := h.ecnTracker.HandleNewlyAcked(ackedPackets, int64(ack.ECT0), int64(ack.ECT1), int64(ack.ECNCE)) + if congested { + h.congestion.OnCongestionEvent(largestAcked, 0, priorInFlight) + } + } + + pnSpace.largestAcked = max(pnSpace.largestAcked, largestAcked) + + if err := h.detectLostPackets(rcvTime, encLevel); err != nil { + return false, err + } + var acked1RTTPacket bool + for _, p := range ackedPackets { + if p.includedInBytesInFlight && !p.declaredLost { + h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime) + } + if p.EncryptionLevel == protocol.Encryption1RTT { + acked1RTTPacket = true + } + h.removeFromBytesInFlight(p) + putPacket(p) + } + // After this point, we must not use ackedPackets any longer! + // We've already returned the buffers. + ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side. + + // Reset the pto_count unless the client is unsure if the server has validated the client's address. + if h.peerCompletedAddressValidation { + if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { + h.tracer.UpdatedPTOCount(0) + } + h.ptoCount = 0 + } + h.numProbesToSend = 0 + + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { + h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + } + + h.setLossDetectionTimer() + return acked1RTTPacket, nil +} + +func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber { + return h.lowestNotConfirmedAcked +} + +// Packets are returned in ascending packet number order. +func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]*packet, error) { + pnSpace := h.getPacketNumberSpace(encLevel) + h.ackedPackets = h.ackedPackets[:0] + ackRangeIndex := 0 + lowestAcked := ack.LowestAcked() + largestAcked := ack.LargestAcked() + err := pnSpace.history.Iterate(func(p *packet) (bool, error) { + // Ignore packets below the lowest acked + if p.PacketNumber < lowestAcked { + return true, nil + } + // Break after largest acked is reached + if p.PacketNumber > largestAcked { + return false, nil + } + + if ack.HasMissingRanges() { + ackRange := ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] + + for p.PacketNumber > ackRange.Largest && ackRangeIndex < len(ack.AckRanges)-1 { + ackRangeIndex++ + ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] + } + + if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range + return true, nil + } + 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) + } + } + if p.skippedPacket { + return false, &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel), + } + } + 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 { + pns[i] = p.PacketNumber + } + h.logger.Debugf("\tnewly acked packets (%d): %d", len(pns), pns) + } + + for _, p := range h.ackedPackets { + if p.LargestAcked != protocol.InvalidPacketNumber && encLevel == protocol.Encryption1RTT { + h.lowestNotConfirmedAcked = max(h.lowestNotConfirmedAcked, p.LargestAcked+1) + } + + for _, f := range p.Frames { + if f.Handler != nil { + f.Handler.OnAcked(f.Frame) + } + } + for _, f := range p.StreamFrames { + if f.Handler != nil { + f.Handler.OnAcked(f.Frame) + } + } + if err := pnSpace.history.Remove(p.PacketNumber); err != nil { + return nil, err + } + if h.tracer != nil && h.tracer.AcknowledgedPacket != nil { + h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber) + } + } + + return h.ackedPackets, err +} + +func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) { + var encLevel protocol.EncryptionLevel + var lossTime time.Time + + if h.initialPackets != nil { + lossTime = h.initialPackets.lossTime + encLevel = protocol.EncryptionInitial + } + if h.handshakePackets != nil && (lossTime.IsZero() || (!h.handshakePackets.lossTime.IsZero() && h.handshakePackets.lossTime.Before(lossTime))) { + lossTime = h.handshakePackets.lossTime + encLevel = protocol.EncryptionHandshake + } + if lossTime.IsZero() || (!h.appDataPackets.lossTime.IsZero() && h.appDataPackets.lossTime.Before(lossTime)) { + lossTime = h.appDataPackets.lossTime + encLevel = protocol.Encryption1RTT + } + return lossTime, encLevel +} + +func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration { + pto := h.rttStats.PTO(includeMaxAckDelay) << h.ptoCount + if pto > maxPTODuration || pto <= 0 { + return maxPTODuration + } + return pto +} + +// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime +func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) { + // 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)) + if h.initialPackets != nil { + return t, protocol.EncryptionInitial, true + } + return t, protocol.EncryptionHandshake, true + } + + if h.initialPackets != nil { + encLevel = protocol.EncryptionInitial + if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() { + pto = t.Add(h.getScaledPTO(false)) + } + } + if h.handshakePackets != nil && !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() { + 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 +} + +func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { + if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() { + return true + } + if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() { + return true + } + return false +} + +func (h *sentPacketHandler) hasOutstandingPackets() bool { + return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets() +} + +func (h *sentPacketHandler) setLossDetectionTimer() { + 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) + } + 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) + } +} + +func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) error { + pnSpace := h.getPacketNumberSpace(encLevel) + pnSpace.lossTime = time.Time{} + + maxRTT := float64(max(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) + lossDelay := time.Duration(timeThreshold * maxRTT) + + // Minimum time of granularity before packets are deemed lost. + lossDelay = max(lossDelay, protocol.TimerGranularity) + + // Packets sent before this time are deemed lost. + lostSendTime := now.Add(-lossDelay) + + priorInFlight := h.bytesInFlight + return pnSpace.history.Iterate(func(p *packet) (bool, error) { + if p.PacketNumber > pnSpace.largestAcked { + return false, nil + } + + var packetLost bool + if p.SendTime.Before(lostSendTime) { + packetLost = true + if !p.skippedPacket { + if h.logger.Debug() { + h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) + } + if h.tracer != nil && h.tracer.LostPacket != nil { + h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold) + } + } + } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold { + packetLost = true + if !p.skippedPacket { + if h.logger.Debug() { + h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) + } + if h.tracer != nil && h.tracer.LostPacket != nil { + h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossReorderingThreshold) + } + } + } else if pnSpace.lossTime.IsZero() { + // Note: This conditional is only entered once per call + lossTime := p.SendTime.Add(lossDelay) + if h.logger.Debug() { + h.logger.Debugf("\tsetting loss timer for packet %d (%s) to %s (in %s)", p.PacketNumber, encLevel, lossDelay, lossTime) + } + pnSpace.lossTime = lossTime + } + if packetLost { + pnSpace.history.DeclareLost(p.PacketNumber) + if !p.skippedPacket { + // 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) + if !p.IsPathMTUProbePacket { + h.congestion.OnCongestionEvent(p.PacketNumber, p.Length, priorInFlight) + } + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { + h.ecnTracker.LostPacket(p.PacketNumber) + } + } + } + return true, nil + }) +} + +func (h *sentPacketHandler) OnLossDetectionTimeout() error { + defer h.setLossDetectionTimer() + earliestLossTime, encLevel := h.getLossTimeAndSpace() + if !earliestLossTime.IsZero() { + if h.logger.Debug() { + h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime) + } + if h.tracer != nil && h.tracer.LossTimerExpired != nil { + h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel) + } + // Early retransmit or time loss detection + return h.detectLostPackets(time.Now(), encLevel) + } + + // 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 + // actually packets outstanding. + if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation { + h.ptoCount++ + h.numProbesToSend++ + if h.initialPackets != nil { + h.ptoMode = SendPTOInitial + } else if h.handshakePackets != nil { + h.ptoMode = SendPTOHandshake + } else { + return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped") + } + return nil + } + + _, encLevel, ok := h.getPTOTimeAndSpace() + if !ok { + return nil + } + if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation { + return nil + } + h.ptoCount++ + if h.logger.Debug() { + h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount) + } + if h.tracer != nil { + if h.tracer.LossTimerExpired != nil { + h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) + } + if h.tracer.UpdatedPTOCount != nil { + h.tracer.UpdatedPTOCount(h.ptoCount) + } + } + h.numProbesToSend += 2 + //nolint:exhaustive // We never arm a PTO timer for 0-RTT packets. + switch encLevel { + case protocol.EncryptionInitial: + h.ptoMode = SendPTOInitial + case protocol.EncryptionHandshake: + h.ptoMode = SendPTOHandshake + case protocol.Encryption1RTT: + // skip a packet number in order to elicit an immediate ACK + pn := h.PopPacketNumber(protocol.Encryption1RTT) + h.getPacketNumberSpace(protocol.Encryption1RTT).history.SkippedPacket(pn) + h.ptoMode = SendPTOAppData + default: + return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel) + } + return nil +} + +func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time { + return h.alarm +} + +func (h *sentPacketHandler) ECNMode(isShortHeaderPacket bool) protocol.ECN { + if !h.enableECN { + return protocol.ECNUnsupported + } + if !isShortHeaderPacket { + return protocol.ECNNon + } + return h.ecnTracker.Mode() +} + +func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { + pnSpace := h.getPacketNumberSpace(encLevel) + pn := pnSpace.pns.Peek() + // See section 17.1 of RFC 9000. + return pn, protocol.GetPacketNumberLengthForHeader(pn, pnSpace.largestAcked) +} + +func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber { + pnSpace := h.getPacketNumberSpace(encLevel) + skipped, pn := pnSpace.pns.Pop() + if skipped { + skippedPN := pn - 1 + pnSpace.history.SkippedPacket(skippedPN) + if h.logger.Debug() { + h.logger.Debugf("Skipping packet number %d", skippedPN) + } + } + return pn +} + +func (h *sentPacketHandler) SendMode(now time.Time) SendMode { + numTrackedPackets := h.appDataPackets.history.Len() + if h.initialPackets != nil { + numTrackedPackets += h.initialPackets.history.Len() + } + if h.handshakePackets != nil { + numTrackedPackets += h.handshakePackets.history.Len() + } + + if h.isAmplificationLimited() { + h.logger.Debugf("Amplification window limited. Received %d bytes, already sent out %d bytes", h.bytesReceived, h.bytesSent) + return SendNone + } + // Don't send any packets if we're keeping track of the maximum number of packets. + // Note that since MaxOutstandingSentPackets is smaller than MaxTrackedSentPackets, + // we will stop sending out new data when reaching MaxOutstandingSentPackets, + // but still allow sending of retransmissions and ACKs. + if numTrackedPackets >= protocol.MaxTrackedSentPackets { + if h.logger.Debug() { + h.logger.Debugf("Limited by the number of tracked packets: tracking %d packets, maximum %d", numTrackedPackets, protocol.MaxTrackedSentPackets) + } + return SendNone + } + if h.numProbesToSend > 0 { + return h.ptoMode + } + // Only send ACKs if we're congestion limited. + if !h.congestion.CanSend(h.bytesInFlight) { + if h.logger.Debug() { + h.logger.Debugf("Congestion limited: bytes in flight %d, window %d", h.bytesInFlight, h.congestion.GetCongestionWindow()) + } + return SendAck + } + if numTrackedPackets >= protocol.MaxOutstandingSentPackets { + if h.logger.Debug() { + h.logger.Debugf("Max outstanding limited: tracking %d packets, maximum: %d", numTrackedPackets, protocol.MaxOutstandingSentPackets) + } + return SendAck + } + if !h.congestion.HasPacingBudget(now) { + return SendPacingLimited + } + return SendAny +} + +func (h *sentPacketHandler) TimeUntilSend() time.Time { + return h.congestion.TimeUntilSend(h.bytesInFlight) +} + +func (h *sentPacketHandler) SetMaxDatagramSize(s protocol.ByteCount) { + h.congestion.SetMaxDatagramSize(s) +} + +func (h *sentPacketHandler) isAmplificationLimited() bool { + if h.peerAddressValidated { + return false + } + return h.bytesSent >= amplificationFactor*h.bytesReceived +} + +func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool { + pnSpace := h.getPacketNumberSpace(encLevel) + p := pnSpace.history.FirstOutstanding() + if p == nil { + return false + } + h.queueFramesForRetransmission(p) + // TODO: don't declare the packet lost here. + // Keep track of acknowledged frames instead. + h.removeFromBytesInFlight(p) + pnSpace.history.DeclareLost(p.PacketNumber) + return true +} + +func (h *sentPacketHandler) queueFramesForRetransmission(p *packet) { + if len(p.Frames) == 0 && len(p.StreamFrames) == 0 { + panic("no frames") + } + for _, f := range p.Frames { + if f.Handler != nil { + f.Handler.OnLost(f.Frame) + } + } + for _, f := range p.StreamFrames { + if f.Handler != nil { + f.Handler.OnLost(f.Frame) + } + } + p.StreamFrames = nil + p.Frames = nil +} + +func (h *sentPacketHandler) ResetForRetry(now time.Time) error { + h.bytesInFlight = 0 + var firstPacketSendTime time.Time + h.initialPackets.history.Iterate(func(p *packet) (bool, error) { + 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 + }) + + // 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) + if h.logger.Debug() { + h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) + } + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { + h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + } + } + h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Peek(), false) + h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Peek(), true) + oldAlarm := h.alarm + h.alarm = time.Time{} + if h.tracer != nil { + if h.tracer.UpdatedPTOCount != nil { + h.tracer.UpdatedPTOCount(0) + } + if !oldAlarm.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") + } + if h.handshakePackets != nil { + panic("didn't drop handshake correctly") + } + 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() +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go new file mode 100644 index 000000000..c14c0f49b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go @@ -0,0 +1,177 @@ +package ackhandler + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" +) + +type sentPacketHistory struct { + packets []*packet + + numOutstanding int + + highestPacketNumber protocol.PacketNumber +} + +func newSentPacketHistory() *sentPacketHistory { + return &sentPacketHistory{ + packets: make([]*packet, 0, 32), + highestPacketNumber: protocol.InvalidPacketNumber, + } +} + +func (h *sentPacketHistory) checkSequentialPacketNumberUse(pn protocol.PacketNumber) { + if h.highestPacketNumber != protocol.InvalidPacketNumber { + if pn != h.highestPacketNumber+1 { + panic("non-sequential packet number use") + } + } +} + +func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) { + h.checkSequentialPacketNumberUse(pn) + h.highestPacketNumber = pn + h.packets = append(h.packets, &packet{ + PacketNumber: pn, + skippedPacket: true, + }) +} + +func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) { + h.checkSequentialPacketNumberUse(pn) + h.highestPacketNumber = pn + if len(h.packets) > 0 { + h.packets = append(h.packets, nil) + } +} + +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 + } + } + return nil +} + +// FirstOutstanding returns the first outstanding packet. +func (h *sentPacketHistory) FirstOutstanding() *packet { + if !h.HasOutstandingPackets() { + return nil + } + for _, p := range h.packets { + if p != nil && p.outstanding() { + return p + } + } + return nil +} + +func (h *sentPacketHistory) Len() int { + return len(h.packets) +} + +func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error { + idx, ok := h.getIndex(pn) + if !ok { + return fmt.Errorf("packet %d not found in sent packet history", pn) + } + p := h.packets[idx] + if p.outstanding() { + h.numOutstanding-- + if h.numOutstanding < 0 { + panic("negative number of outstanding packets") + } + } + h.packets[idx] = nil + // clean up all skipped packets directly before this packet number + for idx > 0 { + idx-- + p := h.packets[idx] + if p == nil || !p.skippedPacket { + break + } + h.packets[idx] = nil + } + if idx == 0 { + h.cleanupStart() + } + if len(h.packets) > 0 && h.packets[0] == nil { + panic("remove failed") + } + return nil +} + +// 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 { + return 0, false + } + first := h.packets[0].PacketNumber + if p < first { + return 0, false + } + index := int(p - first) + if index > len(h.packets)-1 { + return 0, false + } + return index, true +} + +func (h *sentPacketHistory) HasOutstandingPackets() bool { + return h.numOutstanding > 0 +} + +// delete all nil entries at the beginning of the packets slice +func (h *sentPacketHistory) cleanupStart() { + for i, p := range h.packets { + if p != nil { + h.packets = h.packets[i:] + return + } + } + h.packets = h.packets[:0] +} + +func (h *sentPacketHistory) LowestPacketNumber() protocol.PacketNumber { + if len(h.packets) == 0 { + return protocol.InvalidPacketNumber + } + return h.packets[0].PacketNumber +} + +func (h *sentPacketHistory) DeclareLost(pn protocol.PacketNumber) { + idx, ok := h.getIndex(pn) + if !ok { + return + } + p := h.packets[idx] + if p.outstanding() { + h.numOutstanding-- + if h.numOutstanding < 0 { + panic("negative number of outstanding packets") + } + } + h.packets[idx] = nil + if idx == 0 { + h.cleanupStart() + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go b/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go new file mode 100644 index 000000000..1d03abbb8 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go @@ -0,0 +1,25 @@ +package congestion + +import ( + "math" + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// Bandwidth of a connection +type Bandwidth uint64 + +const infBandwidth Bandwidth = math.MaxUint64 + +const ( + // BitsPerSecond is 1 bit per second + BitsPerSecond Bandwidth = 1 + // BytesPerSecond is 1 byte per second + BytesPerSecond = 8 * BitsPerSecond +) + +// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta +func BandwidthFromDelta(bytes protocol.ByteCount, delta time.Duration) Bandwidth { + return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go b/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go new file mode 100644 index 000000000..405fae70f --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go @@ -0,0 +1,18 @@ +package congestion + +import "time" + +// A Clock returns the current time +type Clock interface { + Now() time.Time +} + +// DefaultClock implements the Clock interface using the Go stdlib clock. +type DefaultClock struct{} + +var _ Clock = DefaultClock{} + +// Now gets the current time +func (DefaultClock) Now() time.Time { + return time.Now() +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go new file mode 100644 index 000000000..4e30de650 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go @@ -0,0 +1,213 @@ +package congestion + +import ( + "math" + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// This cubic implementation is based on the one found in Chromiums's QUIC +// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}. + +// Constants based on TCP defaults. +// The following constants are in 2^10 fractions of a second instead of ms to +// allow a 10 shift right to divide. + +// 1024*1024^3 (first 1024 is from 0.100^3) +// where 0.100 is 100 ms which is the scaling round trip time. +const ( + cubeScale = 40 + cubeCongestionWindowScale = 410 + cubeFactor protocol.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize + // TODO: when re-enabling cubic, make sure to use the actual packet size here + maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4) +) + +const defaultNumConnections = 1 + +// Default Cubic backoff factor +const beta float32 = 0.7 + +// Additional backoff factor when loss occurs in the concave part of the Cubic +// curve. This additional backoff factor is expected to give up bandwidth to +// new concurrent flows and speed up convergence. +const betaLastMax float32 = 0.85 + +// Cubic implements the cubic algorithm from TCP +type Cubic struct { + clock Clock + + // Number of connections to simulate. + numConnections int + + // Time when this cycle started, after last loss event. + epoch time.Time + + // Max congestion window used just before last loss event. + // Note: to improve fairness to other streams an additional back off is + // applied to this value if the new value is below our latest value. + lastMaxCongestionWindow protocol.ByteCount + + // Number of acked bytes since the cycle started (epoch). + ackedBytesCount protocol.ByteCount + + // TCP Reno equivalent congestion window in packets. + estimatedTCPcongestionWindow protocol.ByteCount + + // Origin point of cubic function. + originPointCongestionWindow protocol.ByteCount + + // Time to origin point of cubic function in 2^10 fractions of a second. + timeToOriginPoint uint32 + + // Last congestion window in packets computed by cubic function. + lastTargetCongestionWindow protocol.ByteCount +} + +// NewCubic returns a new Cubic instance +func NewCubic(clock Clock) *Cubic { + c := &Cubic{ + clock: clock, + numConnections: defaultNumConnections, + } + c.Reset() + return c +} + +// Reset is called after a timeout to reset the cubic state +func (c *Cubic) Reset() { + c.epoch = time.Time{} + c.lastMaxCongestionWindow = 0 + c.ackedBytesCount = 0 + c.estimatedTCPcongestionWindow = 0 + c.originPointCongestionWindow = 0 + c.timeToOriginPoint = 0 + c.lastTargetCongestionWindow = 0 +} + +func (c *Cubic) alpha() float32 { + // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that + // beta here is a cwnd multiplier, and is equal to 1-beta from the paper. + // We derive the equivalent alpha for an N-connection emulation as: + b := c.beta() + return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b) +} + +func (c *Cubic) beta() float32 { + // kNConnectionBeta is the backoff factor after loss for our N-connection + // emulation, which emulates the effective backoff of an ensemble of N + // TCP-Reno connections on a single loss event. The effective multiplier is + // computed as: + return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections) +} + +func (c *Cubic) betaLastMax() float32 { + // betaLastMax is the additional backoff factor after loss for our + // N-connection emulation, which emulates the additional backoff of + // an ensemble of N TCP-Reno connections on a single loss event. The + // effective multiplier is computed as: + return (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections) +} + +// OnApplicationLimited is called on ack arrival when sender is unable to use +// the available congestion window. Resets Cubic state during quiescence. +func (c *Cubic) OnApplicationLimited() { + // When sender is not using the available congestion window, the window does + // not grow. But to be RTT-independent, Cubic assumes that the sender has been + // using the entire window during the time since the beginning of the current + // "epoch" (the end of the last loss recovery period). Since + // application-limited periods break this assumption, we reset the epoch when + // in such a period. This reset effectively freezes congestion window growth + // through application-limited periods and allows Cubic growth to continue + // when the entire window is being used. + c.epoch = time.Time{} +} + +// CongestionWindowAfterPacketLoss computes a new congestion window to use after +// a loss event. Returns the new congestion window in packets. The new +// congestion window is a multiplicative decrease of our current window. +func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol.ByteCount) protocol.ByteCount { + if currentCongestionWindow+maxDatagramSize < c.lastMaxCongestionWindow { + // We never reached the old max, so assume we are competing with another + // flow. Use our extra back off factor to allow the other flow to go up. + c.lastMaxCongestionWindow = protocol.ByteCount(c.betaLastMax() * float32(currentCongestionWindow)) + } else { + c.lastMaxCongestionWindow = currentCongestionWindow + } + c.epoch = time.Time{} // Reset time. + return protocol.ByteCount(float32(currentCongestionWindow) * c.beta()) +} + +// CongestionWindowAfterAck computes a new congestion window to use after a received ACK. +// Returns the new congestion window in packets. The new congestion window +// follows a cubic function that depends on the time passed since last +// packet loss. +func (c *Cubic) CongestionWindowAfterAck( + ackedBytes protocol.ByteCount, + currentCongestionWindow protocol.ByteCount, + delayMin time.Duration, + eventTime time.Time, +) protocol.ByteCount { + c.ackedBytesCount += ackedBytes + + if c.epoch.IsZero() { + // First ACK after a loss event. + c.epoch = eventTime // Start of epoch. + c.ackedBytesCount = ackedBytes // Reset count. + // Reset estimated_tcp_congestion_window_ to be in sync with cubic. + c.estimatedTCPcongestionWindow = currentCongestionWindow + if c.lastMaxCongestionWindow <= currentCongestionWindow { + c.timeToOriginPoint = 0 + c.originPointCongestionWindow = currentCongestionWindow + } else { + c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow)))) + c.originPointCongestionWindow = c.lastMaxCongestionWindow + } + } + + // Change the time unit from microseconds to 2^10 fractions per second. Take + // the round trip time in account. This is done to allow us to use shift as a + // divide operator. + elapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000) + + // Right-shifts of negative, signed numbers have implementation-dependent + // behavior, so force the offset to be positive, as is done in the kernel. + offset := int64(c.timeToOriginPoint) - elapsedTime + if offset < 0 { + offset = -offset + } + + deltaCongestionWindow := protocol.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * maxDatagramSize >> cubeScale + var targetCongestionWindow protocol.ByteCount + if elapsedTime > int64(c.timeToOriginPoint) { + targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow + } else { + targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow + } + // Limit the CWND increase to half the acked bytes. + targetCongestionWindow = min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) + + // Increase the window by approximately Alpha * 1 MSS of bytes every + // time we ack an estimated tcp window of bytes. For small + // congestion windows (less than 25), the formula below will + // increase slightly slower than linearly per estimated tcp window + // of bytes. + c.estimatedTCPcongestionWindow += protocol.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(maxDatagramSize) / float32(c.estimatedTCPcongestionWindow)) + c.ackedBytesCount = 0 + + // We have a new cubic congestion window. + c.lastTargetCongestionWindow = targetCongestionWindow + + // Compute target congestion_window based on cubic target and estimated TCP + // congestion_window, use highest (fastest). + if targetCongestionWindow < c.estimatedTCPcongestionWindow { + targetCongestionWindow = c.estimatedTCPcongestionWindow + } + return targetCongestionWindow +} + +// SetNumConnections sets the number of emulated connections +func (c *Cubic) SetNumConnections(n int) { + c.numConnections = n +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go new file mode 100644 index 000000000..a1b06ab34 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go @@ -0,0 +1,316 @@ +package congestion + +import ( + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +const ( + // maxDatagramSize is the default maximum packet size used in the Linux TCP implementation. + // Used in QUIC for congestion window computations in bytes. + initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4) + maxBurstPackets = 3 + renoBeta = 0.7 // Reno backoff factor. + minCongestionWindowPackets = 2 + initialCongestionWindow = 32 +) + +type cubicSender struct { + hybridSlowStart HybridSlowStart + rttStats *utils.RTTStats + cubic *Cubic + pacer *pacer + clock Clock + + reno bool + + // Track the largest packet that has been sent. + largestSentPacketNumber protocol.PacketNumber + + // Track the largest packet that has been acked. + largestAckedPacketNumber protocol.PacketNumber + + // Track the largest packet number outstanding when a CWND cutback occurs. + largestSentAtLastCutback protocol.PacketNumber + + // Whether the last loss event caused us to exit slowstart. + // Used for stats collection of slowstartPacketsLost + lastCutbackExitedSlowstart bool + + // Congestion window in bytes. + congestionWindow protocol.ByteCount + + // Slow start congestion window in bytes, aka ssthresh. + slowStartThreshold protocol.ByteCount + + // ACK counter for the Reno implementation. + numAckedPackets uint64 + + initialCongestionWindow protocol.ByteCount + initialMaxCongestionWindow protocol.ByteCount + + maxDatagramSize protocol.ByteCount + + lastState logging.CongestionState + tracer *logging.ConnectionTracer +} + +var ( + _ SendAlgorithm = &cubicSender{} + _ SendAlgorithmWithDebugInfos = &cubicSender{} +) + +// NewCubicSender makes a new cubic sender +func NewCubicSender( + clock Clock, + rttStats *utils.RTTStats, + initialMaxDatagramSize protocol.ByteCount, + reno bool, + tracer *logging.ConnectionTracer, +) *cubicSender { + return newCubicSender( + clock, + rttStats, + reno, + initialMaxDatagramSize, + initialCongestionWindow*initialMaxDatagramSize, + protocol.MaxCongestionWindowPackets*initialMaxDatagramSize, + tracer, + ) +} + +func newCubicSender( + clock Clock, + rttStats *utils.RTTStats, + reno bool, + initialMaxDatagramSize, + initialCongestionWindow, + initialMaxCongestionWindow protocol.ByteCount, + tracer *logging.ConnectionTracer, +) *cubicSender { + c := &cubicSender{ + rttStats: rttStats, + largestSentPacketNumber: protocol.InvalidPacketNumber, + largestAckedPacketNumber: protocol.InvalidPacketNumber, + largestSentAtLastCutback: protocol.InvalidPacketNumber, + initialCongestionWindow: initialCongestionWindow, + initialMaxCongestionWindow: initialMaxCongestionWindow, + congestionWindow: initialCongestionWindow, + slowStartThreshold: protocol.MaxByteCount, + cubic: NewCubic(clock), + clock: clock, + reno: reno, + tracer: tracer, + maxDatagramSize: initialMaxDatagramSize, + } + c.pacer = newPacer(c.BandwidthEstimate) + if c.tracer != nil && c.tracer.UpdatedCongestionState != nil { + c.lastState = logging.CongestionStateSlowStart + c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart) + } + return c +} + +// TimeUntilSend returns when the next packet should be sent. +func (c *cubicSender) TimeUntilSend(_ protocol.ByteCount) time.Time { + return c.pacer.TimeUntilSend() +} + +func (c *cubicSender) HasPacingBudget(now time.Time) bool { + return c.pacer.Budget(now) >= c.maxDatagramSize +} + +func (c *cubicSender) maxCongestionWindow() protocol.ByteCount { + return c.maxDatagramSize * protocol.MaxCongestionWindowPackets +} + +func (c *cubicSender) minCongestionWindow() protocol.ByteCount { + return c.maxDatagramSize * minCongestionWindowPackets +} + +func (c *cubicSender) OnPacketSent( + sentTime time.Time, + _ protocol.ByteCount, + packetNumber protocol.PacketNumber, + bytes protocol.ByteCount, + isRetransmittable bool, +) { + c.pacer.SentPacket(sentTime, bytes) + if !isRetransmittable { + return + } + c.largestSentPacketNumber = packetNumber + c.hybridSlowStart.OnPacketSent(packetNumber) +} + +func (c *cubicSender) CanSend(bytesInFlight protocol.ByteCount) bool { + return bytesInFlight < c.GetCongestionWindow() +} + +func (c *cubicSender) InRecovery() bool { + return c.largestAckedPacketNumber != protocol.InvalidPacketNumber && c.largestAckedPacketNumber <= c.largestSentAtLastCutback +} + +func (c *cubicSender) InSlowStart() bool { + return c.GetCongestionWindow() < c.slowStartThreshold +} + +func (c *cubicSender) GetCongestionWindow() protocol.ByteCount { + return c.congestionWindow +} + +func (c *cubicSender) MaybeExitSlowStart() { + if c.InSlowStart() && + c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) { + // exit slow start + c.slowStartThreshold = c.congestionWindow + c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + } +} + +func (c *cubicSender) OnPacketAcked( + ackedPacketNumber protocol.PacketNumber, + ackedBytes protocol.ByteCount, + priorInFlight protocol.ByteCount, + eventTime time.Time, +) { + c.largestAckedPacketNumber = max(ackedPacketNumber, c.largestAckedPacketNumber) + if c.InRecovery() { + return + } + c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime) + if c.InSlowStart() { + c.hybridSlowStart.OnPacketAcked(ackedPacketNumber) + } +} + +func (c *cubicSender) OnCongestionEvent(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) { + // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets + // already sent should be treated as a single loss event, since it's expected. + if packetNumber <= c.largestSentAtLastCutback { + return + } + c.lastCutbackExitedSlowstart = c.InSlowStart() + c.maybeTraceStateChange(logging.CongestionStateRecovery) + + if c.reno { + c.congestionWindow = protocol.ByteCount(float64(c.congestionWindow) * renoBeta) + } else { + c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow) + } + if minCwnd := c.minCongestionWindow(); c.congestionWindow < minCwnd { + c.congestionWindow = minCwnd + } + c.slowStartThreshold = c.congestionWindow + c.largestSentAtLastCutback = c.largestSentPacketNumber + // reset packet count from congestion avoidance mode. We start + // counting again when we're out of recovery. + c.numAckedPackets = 0 +} + +// Called when we receive an ack. Normal TCP tracks how many packets one ack +// represents, but quic has a separate ack for each packet. +func (c *cubicSender) maybeIncreaseCwnd( + _ protocol.PacketNumber, + ackedBytes protocol.ByteCount, + priorInFlight protocol.ByteCount, + eventTime time.Time, +) { + // Do not increase the congestion window unless the sender is close to using + // the current window. + if !c.isCwndLimited(priorInFlight) { + c.cubic.OnApplicationLimited() + c.maybeTraceStateChange(logging.CongestionStateApplicationLimited) + return + } + if c.congestionWindow >= c.maxCongestionWindow() { + return + } + if c.InSlowStart() { + // TCP slow start, exponential growth, increase by one for each ACK. + c.congestionWindow += c.maxDatagramSize + c.maybeTraceStateChange(logging.CongestionStateSlowStart) + return + } + // Congestion avoidance + c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + if c.reno { + // Classic Reno congestion avoidance. + c.numAckedPackets++ + if c.numAckedPackets >= uint64(c.congestionWindow/c.maxDatagramSize) { + c.congestionWindow += c.maxDatagramSize + c.numAckedPackets = 0 + } + } else { + c.congestionWindow = min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) + } +} + +func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool { + congestionWindow := c.GetCongestionWindow() + if bytesInFlight >= congestionWindow { + return true + } + availableBytes := congestionWindow - bytesInFlight + slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2 + return slowStartLimited || availableBytes <= maxBurstPackets*c.maxDatagramSize +} + +// BandwidthEstimate returns the current bandwidth estimate +func (c *cubicSender) BandwidthEstimate() Bandwidth { + srtt := c.rttStats.SmoothedRTT() + if srtt == 0 { + // If we haven't measured an rtt, the bandwidth estimate is unknown. + return infBandwidth + } + return BandwidthFromDelta(c.GetCongestionWindow(), srtt) +} + +// OnRetransmissionTimeout is called on an retransmission timeout +func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) { + c.largestSentAtLastCutback = protocol.InvalidPacketNumber + if !packetsRetransmitted { + return + } + c.hybridSlowStart.Restart() + c.cubic.Reset() + c.slowStartThreshold = c.congestionWindow / 2 + c.congestionWindow = c.minCongestionWindow() +} + +// OnConnectionMigration is called when the connection is migrated (?) +func (c *cubicSender) OnConnectionMigration() { + c.hybridSlowStart.Restart() + c.largestSentPacketNumber = protocol.InvalidPacketNumber + c.largestAckedPacketNumber = protocol.InvalidPacketNumber + c.largestSentAtLastCutback = protocol.InvalidPacketNumber + c.lastCutbackExitedSlowstart = false + c.cubic.Reset() + c.numAckedPackets = 0 + c.congestionWindow = c.initialCongestionWindow + c.slowStartThreshold = c.initialMaxCongestionWindow +} + +func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) { + if c.tracer == nil || c.tracer.UpdatedCongestionState == nil || new == c.lastState { + return + } + c.tracer.UpdatedCongestionState(new) + c.lastState = new +} + +func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) { + if s < c.maxDatagramSize { + panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s)) + } + cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow() + c.maxDatagramSize = s + if cwndIsMinCwnd { + c.congestionWindow = c.minCongestionWindow() + } + c.pacer.SetMaxDatagramSize(s) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go b/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go new file mode 100644 index 000000000..9679d9e46 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go @@ -0,0 +1,112 @@ +package congestion + +import ( + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// Note(pwestin): the magic clamping numbers come from the original code in +// tcp_cubic.c. +const hybridStartLowWindow = protocol.ByteCount(16) + +// Number of delay samples for detecting the increase of delay. +const hybridStartMinSamples = uint32(8) + +// Exit slow start if the min rtt has increased by more than 1/8th. +const hybridStartDelayFactorExp = 3 // 2^3 = 8 +// The original paper specifies 2 and 8ms, but those have changed over time. +const ( + hybridStartDelayMinThresholdUs = int64(4000) + hybridStartDelayMaxThresholdUs = int64(16000) +) + +// HybridSlowStart implements the TCP hybrid slow start algorithm +type HybridSlowStart struct { + endPacketNumber protocol.PacketNumber + lastSentPacketNumber protocol.PacketNumber + started bool + currentMinRTT time.Duration + rttSampleCount uint32 + hystartFound bool +} + +// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase. +func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) { + s.endPacketNumber = lastSent + s.currentMinRTT = 0 + s.rttSampleCount = 0 + s.started = true +} + +// IsEndOfRound returns true if this ack is the last packet number of our current slow start round. +func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool { + return s.endPacketNumber < ack +} + +// ShouldExitSlowStart should be called on every new ack frame, since a new +// RTT measurement can be made then. +// rtt: the RTT for this ack packet. +// minRTT: is the lowest delay (RTT) we have seen during the session. +// congestionWindow: the congestion window in packets. +func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool { + if !s.started { + // Time to start the hybrid slow start. + s.StartReceiveRound(s.lastSentPacketNumber) + } + if s.hystartFound { + return true + } + // Second detection parameter - delay increase detection. + // Compare the minimum delay (s.currentMinRTT) of the current + // burst of packets relative to the minimum delay during the session. + // Note: we only look at the first few(8) packets in each burst, since we + // only want to compare the lowest RTT of the burst relative to previous + // bursts. + s.rttSampleCount++ + if s.rttSampleCount <= hybridStartMinSamples { + if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT { + s.currentMinRTT = latestRTT + } + } + // We only need to check this once per round. + if s.rttSampleCount == hybridStartMinSamples { + // Divide minRTT by 8 to get a rtt increase threshold for exiting. + minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) + // Ensure the rtt threshold is never less than 2ms or more than 16ms. + minRTTincreaseThresholdUs = min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) + minRTTincreaseThreshold := time.Duration(max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond + + if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { + s.hystartFound = true + } + } + // Exit from slow start if the cwnd is greater than 16 and + // increasing delay is found. + return congestionWindow >= hybridStartLowWindow && s.hystartFound +} + +// OnPacketSent is called when a packet was sent +func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) { + s.lastSentPacketNumber = packetNumber +} + +// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end +// the round when the final packet of the burst is received and start it on +// the next incoming ack. +func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) { + if s.IsEndOfRound(ackedPacketNumber) { + s.started = false + } +} + +// Started returns true if started +func (s *HybridSlowStart) Started() bool { + return s.started +} + +// Restart the slow start phase +func (s *HybridSlowStart) Restart() { + s.started = false + s.hystartFound = false +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go new file mode 100644 index 000000000..881f453b6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go @@ -0,0 +1,28 @@ +package congestion + +import ( + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// A SendAlgorithm performs congestion control +type SendAlgorithm interface { + TimeUntilSend(bytesInFlight protocol.ByteCount) time.Time + HasPacingBudget(now time.Time) bool + OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) + CanSend(bytesInFlight protocol.ByteCount) bool + MaybeExitSlowStart() + OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time) + OnCongestionEvent(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) + OnRetransmissionTimeout(packetsRetransmitted bool) + SetMaxDatagramSize(protocol.ByteCount) +} + +// A SendAlgorithmWithDebugInfos is a SendAlgorithm that exposes some debug infos +type SendAlgorithmWithDebugInfos interface { + SendAlgorithm + InSlowStart() bool + InRecovery() bool + GetCongestionWindow() protocol.ByteCount +} diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go new file mode 100644 index 000000000..34d3d1d09 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go @@ -0,0 +1,84 @@ +package congestion + +import ( + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +const maxBurstSizePackets = 10 + +// The pacer implements a token bucket pacing algorithm. +type pacer struct { + budgetAtLastSent protocol.ByteCount + maxDatagramSize protocol.ByteCount + lastSentTime time.Time + adjustedBandwidth func() uint64 // in bytes/s +} + +func newPacer(getBandwidth func() Bandwidth) *pacer { + p := &pacer{ + maxDatagramSize: initialMaxDatagramSize, + adjustedBandwidth: func() uint64 { + // Bandwidth is in bits/s. We need the value in bytes/s. + bw := uint64(getBandwidth() / BytesPerSecond) + // Use a slightly higher value than the actual measured bandwidth. + // RTT variations then won't result in under-utilization of the congestion window. + // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire, + // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals. + return bw * 5 / 4 + }, + } + p.budgetAtLastSent = p.maxBurstSize() + return p +} + +func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { + budget := p.Budget(sendTime) + if size >= budget { + p.budgetAtLastSent = 0 + } else { + p.budgetAtLastSent = budget - size + } + p.lastSentTime = sendTime +} + +func (p *pacer) Budget(now time.Time) protocol.ByteCount { + if p.lastSentTime.IsZero() { + return p.maxBurstSize() + } + budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + if budget < 0 { // protect against overflows + budget = protocol.MaxByteCount + } + return min(p.maxBurstSize(), budget) +} + +func (p *pacer) maxBurstSize() protocol.ByteCount { + return max( + protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9, + maxBurstSizePackets*p.maxDatagramSize, + ) +} + +// TimeUntilSend returns when the next packet should be sent. +// It returns the zero value of time.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() time.Time { + if p.budgetAtLastSent >= p.maxDatagramSize { + return time.Time{} + } + diff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent) + bw := p.adjustedBandwidth() + // We might need to round up this value. + // Otherwise, we might have a budget (slightly) smaller than the datagram size when the timer expires. + d := diff / bw + // this is effectively a math.Ceil, but using only integer math + if diff%bw > 0 { + d++ + } + return p.lastSentTime.Add(max(protocol.MinPacingDelay, time.Duration(d)*time.Nanosecond)) +} + +func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) { + p.maxDatagramSize = s +} diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go new file mode 100644 index 000000000..3d88d577e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go @@ -0,0 +1,127 @@ +package flowcontrol + +import ( + "sync" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +type baseFlowController struct { + // for sending data + bytesSent protocol.ByteCount + sendWindow protocol.ByteCount + lastBlockedAt protocol.ByteCount + + // for receiving data + //nolint:structcheck // The mutex is used both by the stream and the connection flow controller + mutex sync.Mutex + bytesRead protocol.ByteCount + highestReceived protocol.ByteCount + receiveWindow protocol.ByteCount + receiveWindowSize protocol.ByteCount + maxReceiveWindowSize protocol.ByteCount + + allowWindowIncrease func(size protocol.ByteCount) bool + + epochStartTime time.Time + epochStartOffset protocol.ByteCount + rttStats *utils.RTTStats + + logger utils.Logger +} + +// IsNewlyBlocked says if it is newly blocked by flow control. +// 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 { + return false, 0 + } + c.lastBlockedAt = c.sendWindow + return true, c.sendWindow +} + +func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) { + c.bytesSent += n +} + +// UpdateSendWindow is called after receiving a MAX_{STREAM_}DATA frame. +func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) (updated bool) { + if offset > c.sendWindow { + c.sendWindow = offset + return true + } + return false +} + +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 + } + return c.sendWindow - c.bytesSent +} + +// 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 +} + +func (c *baseFlowController) hasWindowUpdate() bool { + bytesRemaining := c.receiveWindow - c.bytesRead + // update the window when more than the threshold was consumed + return bytesRemaining <= protocol.ByteCount(float64(c.receiveWindowSize)*(1-protocol.WindowUpdateThreshold)) +} + +// getWindowUpdate updates the receive window, if necessary +// it returns the new offset +func (c *baseFlowController) getWindowUpdate() protocol.ByteCount { + if !c.hasWindowUpdate() { + return 0 + } + + c.maybeAdjustWindowSize() + 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() { + bytesReadInEpoch := c.bytesRead - c.epochStartOffset + // don't do anything if less than half the window has been consumed + if bytesReadInEpoch <= c.receiveWindowSize/2 { + return + } + rtt := c.rttStats.SmoothedRTT() + if rtt == 0 { + return + } + + 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) + if newSize > c.receiveWindowSize && (c.allowWindowIncrease == nil || c.allowWindowIncrease(newSize-c.receiveWindowSize)) { + c.receiveWindowSize = newSize + } + } + c.startNewAutoTuningEpoch(now) +} + +func (c *baseFlowController) startNewAutoTuningEpoch(now time.Time) { + c.epochStartTime = now + c.epochStartOffset = c.bytesRead +} + +func (c *baseFlowController) checkFlowControlViolation() bool { + return c.highestReceived > c.receiveWindow +} diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go new file mode 100644 index 000000000..8504cdcf5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go @@ -0,0 +1,112 @@ +package flowcontrol + +import ( + "errors" + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" +) + +type connectionFlowController struct { + baseFlowController + + queueWindowUpdate func() +} + +var _ ConnectionFlowController = &connectionFlowController{} + +// NewConnectionFlowController gets a new flow controller for the connection +// It is created before we receive the peer's transport parameters, thus it starts with a sendWindow of 0. +func NewConnectionFlowController( + receiveWindow protocol.ByteCount, + maxReceiveWindow protocol.ByteCount, + queueWindowUpdate func(), + allowWindowIncrease func(size protocol.ByteCount) bool, + rttStats *utils.RTTStats, + logger utils.Logger, +) ConnectionFlowController { + return &connectionFlowController{ + baseFlowController: baseFlowController{ + rttStats: rttStats, + receiveWindow: receiveWindow, + receiveWindowSize: receiveWindow, + maxReceiveWindowSize: maxReceiveWindow, + 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 { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.highestReceived += increment + if c.checkFlowControlViolation() { + return &qerr.TransportError{ + ErrorCode: qerr.FlowControlError, + ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow), + } + } + return nil +} + +func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) { + c.mutex.Lock() + c.baseFlowController.addBytesRead(n) + shouldQueueWindowUpdate := c.hasWindowUpdate() + c.mutex.Unlock() + if shouldQueueWindowUpdate { + c.queueWindowUpdate() + } +} + +func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount { + c.mutex.Lock() + oldWindowSize := c.receiveWindowSize + offset := c.baseFlowController.getWindowUpdate() + if 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) { + 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()) + } + c.mutex.Unlock() +} + +// 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. +// 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() + defer c.mutex.Unlock() + + if c.bytesRead > 0 || c.highestReceived > 0 || !c.epochStartTime.IsZero() { + return errors.New("flow controller reset after reading data") + } + c.bytesSent = 0 + c.lastBlockedAt = 0 + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go new file mode 100644 index 000000000..fc5f9de0a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go @@ -0,0 +1,41 @@ +package flowcontrol + +import "github.com/quic-go/quic-go/internal/protocol" + +type flowController interface { + // for sending + SendWindowSize() protocol.ByteCount + UpdateSendWindow(protocol.ByteCount) (updated bool) + AddBytesSent(protocol.ByteCount) + // for receiving + AddBytesRead(protocol.ByteCount) + GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary + IsNewlyBlocked() (bool, protocol.ByteCount) +} + +// A StreamFlowController is a flow controller for a QUIC stream. +type StreamFlowController interface { + flowController + // UpdateHighestReceived is called when a new highest offset is received + // final has to be to true if this is the final offset of the stream, + // as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame + UpdateHighestReceived(offset protocol.ByteCount, final bool) error + // Abandon is called when reading from the stream is aborted early, + // and there won't be any further calls to AddBytesRead. + Abandon() +} + +// The ConnectionFlowController is the flow controller for the connection. +type ConnectionFlowController interface { + flowController + Reset() error +} + +type connectionFlowControllerI interface { + ConnectionFlowController + // The following two methods are not supposed to be called from outside this packet, but are needed internally + // for sending + EnsureMinimumWindowSize(protocol.ByteCount) + // for receiving + IncrementHighestReceived(protocol.ByteCount) error +} diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go new file mode 100644 index 000000000..1a69fb2b3 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go @@ -0,0 +1,149 @@ +package flowcontrol + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" +) + +type streamFlowController struct { + baseFlowController + + streamID protocol.StreamID + + queueWindowUpdate func() + + connection connectionFlowControllerI + + receivedFinalOffset bool +} + +var _ StreamFlowController = &streamFlowController{} + +// NewStreamFlowController gets a new flow controller for a stream +func NewStreamFlowController( + streamID protocol.StreamID, + cfc ConnectionFlowController, + receiveWindow protocol.ByteCount, + maxReceiveWindow protocol.ByteCount, + initialSendWindow protocol.ByteCount, + queueWindowUpdate func(protocol.StreamID), + rttStats *utils.RTTStats, + logger utils.Logger, +) StreamFlowController { + return &streamFlowController{ + streamID: streamID, + connection: cfc.(connectionFlowControllerI), + queueWindowUpdate: func() { queueWindowUpdate(streamID) }, + baseFlowController: baseFlowController{ + rttStats: rttStats, + receiveWindow: receiveWindow, + receiveWindowSize: receiveWindow, + maxReceiveWindowSize: maxReceiveWindow, + sendWindow: initialSendWindow, + logger: logger, + }, + } +} + +// UpdateHighestReceived updates the highestReceived value, if the offset is higher. +func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error { + // If the final offset for this stream is already known, check for consistency. + if c.receivedFinalOffset { + // If we receive another final offset, check that it's the same. + if final && offset != c.highestReceived { + return &qerr.TransportError{ + ErrorCode: qerr.FinalSizeError, + ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset), + } + } + // Check that the offset is below the final offset. + if offset > c.highestReceived { + return &qerr.TransportError{ + ErrorCode: qerr.FinalSizeError, + ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived), + } + } + } + + if final { + c.receivedFinalOffset = true + } + if offset == c.highestReceived { + return nil + } + // A higher offset was received before. + // This can happen due to reordering. + if offset <= c.highestReceived { + if final { + return &qerr.TransportError{ + ErrorCode: qerr.FinalSizeError, + ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived), + } + } + return nil + } + + increment := offset - c.highestReceived + c.highestReceived = offset + if c.checkFlowControlViolation() { + return &qerr.TransportError{ + ErrorCode: qerr.FlowControlError, + ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow), + } + } + return c.connection.IncrementHighestReceived(increment) +} + +func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) { + c.mutex.Lock() + c.baseFlowController.addBytesRead(n) + shouldQueueWindowUpdate := c.shouldQueueWindowUpdate() + c.mutex.Unlock() + if shouldQueueWindowUpdate { + c.queueWindowUpdate() + } + c.connection.AddBytesRead(n) +} + +func (c *streamFlowController) Abandon() { + c.mutex.Lock() + unread := c.highestReceived - c.bytesRead + c.mutex.Unlock() + if unread > 0 { + c.connection.AddBytesRead(unread) + } +} + +func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) { + c.baseFlowController.AddBytesSent(n) + c.connection.AddBytesSent(n) +} + +func (c *streamFlowController) SendWindowSize() protocol.ByteCount { + return min(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize()) +} + +func (c *streamFlowController) shouldQueueWindowUpdate() bool { + return !c.receivedFinalOffset && c.hasWindowUpdate() +} + +func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount { + // If we already received the final offset for this stream, the peer won't need any additional flow control credit. + if c.receivedFinalOffset { + return 0 + } + + // Don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler + c.mutex.Lock() + oldWindowSize := c.receiveWindowSize + offset := c.baseFlowController.getWindowUpdate() + if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size + c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10)) + c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier)) + } + c.mutex.Unlock() + return offset +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go new file mode 100644 index 000000000..1baf5d6b0 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go @@ -0,0 +1,90 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/quic-go/quic-go/internal/protocol" +) + +func createAEAD(suite *cipherSuite, trafficSecret []byte, v protocol.Version) *xorNonceAEAD { + keyLabel := hkdfLabelKeyV1 + ivLabel := hkdfLabelIVV1 + if v == protocol.Version2 { + keyLabel = hkdfLabelKeyV2 + ivLabel = hkdfLabelIVV2 + } + key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, keyLabel, suite.KeyLen) + iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, ivLabel, suite.IVLen()) + return suite.AEAD(key, iv) +} + +type longHeaderSealer struct { + aead *xorNonceAEAD + headerProtector headerProtector + nonceBuf [8]byte +} + +var _ LongHeaderSealer = &longHeaderSealer{} + +func newLongHeaderSealer(aead *xorNonceAEAD, headerProtector headerProtector) LongHeaderSealer { + if aead.NonceSize() != 8 { + panic("unexpected nonce size") + } + return &longHeaderSealer{ + aead: aead, + headerProtector: headerProtector, + } +} + +func (s *longHeaderSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { + binary.BigEndian.PutUint64(s.nonceBuf[:], uint64(pn)) + return s.aead.Seal(dst, s.nonceBuf[:], src, ad) +} + +func (s *longHeaderSealer) EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { + s.headerProtector.EncryptHeader(sample, firstByte, pnBytes) +} + +func (s *longHeaderSealer) Overhead() int { + return s.aead.Overhead() +} + +type longHeaderOpener struct { + aead *xorNonceAEAD + headerProtector headerProtector + highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected) + + // use a single array to avoid allocations + nonceBuf [8]byte +} + +var _ LongHeaderOpener = &longHeaderOpener{} + +func newLongHeaderOpener(aead *xorNonceAEAD, headerProtector headerProtector) LongHeaderOpener { + if aead.NonceSize() != 8 { + panic("unexpected nonce size") + } + return &longHeaderOpener{ + aead: aead, + headerProtector: headerProtector, + } +} + +func (o *longHeaderOpener) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber { + return protocol.DecodePacketNumber(wirePNLen, o.highestRcvdPN, wirePN) +} + +func (o *longHeaderOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) { + binary.BigEndian.PutUint64(o.nonceBuf[:], uint64(pn)) + dec, err := o.aead.Open(dst, o.nonceBuf[:], src, ad) + if err == nil { + o.highestRcvdPN = max(o.highestRcvdPN, pn) + } else { + err = ErrDecryptionFailed + } + return dec, err +} + +func (o *longHeaderOpener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { + o.headerProtector.DecryptHeader(sample, firstByte, pnBytes) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go b/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go new file mode 100644 index 000000000..d8a381daf --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go @@ -0,0 +1,104 @@ +package handshake + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/tls" + "fmt" + + "golang.org/x/crypto/chacha20poly1305" +) + +// These cipher suite implementations are copied from the standard library crypto/tls package. + +const aeadNonceLength = 12 + +type cipherSuite struct { + ID uint16 + Hash crypto.Hash + KeyLen int + AEAD func(key, nonceMask []byte) *xorNonceAEAD +} + +func (s cipherSuite) IVLen() int { return aeadNonceLength } + +func getCipherSuite(id uint16) *cipherSuite { + switch id { + case tls.TLS_AES_128_GCM_SHA256: + return &cipherSuite{ID: tls.TLS_AES_128_GCM_SHA256, Hash: crypto.SHA256, KeyLen: 16, AEAD: aeadAESGCMTLS13} + case tls.TLS_CHACHA20_POLY1305_SHA256: + return &cipherSuite{ID: tls.TLS_CHACHA20_POLY1305_SHA256, Hash: crypto.SHA256, KeyLen: 32, AEAD: aeadChaCha20Poly1305} + case tls.TLS_AES_256_GCM_SHA384: + return &cipherSuite{ID: tls.TLS_AES_256_GCM_SHA384, Hash: crypto.SHA384, KeyLen: 32, AEAD: aeadAESGCMTLS13} + default: + panic(fmt.Sprintf("unknown cypher suite: %d", id)) + } +} + +func aeadAESGCMTLS13(key, nonceMask []byte) *xorNonceAEAD { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +func aeadChaCha20Poly1305(key, nonceMask []byte) *xorNonceAEAD { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aead, err := chacha20poly1305.New(key) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce +// before each call. +type xorNonceAEAD struct { + nonceMask [aeadNonceLength]byte + aead cipher.AEAD +} + +func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number +func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } +func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } + +func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result +} + +func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result, err +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go b/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go new file mode 100644 index 000000000..54af823ba --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go @@ -0,0 +1,21 @@ +package handshake + +import ( + "net" + "time" +) + +type conn struct { + localAddr, remoteAddr net.Addr +} + +var _ net.Conn = &conn{} + +func (c *conn) Read([]byte) (int, error) { return 0, nil } +func (c *conn) Write([]byte) (int, error) { return 0, nil } +func (c *conn) Close() error { return nil } +func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } +func (c *conn) LocalAddr() net.Addr { return c.localAddr } +func (c *conn) SetReadDeadline(time.Time) error { return nil } +func (c *conn) SetWriteDeadline(time.Time) error { return nil } +func (c *conn) SetDeadline(time.Time) error { return nil } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go new file mode 100644 index 000000000..adf74fe74 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go @@ -0,0 +1,663 @@ +package handshake + +import ( + "bytes" + "context" + "crypto/tls" + "errors" + "fmt" + "net" + "strings" + "sync/atomic" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/qtls" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/quicvarint" +) + +type quicVersionContextKey struct{} + +var QUICVersionContextKey = &quicVersionContextKey{} + +const clientSessionStateRevision = 4 + +type cryptoSetup struct { + tlsConf *tls.Config + conn *tls.QUICConn + + events []Event + + version protocol.Version + + ourParams *wire.TransportParameters + peerParams *wire.TransportParameters + + zeroRTTParameters *wire.TransportParameters + allow0RTT bool + + rttStats *utils.RTTStats + + tracer *logging.ConnectionTracer + logger utils.Logger + + perspective protocol.Perspective + + handshakeCompleteTime time.Time + + zeroRTTOpener LongHeaderOpener // only set for the server + zeroRTTSealer LongHeaderSealer // only set for the client + + initialOpener LongHeaderOpener + initialSealer LongHeaderSealer + + handshakeOpener LongHeaderOpener + handshakeSealer LongHeaderSealer + + used0RTT atomic.Bool + + aead *updatableAEAD + has1RTTSealer bool + has1RTTOpener bool +} + +var _ CryptoSetup = &cryptoSetup{} + +// NewCryptoSetupClient creates a new crypto setup for the client +func NewCryptoSetupClient( + connID protocol.ConnectionID, + tp *wire.TransportParameters, + tlsConf *tls.Config, + enable0RTT bool, + rttStats *utils.RTTStats, + tracer *logging.ConnectionTracer, + logger utils.Logger, + version protocol.Version, +) CryptoSetup { + cs := newCryptoSetup( + connID, + tp, + rttStats, + tracer, + logger, + protocol.PerspectiveClient, + version, + ) + + tlsConf = tlsConf.Clone() + tlsConf.MinVersion = tls.VersionTLS13 + quicConf := &tls.QUICConfig{TLSConfig: tlsConf} + qtls.SetupConfigForClient(quicConf, cs.marshalDataForSessionState, cs.handleDataFromSessionState) + cs.tlsConf = tlsConf + cs.allow0RTT = enable0RTT + + cs.conn = tls.QUICClient(quicConf) + cs.conn.SetTransportParameters(cs.ourParams.Marshal(protocol.PerspectiveClient)) + + return cs +} + +// NewCryptoSetupServer creates a new crypto setup for the server +func NewCryptoSetupServer( + connID protocol.ConnectionID, + localAddr, remoteAddr net.Addr, + tp *wire.TransportParameters, + tlsConf *tls.Config, + allow0RTT bool, + rttStats *utils.RTTStats, + tracer *logging.ConnectionTracer, + logger utils.Logger, + version protocol.Version, +) CryptoSetup { + cs := newCryptoSetup( + connID, + tp, + rttStats, + tracer, + logger, + protocol.PerspectiveServer, + version, + ) + cs.allow0RTT = allow0RTT + + quicConf := &tls.QUICConfig{TLSConfig: tlsConf} + qtls.SetupConfigForServer(quicConf, cs.allow0RTT, cs.getDataForSessionTicket, cs.handleSessionTicket) + addConnToClientHelloInfo(quicConf.TLSConfig, localAddr, remoteAddr) + + cs.tlsConf = quicConf.TLSConfig + cs.conn = tls.QUICServer(quicConf) + + return cs +} + +// The tls.Config contains two callbacks that pass in a tls.ClientHelloInfo. +// Since crypto/tls doesn't do it, we need to make sure to set the Conn field with a fake net.Conn +// that allows the caller to get the local and the remote address. +func addConnToClientHelloInfo(conf *tls.Config, localAddr, remoteAddr net.Addr) { + if conf.GetConfigForClient != nil { + gcfc := conf.GetConfigForClient + conf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { + info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} + c, err := gcfc(info) + if c != nil { + c = c.Clone() + // This won't be necessary anymore once https://github.com/golang/go/issues/63722 is accepted. + c.MinVersion = tls.VersionTLS13 + // We're returning a tls.Config here, so we need to apply this recursively. + addConnToClientHelloInfo(c, localAddr, remoteAddr) + } + return c, err + } + } + if conf.GetCertificate != nil { + gc := conf.GetCertificate + conf.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} + return gc(info) + } + } +} + +func newCryptoSetup( + connID protocol.ConnectionID, + tp *wire.TransportParameters, + rttStats *utils.RTTStats, + tracer *logging.ConnectionTracer, + logger utils.Logger, + perspective protocol.Perspective, + version protocol.Version, +) *cryptoSetup { + initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) + if tracer != nil && tracer.UpdatedKeyFromTLS != nil { + tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) + tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) + } + return &cryptoSetup{ + initialSealer: initialSealer, + initialOpener: initialOpener, + aead: newUpdatableAEAD(rttStats, tracer, logger, version), + events: make([]Event, 0, 16), + ourParams: tp, + rttStats: rttStats, + tracer: tracer, + logger: logger, + perspective: perspective, + version: version, + } +} + +func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { + initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version) + h.initialSealer = initialSealer + h.initialOpener = initialOpener + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) + h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) + } +} + +func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error { + return h.aead.SetLargestAcked(pn) +} + +func (h *cryptoSetup) StartHandshake() error { + err := h.conn.Start(context.WithValue(context.Background(), QUICVersionContextKey, h.version)) + if err != nil { + return wrapError(err) + } + for { + ev := h.conn.NextEvent() + done, err := h.handleEvent(ev) + if err != nil { + return wrapError(err) + } + if done { + break + } + } + if h.perspective == protocol.PerspectiveClient { + if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { + h.logger.Debugf("Doing 0-RTT.") + h.events = append(h.events, Event{Kind: EventRestoredTransportParameters, TransportParameters: h.zeroRTTParameters}) + } else { + h.logger.Debugf("Not doing 0-RTT. Has sealer: %t, has params: %t", h.zeroRTTSealer != nil, h.zeroRTTParameters != nil) + } + } + return nil +} + +// Close closes the crypto setup. +// It aborts the handshake, if it is still running. +func (h *cryptoSetup) Close() error { + return h.conn.Close() +} + +// HandleMessage handles a TLS handshake message. +// It is called by the crypto streams when a new message is available. +func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) error { + if err := h.handleMessage(data, encLevel); err != nil { + return wrapError(err) + } + return nil +} + +func (h *cryptoSetup) handleMessage(data []byte, encLevel protocol.EncryptionLevel) error { + if err := h.conn.HandleData(qtls.ToTLSEncryptionLevel(encLevel), data); err != nil { + return err + } + for { + ev := h.conn.NextEvent() + done, err := h.handleEvent(ev) + if err != nil { + return err + } + if done { + return nil + } + } +} + +func (h *cryptoSetup) handleEvent(ev tls.QUICEvent) (done bool, err error) { + switch ev.Kind { + case tls.QUICNoEvent: + return true, nil + case tls.QUICSetReadSecret: + h.setReadKey(ev.Level, ev.Suite, ev.Data) + return false, nil + case tls.QUICSetWriteSecret: + h.setWriteKey(ev.Level, ev.Suite, ev.Data) + return false, nil + case tls.QUICTransportParameters: + return false, h.handleTransportParameters(ev.Data) + case tls.QUICTransportParametersRequired: + h.conn.SetTransportParameters(h.ourParams.Marshal(h.perspective)) + return false, nil + case tls.QUICRejectedEarlyData: + h.rejected0RTT() + return false, nil + case tls.QUICWriteData: + h.writeRecord(ev.Level, ev.Data) + return false, nil + case tls.QUICHandshakeDone: + h.handshakeComplete() + return false, nil + default: + return false, fmt.Errorf("unexpected event: %d", ev.Kind) + } +} + +func (h *cryptoSetup) NextEvent() Event { + if len(h.events) == 0 { + return Event{Kind: EventNoEvent} + } + ev := h.events[0] + h.events = h.events[1:] + return ev +} + +func (h *cryptoSetup) handleTransportParameters(data []byte) error { + var tp wire.TransportParameters + if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil { + return err + } + h.peerParams = &tp + h.events = append(h.events, Event{Kind: EventReceivedTransportParameters, TransportParameters: h.peerParams}) + return nil +} + +// must be called after receiving the transport parameters +func (h *cryptoSetup) marshalDataForSessionState(earlyData bool) []byte { + b := make([]byte, 0, 256) + b = quicvarint.Append(b, clientSessionStateRevision) + b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds())) + if earlyData { + // only save the transport parameters for 0-RTT enabled session tickets + return h.peerParams.MarshalForSessionTicket(b) + } + return b +} + +func (h *cryptoSetup) handleDataFromSessionState(data []byte, earlyData bool) (allowEarlyData bool) { + rtt, tp, err := decodeDataFromSessionState(data, earlyData) + if err != nil { + h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error()) + return + } + h.rttStats.SetInitialRTT(rtt) + // The session ticket might have been saved from a connection that allowed 0-RTT, + // and therefore contain transport parameters. + // Only use them if 0-RTT is actually used on the new connection. + if tp != nil && h.allow0RTT { + h.zeroRTTParameters = tp + return true + } + return false +} + +func decodeDataFromSessionState(data []byte, earlyData bool) (time.Duration, *wire.TransportParameters, error) { + r := bytes.NewReader(data) + ver, err := quicvarint.Read(r) + if err != nil { + return 0, nil, err + } + if ver != clientSessionStateRevision { + return 0, nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) + } + rttEncoded, err := quicvarint.Read(r) + if err != nil { + return 0, nil, err + } + rtt := time.Duration(rttEncoded) * time.Microsecond + if !earlyData { + return rtt, nil, nil + } + var tp wire.TransportParameters + if err := tp.UnmarshalFromSessionTicket(r); err != nil { + return 0, nil, err + } + return rtt, &tp, nil +} + +func (h *cryptoSetup) getDataForSessionTicket() []byte { + ticket := &sessionTicket{ + RTT: h.rttStats.SmoothedRTT(), + } + if h.allow0RTT { + ticket.Parameters = h.ourParams + } + return ticket.Marshal() +} + +// GetSessionTicket generates a new session ticket. +// Due to limitations in crypto/tls, it's only possible to generate a single session ticket per connection. +// It is only valid for the server. +func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { + if err := h.conn.SendSessionTicket(tls.QUICSessionTicketOptions{ + EarlyData: h.allow0RTT, + }); err != nil { + // Session tickets might be disabled by tls.Config.SessionTicketsDisabled. + // We can't check h.tlsConfig here, since the actual config might have been obtained from + // the GetConfigForClient callback. + // See https://github.com/golang/go/issues/62032. + // Once that issue is resolved, this error assertion can be removed. + if strings.Contains(err.Error(), "session ticket keys unavailable") { + return nil, nil + } + return nil, err + } + ev := h.conn.NextEvent() + if ev.Kind != tls.QUICWriteData || ev.Level != tls.QUICEncryptionLevelApplication { + panic("crypto/tls bug: where's my session ticket?") + } + ticket := ev.Data + if ev := h.conn.NextEvent(); ev.Kind != tls.QUICNoEvent { + panic("crypto/tls bug: why more than one ticket?") + } + return ticket, nil +} + +// handleSessionTicket is called for the server when receiving the client's session ticket. +// It reads parameters from the session ticket and checks whether to accept 0-RTT if the session ticket enabled 0-RTT. +// Note that the fact that the session ticket allows 0-RTT doesn't mean that the actual TLS handshake enables 0-RTT: +// A client may use a 0-RTT enabled session to resume a TLS session without using 0-RTT. +func (h *cryptoSetup) handleSessionTicket(sessionTicketData []byte, using0RTT bool) bool { + var t sessionTicket + if err := t.Unmarshal(sessionTicketData, using0RTT); err != nil { + h.logger.Debugf("Unmarshalling session ticket failed: %s", err.Error()) + return false + } + h.rttStats.SetInitialRTT(t.RTT) + if !using0RTT { + return false + } + valid := h.ourParams.ValidFor0RTT(t.Parameters) + if !valid { + h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.") + return false + } + if !h.allow0RTT { + h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.") + return false + } + h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) + return true +} + +// rejected0RTT is called for the client when the server rejects 0-RTT. +func (h *cryptoSetup) rejected0RTT() { + h.logger.Debugf("0-RTT was rejected. Dropping 0-RTT keys.") + + had0RTTKeys := h.zeroRTTSealer != nil + h.zeroRTTSealer = nil + + if had0RTTKeys { + h.events = append(h.events, Event{Kind: EventDiscard0RTTKeys}) + } +} + +func (h *cryptoSetup) setReadKey(el tls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { + suite := getCipherSuite(suiteID) + //nolint:exhaustive // The TLS stack doesn't export Initial keys. + switch el { + case tls.QUICEncryptionLevelEarly: + if h.perspective == protocol.PerspectiveClient { + panic("Received 0-RTT read key for the client") + } + h.zeroRTTOpener = newLongHeaderOpener( + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), + ) + h.used0RTT.Store(true) + if h.logger.Debug() { + h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + case tls.QUICEncryptionLevelHandshake: + h.handshakeOpener = newLongHeaderOpener( + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), + ) + if h.logger.Debug() { + h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + case tls.QUICEncryptionLevelApplication: + h.aead.SetReadKey(suite, trafficSecret) + h.has1RTTOpener = true + if h.logger.Debug() { + h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + default: + panic("unexpected read encryption level") + } + h.events = append(h.events, Event{Kind: EventReceivedReadKeys}) + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective.Opposite()) + } +} + +func (h *cryptoSetup) setWriteKey(el tls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { + suite := getCipherSuite(suiteID) + //nolint:exhaustive // The TLS stack doesn't export Initial keys. + switch el { + case tls.QUICEncryptionLevelEarly: + if h.perspective == protocol.PerspectiveServer { + panic("Received 0-RTT write key for the server") + } + h.zeroRTTSealer = newLongHeaderSealer( + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), + ) + if h.logger.Debug() { + h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) + } + // don't set used0RTT here. 0-RTT might still get rejected. + return + case tls.QUICEncryptionLevelHandshake: + h.handshakeSealer = newLongHeaderSealer( + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), + ) + if h.logger.Debug() { + h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + case tls.QUICEncryptionLevelApplication: + h.aead.SetWriteKey(suite, trafficSecret) + h.has1RTTSealer = true + if h.logger.Debug() { + h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) + } + if h.zeroRTTSealer != nil { + // Once we receive handshake keys, we know that 0-RTT was not rejected. + h.used0RTT.Store(true) + h.zeroRTTSealer = nil + h.logger.Debugf("Dropping 0-RTT keys.") + if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { + h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) + } + } + default: + panic("unexpected write encryption level") + } + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective) + } +} + +// writeRecord is called when TLS writes data +func (h *cryptoSetup) writeRecord(encLevel tls.QUICEncryptionLevel, p []byte) { + //nolint:exhaustive // handshake records can only be written for Initial and Handshake. + switch encLevel { + case tls.QUICEncryptionLevelInitial: + h.events = append(h.events, Event{Kind: EventWriteInitialData, Data: p}) + case tls.QUICEncryptionLevelHandshake: + h.events = append(h.events, Event{Kind: EventWriteHandshakeData, Data: p}) + case tls.QUICEncryptionLevelApplication: + panic("unexpected write") + default: + panic(fmt.Sprintf("unexpected write encryption level: %s", encLevel)) + } +} + +func (h *cryptoSetup) DiscardInitialKeys() { + dropped := h.initialOpener != nil + h.initialOpener = nil + h.initialSealer = nil + if dropped { + h.logger.Debugf("Dropping Initial keys.") + } +} + +func (h *cryptoSetup) handshakeComplete() { + h.handshakeCompleteTime = time.Now() + h.events = append(h.events, Event{Kind: EventHandshakeComplete}) +} + +func (h *cryptoSetup) SetHandshakeConfirmed() { + h.aead.SetHandshakeConfirmed() + // drop Handshake keys + var dropped bool + if h.handshakeOpener != nil { + h.handshakeOpener = nil + h.handshakeSealer = nil + dropped = true + } + if dropped { + h.logger.Debugf("Dropping Handshake keys.") + } +} + +func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) { + if h.initialSealer == nil { + return nil, ErrKeysDropped + } + return h.initialSealer, nil +} + +func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) { + if h.zeroRTTSealer == nil { + return nil, ErrKeysDropped + } + return h.zeroRTTSealer, nil +} + +func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) { + if h.handshakeSealer == nil { + if h.initialSealer == nil { + return nil, ErrKeysDropped + } + return nil, ErrKeysNotYetAvailable + } + return h.handshakeSealer, nil +} + +func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) { + if !h.has1RTTSealer { + return nil, ErrKeysNotYetAvailable + } + return h.aead, nil +} + +func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) { + if h.initialOpener == nil { + return nil, ErrKeysDropped + } + return h.initialOpener, nil +} + +func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) { + if h.zeroRTTOpener == nil { + if h.initialOpener != nil { + return nil, ErrKeysNotYetAvailable + } + // if the initial opener is also not available, the keys were already dropped + return nil, ErrKeysDropped + } + return h.zeroRTTOpener, nil +} + +func (h *cryptoSetup) GetHandshakeOpener() (LongHeaderOpener, error) { + if h.handshakeOpener == nil { + if h.initialOpener != nil { + return nil, ErrKeysNotYetAvailable + } + // if the initial opener is also not available, the keys were already dropped + return nil, ErrKeysDropped + } + return h.handshakeOpener, nil +} + +func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { + if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) { + h.zeroRTTOpener = nil + h.logger.Debugf("Dropping 0-RTT keys.") + if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { + h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) + } + } + + if !h.has1RTTOpener { + return nil, ErrKeysNotYetAvailable + } + return h.aead, nil +} + +func (h *cryptoSetup) ConnectionState() ConnectionState { + return ConnectionState{ + ConnectionState: h.conn.ConnectionState(), + Used0RTT: h.used0RTT.Load(), + } +} + +func wrapError(err error) error { + // alert 80 is an internal error + if alertErr := tls.AlertError(0); errors.As(err, &alertErr) && alertErr != 80 { + return qerr.NewLocalCryptoError(uint8(alertErr), err) + } + return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()} +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go new file mode 100644 index 000000000..2c5ee42f1 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go @@ -0,0 +1,134 @@ +package handshake + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/tls" + "encoding/binary" + "fmt" + + "golang.org/x/crypto/chacha20" + + "github.com/quic-go/quic-go/internal/protocol" +) + +type headerProtector interface { + EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) + DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) +} + +func hkdfHeaderProtectionLabel(v protocol.Version) string { + if v == protocol.Version2 { + return "quicv2 hp" + } + return "quic hp" +} + +func newHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, v protocol.Version) headerProtector { + hkdfLabel := hkdfHeaderProtectionLabel(v) + switch suite.ID { + case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: + return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) + case tls.TLS_CHACHA20_POLY1305_SHA256: + return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) + default: + panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID)) + } +} + +type aesHeaderProtector struct { + mask [16]byte // AES always has a 16 byte block size + block cipher.Block + isLongHeader bool +} + +var _ headerProtector = &aesHeaderProtector{} + +func newAESHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { + hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) + block, err := aes.NewCipher(hpKey) + if err != nil { + panic(fmt.Sprintf("error creating new AES cipher: %s", err)) + } + return &aesHeaderProtector{ + block: block, + isLongHeader: isLongHeader, + } +} + +func (p *aesHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + p.apply(sample, firstByte, hdrBytes) +} + +func (p *aesHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + p.apply(sample, firstByte, hdrBytes) +} + +func (p *aesHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { + if len(sample) != len(p.mask) { + panic("invalid sample size") + } + p.block.Encrypt(p.mask[:], sample) + if p.isLongHeader { + *firstByte ^= p.mask[0] & 0xf + } else { + *firstByte ^= p.mask[0] & 0x1f + } + for i := range hdrBytes { + hdrBytes[i] ^= p.mask[i+1] + } +} + +type chachaHeaderProtector struct { + mask [5]byte + + key [32]byte + isLongHeader bool +} + +var _ headerProtector = &chachaHeaderProtector{} + +func newChaChaHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { + hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) + + p := &chachaHeaderProtector{ + isLongHeader: isLongHeader, + } + copy(p.key[:], hpKey) + return p +} + +func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + p.apply(sample, firstByte, hdrBytes) +} + +func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + p.apply(sample, firstByte, hdrBytes) +} + +func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { + if len(sample) != 16 { + panic("invalid sample size") + } + for i := 0; i < 5; i++ { + p.mask[i] = 0 + } + cipher, err := chacha20.NewUnauthenticatedCipher(p.key[:], sample[4:]) + if err != nil { + panic(err) + } + cipher.SetCounter(binary.LittleEndian.Uint32(sample[:4])) + cipher.XORKeyStream(p.mask[:], p.mask[:]) + p.applyMask(firstByte, hdrBytes) +} + +func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) { + if p.isLongHeader { + *firstByte ^= p.mask[0] & 0xf + } else { + *firstByte ^= p.mask[0] & 0x1f + } + for i := range hdrBytes { + hdrBytes[i] ^= p.mask[i+1] + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go b/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go new file mode 100644 index 000000000..0caf1c8e5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go @@ -0,0 +1,29 @@ +package handshake + +import ( + "crypto" + "encoding/binary" + + "golang.org/x/crypto/hkdf" +) + +// hkdfExpandLabel HKDF expands a label as defined in RFC 8446, section 7.1. +// Since this implementation avoids using a cryptobyte.Builder, it is about 15% faster than the +// hkdfExpandLabel in the standard library. +func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte { + b := make([]byte, 3, 3+6+len(label)+1+len(context)) + binary.BigEndian.PutUint16(b, uint16(length)) + b[2] = uint8(6 + len(label)) + b = append(b, []byte("tls13 ")...) + b = append(b, []byte(label)...) + b = b[:3+6+len(label)+1] + b[3+6+len(label)] = uint8(len(context)) + b = append(b, context...) + + out := make([]byte, length) + n, err := hkdf.Expand(hash.New, secret, b).Read(out) + if err != nil || n != length { + panic("quic: HKDF-Expand-Label invocation failed unexpectedly") + } + return out +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go new file mode 100644 index 000000000..b8aa7e3e7 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go @@ -0,0 +1,71 @@ +package handshake + +import ( + "crypto" + "crypto/tls" + + "golang.org/x/crypto/hkdf" + + "github.com/quic-go/quic-go/internal/protocol" +) + +var ( + quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} + quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9} +) + +const ( + hkdfLabelKeyV1 = "quic key" + hkdfLabelKeyV2 = "quicv2 key" + hkdfLabelIVV1 = "quic iv" + hkdfLabelIVV2 = "quicv2 iv" +) + +func getSalt(v protocol.Version) []byte { + if v == protocol.Version2 { + return quicSaltV2 + } + return quicSaltV1 +} + +var initialSuite = getCipherSuite(tls.TLS_AES_128_GCM_SHA256) + +// NewInitialAEAD creates a new AEAD for Initial encryption / decryption. +func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v protocol.Version) (LongHeaderSealer, LongHeaderOpener) { + clientSecret, serverSecret := computeSecrets(connID, v) + var mySecret, otherSecret []byte + if pers == protocol.PerspectiveClient { + mySecret = clientSecret + otherSecret = serverSecret + } else { + mySecret = serverSecret + otherSecret = clientSecret + } + myKey, myIV := computeInitialKeyAndIV(mySecret, v) + otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v) + + encrypter := initialSuite.AEAD(myKey, myIV) + decrypter := initialSuite.AEAD(otherKey, otherIV) + + return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)), + newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v))) +} + +func computeSecrets(connID protocol.ConnectionID, v protocol.Version) (clientSecret, serverSecret []byte) { + initialSecret := hkdf.Extract(crypto.SHA256.New, connID.Bytes(), getSalt(v)) + clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) + serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size()) + return +} + +func computeInitialKeyAndIV(secret []byte, v protocol.Version) (key, iv []byte) { + keyLabel := hkdfLabelKeyV1 + ivLabel := hkdfLabelIVV1 + if v == protocol.Version2 { + keyLabel = hkdfLabelKeyV2 + ivLabel = hkdfLabelIVV2 + } + key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, keyLabel, 16) + iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, ivLabel, 12) + return +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go new file mode 100644 index 000000000..fab224f9b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go @@ -0,0 +1,116 @@ +package handshake + +import ( + "crypto/tls" + "errors" + "io" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +var ( + // ErrKeysNotYetAvailable is returned when an opener or a sealer is requested for an encryption level, + // but the corresponding opener has not yet been initialized + // This can happen when packets arrive out of order. + ErrKeysNotYetAvailable = errors.New("CryptoSetup: keys at this encryption level not yet available") + // ErrKeysDropped is returned when an opener or a sealer is requested for an encryption level, + // but the corresponding keys have already been dropped. + ErrKeysDropped = errors.New("CryptoSetup: keys were already dropped") + // ErrDecryptionFailed is returned when the AEAD fails to open the packet. + ErrDecryptionFailed = errors.New("decryption failed") +) + +type headerDecryptor interface { + DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) +} + +// LongHeaderOpener opens a long header packet +type LongHeaderOpener interface { + headerDecryptor + DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber + Open(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) ([]byte, error) +} + +// ShortHeaderOpener opens a short header packet +type ShortHeaderOpener interface { + headerDecryptor + DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber + Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, associatedData []byte) ([]byte, error) +} + +// LongHeaderSealer seals a long header packet +type LongHeaderSealer interface { + Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte + EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) + Overhead() int +} + +// ShortHeaderSealer seals a short header packet +type ShortHeaderSealer interface { + LongHeaderSealer + KeyPhase() protocol.KeyPhaseBit +} + +type ConnectionState struct { + tls.ConnectionState + Used0RTT bool +} + +// EventKind is the kind of handshake event. +type EventKind uint8 + +const ( + // EventNoEvent signals that there are no new handshake events + EventNoEvent EventKind = iota + 1 + // EventWriteInitialData contains new CRYPTO data to send at the Initial encryption level + EventWriteInitialData + // EventWriteHandshakeData contains new CRYPTO data to send at the Handshake encryption level + EventWriteHandshakeData + // EventReceivedReadKeys signals that new decryption keys are available. + // It doesn't say which encryption level those keys are for. + EventReceivedReadKeys + // EventDiscard0RTTKeys signals that the Handshake keys were discarded. + EventDiscard0RTTKeys + // EventReceivedTransportParameters contains the transport parameters sent by the peer. + EventReceivedTransportParameters + // EventRestoredTransportParameters contains the transport parameters restored from the session ticket. + // It is only used for the client. + EventRestoredTransportParameters + // EventHandshakeComplete signals that the TLS handshake was completed. + EventHandshakeComplete +) + +// Event is a handshake event. +type Event struct { + Kind EventKind + Data []byte + TransportParameters *wire.TransportParameters +} + +// CryptoSetup handles the handshake and protecting / unprotecting packets +type CryptoSetup interface { + StartHandshake() error + io.Closer + ChangeConnectionID(protocol.ConnectionID) + GetSessionTicket() ([]byte, error) + + HandleMessage([]byte, protocol.EncryptionLevel) error + NextEvent() Event + + SetLargest1RTTAcked(protocol.PacketNumber) error + DiscardInitialKeys() + SetHandshakeConfirmed() + ConnectionState() ConnectionState + + GetInitialOpener() (LongHeaderOpener, error) + GetHandshakeOpener() (LongHeaderOpener, error) + Get0RTTOpener() (LongHeaderOpener, error) + Get1RTTOpener() (ShortHeaderOpener, error) + + GetInitialSealer() (LongHeaderSealer, error) + GetHandshakeSealer() (LongHeaderSealer, error) + Get0RTTSealer() (LongHeaderSealer, error) + Get1RTTSealer() (ShortHeaderSealer, error) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go new file mode 100644 index 000000000..30643cdfc --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go @@ -0,0 +1,63 @@ +package handshake + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "fmt" + "sync" + + "github.com/quic-go/quic-go/internal/protocol" +) + +var ( + retryAEADv1 cipher.AEAD // used for QUIC v1 (RFC 9000) + retryAEADv2 cipher.AEAD // used for QUIC v2 (RFC 9369) +) + +func init() { + retryAEADv1 = initAEAD([16]byte{0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e}) + retryAEADv2 = initAEAD([16]byte{0x8f, 0xb4, 0xb0, 0x1b, 0x56, 0xac, 0x48, 0xe2, 0x60, 0xfb, 0xcb, 0xce, 0xad, 0x7c, 0xcc, 0x92}) +} + +func initAEAD(key [16]byte) cipher.AEAD { + aes, err := aes.NewCipher(key[:]) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + return aead +} + +var ( + retryBuf bytes.Buffer + retryMutex sync.Mutex + retryNonceV1 = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb} + retryNonceV2 = [12]byte{0xd8, 0x69, 0x69, 0xbc, 0x2d, 0x7c, 0x6d, 0x99, 0x90, 0xef, 0xb0, 0x4a} +) + +// GetRetryIntegrityTag calculates the integrity tag on a Retry packet +func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, version protocol.Version) *[16]byte { + retryMutex.Lock() + defer retryMutex.Unlock() + + retryBuf.WriteByte(uint8(origDestConnID.Len())) + retryBuf.Write(origDestConnID.Bytes()) + retryBuf.Write(retry) + defer retryBuf.Reset() + + var tag [16]byte + var sealed []byte + if version == protocol.Version2 { + sealed = retryAEADv2.Seal(tag[:0], retryNonceV2[:], nil, retryBuf.Bytes()) + } else { + sealed = retryAEADv1.Seal(tag[:0], retryNonceV1[:], nil, retryBuf.Bytes()) + } + if len(sealed) != 16 { + panic(fmt.Sprintf("unexpected Retry integrity tag length: %d", len(sealed))) + } + return &tag +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go new file mode 100644 index 000000000..9481af563 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go @@ -0,0 +1,54 @@ +package handshake + +import ( + "bytes" + "errors" + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/quicvarint" +) + +const sessionTicketRevision = 4 + +type sessionTicket struct { + Parameters *wire.TransportParameters + RTT time.Duration // to be encoded in mus +} + +func (t *sessionTicket) Marshal() []byte { + b := make([]byte, 0, 256) + b = quicvarint.Append(b, sessionTicketRevision) + b = quicvarint.Append(b, uint64(t.RTT.Microseconds())) + if t.Parameters == nil { + return b + } + return t.Parameters.MarshalForSessionTicket(b) +} + +func (t *sessionTicket) Unmarshal(b []byte, using0RTT bool) error { + r := bytes.NewReader(b) + rev, err := quicvarint.Read(r) + if err != nil { + return errors.New("failed to read session ticket revision") + } + if rev != sessionTicketRevision { + return fmt.Errorf("unknown session ticket revision: %d", rev) + } + rtt, err := quicvarint.Read(r) + if err != nil { + return errors.New("failed to read RTT") + } + if using0RTT { + var tp wire.TransportParameters + if err := tp.UnmarshalFromSessionTicket(r); err != nil { + return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) + } + t.Parameters = &tp + } else if r.Len() > 0 { + return fmt.Errorf("the session ticket has more bytes than expected") + } + t.RTT = time.Duration(rtt) * time.Microsecond + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go new file mode 100644 index 000000000..2d91e6b25 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go @@ -0,0 +1,120 @@ +package handshake + +import ( + "bytes" + "encoding/asn1" + "fmt" + "net" + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +const ( + tokenPrefixIP byte = iota + tokenPrefixString +) + +// A Token is derived from the client address and can be used to verify the ownership of this address. +type Token struct { + IsRetryToken bool + SentTime time.Time + encodedRemoteAddr []byte + // only set for retry tokens + OriginalDestConnectionID protocol.ConnectionID + RetrySrcConnectionID protocol.ConnectionID +} + +// ValidateRemoteAddr validates the address, but does not check expiration +func (t *Token) ValidateRemoteAddr(addr net.Addr) bool { + return bytes.Equal(encodeRemoteAddr(addr), t.encodedRemoteAddr) +} + +// token is the struct that is used for ASN1 serialization and deserialization +type token struct { + IsRetryToken bool + RemoteAddr []byte + Timestamp int64 + OriginalDestConnectionID []byte + RetrySrcConnectionID []byte +} + +// A TokenGenerator generates tokens +type TokenGenerator struct { + tokenProtector tokenProtector +} + +// NewTokenGenerator initializes a new TokenGenerator +func NewTokenGenerator(key TokenProtectorKey) *TokenGenerator { + return &TokenGenerator{tokenProtector: newTokenProtector(key)} +} + +// NewRetryToken generates a new token for a Retry for a given source address +func (g *TokenGenerator) NewRetryToken( + raddr net.Addr, + origDestConnID protocol.ConnectionID, + retrySrcConnID protocol.ConnectionID, +) ([]byte, error) { + data, err := asn1.Marshal(token{ + IsRetryToken: true, + RemoteAddr: encodeRemoteAddr(raddr), + OriginalDestConnectionID: origDestConnID.Bytes(), + RetrySrcConnectionID: retrySrcConnID.Bytes(), + Timestamp: time.Now().UnixNano(), + }) + if err != nil { + return nil, err + } + return g.tokenProtector.NewToken(data) +} + +// NewToken generates a new token to be sent in a NEW_TOKEN frame +func (g *TokenGenerator) NewToken(raddr net.Addr) ([]byte, error) { + data, err := asn1.Marshal(token{ + RemoteAddr: encodeRemoteAddr(raddr), + Timestamp: time.Now().UnixNano(), + }) + if err != nil { + return nil, err + } + return g.tokenProtector.NewToken(data) +} + +// DecodeToken decodes a token +func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) { + // if the client didn't send any token, DecodeToken will be called with a nil-slice + if len(encrypted) == 0 { + return nil, nil + } + + data, err := g.tokenProtector.DecodeToken(encrypted) + if err != nil { + return nil, err + } + t := &token{} + rest, err := asn1.Unmarshal(data, t) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, fmt.Errorf("rest when unpacking token: %d", len(rest)) + } + token := &Token{ + IsRetryToken: t.IsRetryToken, + SentTime: time.Unix(0, t.Timestamp), + encodedRemoteAddr: t.RemoteAddr, + } + if t.IsRetryToken { + token.OriginalDestConnectionID = protocol.ParseConnectionID(t.OriginalDestConnectionID) + token.RetrySrcConnectionID = protocol.ParseConnectionID(t.RetrySrcConnectionID) + } + return token, nil +} + +// encodeRemoteAddr encodes a remote address such that it can be saved in the token +func encodeRemoteAddr(remoteAddr net.Addr) []byte { + if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok { + return append([]byte{tokenPrefixIP}, udpAddr.IP...) + } + return append([]byte{tokenPrefixString}, []byte(remoteAddr.String())...) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go new file mode 100644 index 000000000..f3a99e411 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go @@ -0,0 +1,82 @@ +package handshake + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "fmt" + "io" + + "golang.org/x/crypto/hkdf" +) + +// TokenProtectorKey is the key used to encrypt both Retry and session resumption tokens. +type TokenProtectorKey [32]byte + +// TokenProtector is used to create and verify a token +type tokenProtector interface { + // NewToken creates a new token + NewToken([]byte) ([]byte, error) + // DecodeToken decodes a token + DecodeToken([]byte) ([]byte, error) +} + +const tokenNonceSize = 32 + +// tokenProtector is used to create and verify a token +type tokenProtectorImpl struct { + key TokenProtectorKey +} + +// newTokenProtector creates a source for source address tokens +func newTokenProtector(key TokenProtectorKey) tokenProtector { + return &tokenProtectorImpl{key: key} +} + +// NewToken encodes data into a new token. +func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) { + var nonce [tokenNonceSize]byte + if _, err := rand.Read(nonce[:]); err != nil { + return nil, err + } + aead, aeadNonce, err := s.createAEAD(nonce[:]) + if err != nil { + return nil, err + } + return append(nonce[:], aead.Seal(nil, aeadNonce, data, nil)...), nil +} + +// DecodeToken decodes a token. +func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) { + if len(p) < tokenNonceSize { + return nil, fmt.Errorf("token too short: %d", len(p)) + } + nonce := p[:tokenNonceSize] + aead, aeadNonce, err := s.createAEAD(nonce) + if err != nil { + return nil, err + } + return aead.Open(nil, aeadNonce, p[tokenNonceSize:], nil) +} + +func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) { + h := hkdf.New(sha256.New, s.key[:], nonce, []byte("quic-go token source")) + key := make([]byte, 32) // use a 32 byte key, in order to select AES-256 + if _, err := io.ReadFull(h, key); err != nil { + return nil, nil, err + } + aeadNonce := make([]byte, 12) + if _, err := io.ReadFull(h, aeadNonce); err != nil { + return nil, nil, err + } + c, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + aead, err := cipher.NewGCM(c) + if err != nil { + return nil, nil, err + } + return aead, aeadNonce, nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go new file mode 100644 index 000000000..ceaa8047a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go @@ -0,0 +1,332 @@ +package handshake + +import ( + "crypto" + "crypto/cipher" + "crypto/tls" + "encoding/binary" + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +// KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update. +// It's a package-level variable to allow modifying it for testing purposes. +var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval + +// FirstKeyUpdateInterval is the maximum number of packets we send or receive before initiating the first key update. +// It's a package-level variable to allow modifying it for testing purposes. +var FirstKeyUpdateInterval uint64 = 100 + +type updatableAEAD struct { + suite *cipherSuite + + keyPhase protocol.KeyPhase + largestAcked protocol.PacketNumber + firstPacketNumber protocol.PacketNumber + handshakeConfirmed bool + + invalidPacketLimit uint64 + invalidPacketCount uint64 + + // Time when the keys should be dropped. Keys are dropped on the next call to Open(). + prevRcvAEADExpiry time.Time + prevRcvAEAD cipher.AEAD + + firstRcvdWithCurrentKey protocol.PacketNumber + firstSentWithCurrentKey protocol.PacketNumber + highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected) + numRcvdWithCurrentKey uint64 + numSentWithCurrentKey uint64 + rcvAEAD cipher.AEAD + sendAEAD cipher.AEAD + // caches cipher.AEAD.Overhead(). This speeds up calls to Overhead(). + aeadOverhead int + + nextRcvAEAD cipher.AEAD + nextSendAEAD cipher.AEAD + nextRcvTrafficSecret []byte + nextSendTrafficSecret []byte + + headerDecrypter headerProtector + headerEncrypter headerProtector + + rttStats *utils.RTTStats + + tracer *logging.ConnectionTracer + logger utils.Logger + version protocol.Version + + // use a single slice to avoid allocations + nonceBuf []byte +} + +var ( + _ ShortHeaderOpener = &updatableAEAD{} + _ ShortHeaderSealer = &updatableAEAD{} +) + +func newUpdatableAEAD(rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.Version) *updatableAEAD { + return &updatableAEAD{ + firstPacketNumber: protocol.InvalidPacketNumber, + largestAcked: protocol.InvalidPacketNumber, + firstRcvdWithCurrentKey: protocol.InvalidPacketNumber, + firstSentWithCurrentKey: protocol.InvalidPacketNumber, + rttStats: rttStats, + tracer: tracer, + logger: logger, + version: version, + } +} + +func (a *updatableAEAD) rollKeys() { + if a.prevRcvAEAD != nil { + a.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", a.keyPhase-1, a.prevRcvAEADExpiry) + if a.tracer != nil && a.tracer.DroppedKey != nil { + a.tracer.DroppedKey(a.keyPhase - 1) + } + a.prevRcvAEADExpiry = time.Time{} + } + + a.keyPhase++ + a.firstRcvdWithCurrentKey = protocol.InvalidPacketNumber + a.firstSentWithCurrentKey = protocol.InvalidPacketNumber + a.numRcvdWithCurrentKey = 0 + a.numSentWithCurrentKey = 0 + a.prevRcvAEAD = a.rcvAEAD + a.rcvAEAD = a.nextRcvAEAD + a.sendAEAD = a.nextSendAEAD + + a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret) + a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret) + a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret, a.version) + a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version) +} + +func (a *updatableAEAD) startKeyDropTimer(now time.Time) { + d := 3 * a.rttStats.PTO(true) + a.logger.Debugf("Starting key drop timer to drop key phase %d (in %s)", a.keyPhase-1, d) + a.prevRcvAEADExpiry = now.Add(d) +} + +func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte { + return hkdfExpandLabel(hash, ts, []byte{}, "quic ku", hash.Size()) +} + +// SetReadKey sets the read key. +// For the client, this function is called before SetWriteKey. +// For the server, this function is called after SetWriteKey. +func (a *updatableAEAD) SetReadKey(suite *cipherSuite, trafficSecret []byte) { + a.rcvAEAD = createAEAD(suite, trafficSecret, a.version) + a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version) + if a.suite == nil { + a.setAEADParameters(a.rcvAEAD, suite) + } + + a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) + a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version) +} + +// SetWriteKey sets the write key. +// For the client, this function is called after SetReadKey. +// For the server, this function is called before SetReadKey. +func (a *updatableAEAD) SetWriteKey(suite *cipherSuite, trafficSecret []byte) { + a.sendAEAD = createAEAD(suite, trafficSecret, a.version) + a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version) + if a.suite == nil { + a.setAEADParameters(a.sendAEAD, suite) + } + + a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) + a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version) +} + +func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *cipherSuite) { + a.nonceBuf = make([]byte, aead.NonceSize()) + a.aeadOverhead = aead.Overhead() + a.suite = suite + switch suite.ID { + case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: + a.invalidPacketLimit = protocol.InvalidPacketLimitAES + case tls.TLS_CHACHA20_POLY1305_SHA256: + a.invalidPacketLimit = protocol.InvalidPacketLimitChaCha + default: + panic(fmt.Sprintf("unknown cipher suite %d", suite.ID)) + } +} + +func (a *updatableAEAD) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber { + return protocol.DecodePacketNumber(wirePNLen, a.highestRcvdPN, wirePN) +} + +func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { + dec, err := a.open(dst, src, rcvTime, pn, kp, ad) + if err == ErrDecryptionFailed { + a.invalidPacketCount++ + if a.invalidPacketCount >= a.invalidPacketLimit { + return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached} + } + } + if err == nil { + a.highestRcvdPN = max(a.highestRcvdPN, pn) + } + return dec, err +} + +func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { + if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) { + a.prevRcvAEAD = nil + a.logger.Debugf("Dropping key phase %d", a.keyPhase-1) + a.prevRcvAEADExpiry = time.Time{} + if a.tracer != nil && a.tracer.DroppedKey != nil { + a.tracer.DroppedKey(a.keyPhase - 1) + } + } + binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn)) + if kp != a.keyPhase.Bit() { + if a.keyPhase > 0 && a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber || pn < a.firstRcvdWithCurrentKey { + if a.prevRcvAEAD == nil { + return nil, ErrKeysDropped + } + // we updated the key, but the peer hasn't updated yet + dec, err := a.prevRcvAEAD.Open(dst, a.nonceBuf, src, ad) + if err != nil { + err = ErrDecryptionFailed + } + return dec, err + } + // try opening the packet with the next key phase + dec, err := a.nextRcvAEAD.Open(dst, a.nonceBuf, src, ad) + if err != nil { + return nil, ErrDecryptionFailed + } + // Opening succeeded. Check if the peer was allowed to update. + if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber { + return nil, &qerr.TransportError{ + ErrorCode: qerr.KeyUpdateError, + ErrorMessage: "keys updated too quickly", + } + } + a.rollKeys() + a.logger.Debugf("Peer updated keys to %d", a.keyPhase) + // The peer initiated this key update. It's safe to drop the keys for the previous generation now. + // Start a timer to drop the previous key generation. + a.startKeyDropTimer(rcvTime) + if a.tracer != nil && a.tracer.UpdatedKey != nil { + a.tracer.UpdatedKey(a.keyPhase, true) + } + a.firstRcvdWithCurrentKey = pn + return dec, err + } + // The AEAD we're using here will be the qtls.aeadAESGCM13. + // It uses the nonce provided here and XOR it with the IV. + dec, err := a.rcvAEAD.Open(dst, a.nonceBuf, src, ad) + if err != nil { + return dec, ErrDecryptionFailed + } + a.numRcvdWithCurrentKey++ + if a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber { + // We initiated the key updated, and now we received the first packet protected with the new key phase. + // Therefore, we are certain that the peer rolled its keys as well. Start a timer to drop the old keys. + if a.keyPhase > 0 { + a.logger.Debugf("Peer confirmed key update to phase %d", a.keyPhase) + a.startKeyDropTimer(rcvTime) + } + a.firstRcvdWithCurrentKey = pn + } + return dec, err +} + +func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { + if a.firstSentWithCurrentKey == protocol.InvalidPacketNumber { + a.firstSentWithCurrentKey = pn + } + if a.firstPacketNumber == protocol.InvalidPacketNumber { + a.firstPacketNumber = pn + } + a.numSentWithCurrentKey++ + binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn)) + // The AEAD we're using here will be the qtls.aeadAESGCM13. + // It uses the nonce provided here and XOR it with the IV. + return a.sendAEAD.Seal(dst, a.nonceBuf, src, ad) +} + +func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error { + if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber && + pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 { + return &qerr.TransportError{ + ErrorCode: qerr.KeyUpdateError, + ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase), + } + } + a.largestAcked = pn + return nil +} + +func (a *updatableAEAD) SetHandshakeConfirmed() { + a.handshakeConfirmed = true +} + +func (a *updatableAEAD) updateAllowed() bool { + if !a.handshakeConfirmed { + return false + } + // the first key update is allowed as soon as the handshake is confirmed + return a.keyPhase == 0 || + // subsequent key updates as soon as a packet sent with that key phase has been acknowledged + (a.firstSentWithCurrentKey != protocol.InvalidPacketNumber && + a.largestAcked != protocol.InvalidPacketNumber && + a.largestAcked >= a.firstSentWithCurrentKey) +} + +func (a *updatableAEAD) shouldInitiateKeyUpdate() bool { + if !a.updateAllowed() { + return false + } + // Initiate the first key update shortly after the handshake, in order to exercise the key update mechanism. + if a.keyPhase == 0 { + if a.numRcvdWithCurrentKey >= FirstKeyUpdateInterval || a.numSentWithCurrentKey >= FirstKeyUpdateInterval { + return true + } + } + if a.numRcvdWithCurrentKey >= KeyUpdateInterval { + a.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", a.numRcvdWithCurrentKey, a.keyPhase+1) + return true + } + if a.numSentWithCurrentKey >= KeyUpdateInterval { + a.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", a.numSentWithCurrentKey, a.keyPhase+1) + return true + } + return false +} + +func (a *updatableAEAD) KeyPhase() protocol.KeyPhaseBit { + if a.shouldInitiateKeyUpdate() { + a.rollKeys() + a.logger.Debugf("Initiating key update to key phase %d", a.keyPhase) + if a.tracer != nil && a.tracer.UpdatedKey != nil { + a.tracer.UpdatedKey(a.keyPhase, false) + } + } + return a.keyPhase.Bit() +} + +func (a *updatableAEAD) Overhead() int { + return a.aeadOverhead +} + +func (a *updatableAEAD) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + a.headerEncrypter.EncryptHeader(sample, firstByte, hdrBytes) +} + +func (a *updatableAEAD) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + a.headerDecrypter.DecryptHeader(sample, firstByte, hdrBytes) +} + +func (a *updatableAEAD) FirstPacketNumber() protocol.PacketNumber { + return a.firstPacketNumber +} diff --git a/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go b/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go new file mode 100644 index 000000000..a6032fc20 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go @@ -0,0 +1,50 @@ +package logutils + +import ( + "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 ConvertFrame(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 ConvertAckFrame(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 ConvertAckFrame(f *wire.AckFrame) *logging.AckFrame { + ranges := make([]wire.AckRange, 0, len(f.AckRanges)) + ranges = append(ranges, f.AckRanges...) + ack := &logging.AckFrame{ + AckRanges: ranges, + DelayTime: f.DelayTime, + ECNCE: f.ECNCE, + ECT0: f.ECT0, + ECT1: f.ECT1, + } + return ack +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go b/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go new file mode 100644 index 000000000..77259b5fa --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go @@ -0,0 +1,116 @@ +package protocol + +import ( + "crypto/rand" + "errors" + "fmt" + "io" +) + +var ErrInvalidConnectionIDLen = errors.New("invalid Connection ID length") + +// An ArbitraryLenConnectionID is a QUIC Connection ID able to represent Connection IDs according to RFC 8999. +// Future QUIC versions might allow connection ID lengths up to 255 bytes, while QUIC v1 +// restricts the length to 20 bytes. +type ArbitraryLenConnectionID []byte + +func (c ArbitraryLenConnectionID) Len() int { + return len(c) +} + +func (c ArbitraryLenConnectionID) Bytes() []byte { + return c +} + +func (c ArbitraryLenConnectionID) String() string { + if c.Len() == 0 { + return "(empty)" + } + return fmt.Sprintf("%x", c.Bytes()) +} + +const maxConnectionIDLen = 20 + +// A ConnectionID in QUIC +type ConnectionID struct { + b [20]byte + l uint8 +} + +// GenerateConnectionID generates a connection ID using cryptographic random +func GenerateConnectionID(l int) (ConnectionID, error) { + var c ConnectionID + c.l = uint8(l) + _, err := rand.Read(c.b[:l]) + return c, err +} + +// ParseConnectionID interprets b as a Connection ID. +// It panics if b is longer than 20 bytes. +func ParseConnectionID(b []byte) ConnectionID { + if len(b) > maxConnectionIDLen { + panic("invalid conn id length") + } + var c ConnectionID + c.l = uint8(len(b)) + copy(c.b[:c.l], b) + return c +} + +// GenerateConnectionIDForInitial generates a connection ID for the Initial packet. +// It uses a length randomly chosen between 8 and 20 bytes. +func GenerateConnectionIDForInitial() (ConnectionID, error) { + r := make([]byte, 1) + if _, err := rand.Read(r); err != nil { + return ConnectionID{}, err + } + l := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1) + return GenerateConnectionID(l) +} + +// ReadConnectionID reads a connection ID of length len from the given io.Reader. +// It returns io.EOF if there are not enough bytes to read. +func ReadConnectionID(r io.Reader, l int) (ConnectionID, error) { + var c ConnectionID + if l == 0 { + return c, nil + } + if l > maxConnectionIDLen { + return c, ErrInvalidConnectionIDLen + } + c.l = uint8(l) + _, err := io.ReadFull(r, c.b[:l]) + if err == io.ErrUnexpectedEOF { + return c, io.EOF + } + return c, err +} + +// Len returns the length of the connection ID in bytes +func (c ConnectionID) Len() int { + return int(c.l) +} + +// Bytes returns the byte representation +func (c ConnectionID) Bytes() []byte { + return c.b[:c.l] +} + +func (c ConnectionID) String() string { + if c.Len() == 0 { + return "(empty)" + } + return fmt.Sprintf("%x", c.Bytes()) +} + +type DefaultConnectionIDGenerator struct { + ConnLen int +} + +func (d *DefaultConnectionIDGenerator) GenerateConnectionID() (ConnectionID, error) { + return GenerateConnectionID(d.ConnLen) +} + +func (d *DefaultConnectionIDGenerator) ConnectionIDLen() int { + return d.ConnLen +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go b/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go new file mode 100644 index 000000000..32d38ab1e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go @@ -0,0 +1,30 @@ +package protocol + +// EncryptionLevel is the encryption level +// Default value is Unencrypted +type EncryptionLevel uint8 + +const ( + // EncryptionInitial is the Initial encryption level + EncryptionInitial EncryptionLevel = 1 + iota + // EncryptionHandshake is the Handshake encryption level + EncryptionHandshake + // Encryption0RTT is the 0-RTT encryption level + Encryption0RTT + // Encryption1RTT is the 1-RTT encryption level + Encryption1RTT +) + +func (e EncryptionLevel) String() string { + switch e { + case EncryptionInitial: + return "Initial" + case EncryptionHandshake: + return "Handshake" + case Encryption0RTT: + return "0-RTT" + case Encryption1RTT: + return "1-RTT" + } + return "unknown" +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go b/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go new file mode 100644 index 000000000..edd740cf6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go @@ -0,0 +1,36 @@ +package protocol + +// KeyPhase is the key phase +type KeyPhase uint64 + +// Bit determines the key phase bit +func (p KeyPhase) Bit() KeyPhaseBit { + if p%2 == 0 { + return KeyPhaseZero + } + return KeyPhaseOne +} + +// KeyPhaseBit is the key phase bit +type KeyPhaseBit uint8 + +const ( + // KeyPhaseUndefined is an undefined key phase + KeyPhaseUndefined KeyPhaseBit = iota + // KeyPhaseZero is key phase 0 + KeyPhaseZero + // KeyPhaseOne is key phase 1 + KeyPhaseOne +) + +func (p KeyPhaseBit) String() string { + //nolint:exhaustive + switch p { + case KeyPhaseZero: + return "0" + case KeyPhaseOne: + return "1" + default: + return "undefined" + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go b/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go new file mode 100644 index 000000000..bd3401619 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go @@ -0,0 +1,79 @@ +package protocol + +// A PacketNumber in QUIC +type PacketNumber int64 + +// InvalidPacketNumber is a packet number that is never sent. +// In QUIC, 0 is a valid packet number. +const InvalidPacketNumber PacketNumber = -1 + +// PacketNumberLen is the length of the packet number in bytes +type PacketNumberLen uint8 + +const ( + // PacketNumberLen1 is a packet number length of 1 byte + PacketNumberLen1 PacketNumberLen = 1 + // PacketNumberLen2 is a packet number length of 2 bytes + PacketNumberLen2 PacketNumberLen = 2 + // PacketNumberLen3 is a packet number length of 3 bytes + PacketNumberLen3 PacketNumberLen = 3 + // PacketNumberLen4 is a packet number length of 4 bytes + PacketNumberLen4 PacketNumberLen = 4 +) + +// DecodePacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number +func DecodePacketNumber( + packetNumberLength PacketNumberLen, + lastPacketNumber PacketNumber, + wirePacketNumber PacketNumber, +) PacketNumber { + var epochDelta PacketNumber + switch packetNumberLength { + case PacketNumberLen1: + epochDelta = PacketNumber(1) << 8 + case PacketNumberLen2: + epochDelta = PacketNumber(1) << 16 + case PacketNumberLen3: + epochDelta = PacketNumber(1) << 24 + case PacketNumberLen4: + epochDelta = PacketNumber(1) << 32 + } + epoch := lastPacketNumber & ^(epochDelta - 1) + var prevEpochBegin PacketNumber + if epoch > epochDelta { + prevEpochBegin = epoch - epochDelta + } + nextEpochBegin := epoch + epochDelta + return closestTo( + lastPacketNumber+1, + epoch+wirePacketNumber, + closestTo(lastPacketNumber+1, prevEpochBegin+wirePacketNumber, nextEpochBegin+wirePacketNumber), + ) +} + +func closestTo(target, a, b PacketNumber) PacketNumber { + if delta(target, a) < delta(target, b) { + return a + } + return b +} + +func delta(a, b PacketNumber) PacketNumber { + if a < b { + return b - a + } + return a - b +} + +// GetPacketNumberLengthForHeader gets the length of the packet number for the public header +// it never chooses a PacketNumberLen of 1 byte, since this is too short under certain circumstances +func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber) PacketNumberLen { + diff := uint64(packetNumber - leastUnacked) + if diff < (1 << (16 - 1)) { + return PacketNumberLen2 + } + if diff < (1 << (24 - 1)) { + return PacketNumberLen3 + } + return PacketNumberLen4 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go new file mode 100644 index 000000000..487cbc06b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go @@ -0,0 +1,183 @@ +package protocol + +import "time" + +// DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use. +const DesiredReceiveBufferSize = (1 << 20) * 2 // 2 MB + +// DesiredSendBufferSize is the kernel UDP send buffer size that we'd like to use. +const DesiredSendBufferSize = (1 << 20) * 2 // 2 MB + +// InitialPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets. +const InitialPacketSizeIPv4 = 1252 + +// InitialPacketSizeIPv6 is the maximum packet size that we use for sending IPv6 packets. +const InitialPacketSizeIPv6 = 1232 + +// MaxCongestionWindowPackets is the maximum congestion window in packet. +const MaxCongestionWindowPackets = 10000 + +// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the connection. +const MaxUndecryptablePackets = 32 + +// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window +// This is the value that Chromium is using +const ConnectionFlowControlMultiplier = 1.5 + +// DefaultInitialMaxStreamData is the default initial stream-level flow control window for receiving data +const DefaultInitialMaxStreamData = (1 << 10) * 512 // 512 kb + +// DefaultInitialMaxData is the connection-level flow control window for receiving data +const DefaultInitialMaxData = ConnectionFlowControlMultiplier * DefaultInitialMaxStreamData + +// DefaultMaxReceiveStreamFlowControlWindow is the default maximum stream-level flow control window for receiving data +const DefaultMaxReceiveStreamFlowControlWindow = 6 * (1 << 20) // 6 MB + +// DefaultMaxReceiveConnectionFlowControlWindow is the default connection-level flow control window for receiving data +const DefaultMaxReceiveConnectionFlowControlWindow = 15 * (1 << 20) // 15 MB + +// WindowUpdateThreshold is the fraction of the receive window that has to be consumed before an higher offset is advertised to the client +const WindowUpdateThreshold = 0.25 + +// DefaultMaxIncomingStreams is the maximum number of streams that a peer may open +const DefaultMaxIncomingStreams = 100 + +// DefaultMaxIncomingUniStreams is the maximum number of unidirectional streams that a peer may open +const DefaultMaxIncomingUniStreams = 100 + +// MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed. +const MaxServerUnprocessedPackets = 1024 + +// MaxConnUnprocessedPackets is the max number of packets stored in each connection that are not yet processed. +const MaxConnUnprocessedPackets = 256 + +// SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack. +// Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod. +const SkipPacketInitialPeriod PacketNumber = 256 + +// SkipPacketMaxPeriod is the maximum period length used for packet number skipping. +const SkipPacketMaxPeriod PacketNumber = 128 * 1024 + +// MaxAcceptQueueSize is the maximum number of connections that the server queues for accepting. +// If the queue is full, new connection attempts will be rejected. +const MaxAcceptQueueSize = 32 + +// TokenValidity is the duration that a (non-retry) token is considered valid +const TokenValidity = 24 * time.Hour + +// MaxOutstandingSentPackets is maximum number of packets saved for retransmission. +// When reached, it imposes a soft limit on sending new packets: +// Sending ACKs and retransmission is still allowed, but now new regular packets can be sent. +const MaxOutstandingSentPackets = 2 * MaxCongestionWindowPackets + +// MaxTrackedSentPackets is maximum number of sent packets saved for retransmission. +// When reached, no more packets will be sent. +// This value *must* be larger than MaxOutstandingSentPackets. +const MaxTrackedSentPackets = MaxOutstandingSentPackets * 5 / 4 + +// MaxNonAckElicitingAcks is the maximum number of packets containing an ACK, +// but no ack-eliciting frames, that we send in a row +const MaxNonAckElicitingAcks = 19 + +// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames +// prevents DoS attacks against the streamFrameSorter +const MaxStreamFrameSorterGaps = 1000 + +// MinStreamFrameBufferSize is the minimum data length of a received STREAM frame +// that we use the buffer for. This protects against a DoS where an attacker would send us +// very small STREAM frames to consume a lot of memory. +const MinStreamFrameBufferSize = 128 + +// MinCoalescedPacketSize is the minimum size of a coalesced packet that we pack. +// If a packet has less than this number of bytes, we won't coalesce any more packets onto it. +const MinCoalescedPacketSize = 128 + +// MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams. +// This limits the size of the ClientHello and Certificates that can be received. +const MaxCryptoStreamOffset = 16 * (1 << 10) + +// MinRemoteIdleTimeout is the minimum value that we accept for the remote idle timeout +const MinRemoteIdleTimeout = 5 * time.Second + +// DefaultIdleTimeout is the default idle timeout +const DefaultIdleTimeout = 30 * time.Second + +// DefaultHandshakeIdleTimeout is the default idle timeout used before handshake completion. +const DefaultHandshakeIdleTimeout = 5 * time.Second + +// MaxKeepAliveInterval is the maximum time until we send a packet to keep a connection alive. +// It should be shorter than the time that NATs clear their mapping. +const MaxKeepAliveInterval = 20 * time.Second + +// RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE. +// after this time all information about the old connection will be deleted +const RetiredConnectionIDDeleteTimeout = 5 * time.Second + +// MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame. +// This avoids splitting up STREAM frames into small pieces, which has 2 advantages: +// 1. it reduces the framing overhead +// 2. it reduces the head-of-line blocking, when a packet is lost +const MinStreamFrameSize ByteCount = 128 + +// MaxPostHandshakeCryptoFrameSize is the maximum size of CRYPTO frames +// we send after the handshake completes. +const MaxPostHandshakeCryptoFrameSize = 1000 + +// MaxAckFrameSize is the maximum size for an ACK frame that we write +// Due to the varint encoding, ACK frames can grow (almost) indefinitely large. +// The MaxAckFrameSize should be large enough to encode many ACK range, +// but must ensure that a maximum size ACK frame fits into one packet. +const MaxAckFrameSize ByteCount = 1000 + +// MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame. +// It also serves as a limit for the packet history. +// If at any point we keep track of more ranges, old ranges are discarded. +const MaxNumAckRanges = 32 + +// MinPacingDelay is the minimum duration that is used for packet pacing +// If the packet packing frequency is higher, multiple packets might be sent at once. +// Example: For a packet pacing delay of 200μs, we would send 5 packets at once, wait for 1ms, and so forth. +const MinPacingDelay = time.Millisecond + +// DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections +// if no other value is configured. +const DefaultConnectionIDLength = 4 + +// MaxActiveConnectionIDs is the number of connection IDs that we're storing. +const MaxActiveConnectionIDs = 4 + +// MaxIssuedConnectionIDs is the maximum number of connection IDs that we're issuing at the same time. +const MaxIssuedConnectionIDs = 6 + +// PacketsPerConnectionID is the number of packets we send using one connection ID. +// If the peer provices us with enough new connection IDs, we switch to a new connection ID. +const PacketsPerConnectionID = 10000 + +// AckDelayExponent is the ack delay exponent used when sending ACKs. +const AckDelayExponent = 3 + +// Estimated timer granularity. +// The loss detection timer will not be set to a value smaller than granularity. +const TimerGranularity = time.Millisecond + +// MaxAckDelay is the maximum time by which we delay sending ACKs. +const MaxAckDelay = 25 * time.Millisecond + +// MaxAckDelayInclGranularity is the max_ack_delay including the timer granularity. +// This is the value that should be advertised to the peer. +const MaxAckDelayInclGranularity = MaxAckDelay + TimerGranularity + +// KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update. +const KeyUpdateInterval = 100 * 1000 + +// Max0RTTQueueingDuration is the maximum time that we store 0-RTT packets in order to wait for the corresponding Initial to be received. +const Max0RTTQueueingDuration = 100 * time.Millisecond + +// Max0RTTQueues is the maximum number of connections that we buffer 0-RTT packets for. +const Max0RTTQueues = 32 + +// Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection. +// When a new connection is created, all buffered packets are passed to the connection immediately. +// To avoid blocking, this value has to be smaller than MaxConnUnprocessedPackets. +// To avoid packets being dropped as undecryptable by the connection, this value has to be smaller than MaxUndecryptablePackets. +const Max0RTTQueueLen = 31 diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go b/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go new file mode 100644 index 000000000..5a29d3ce2 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go @@ -0,0 +1,26 @@ +package protocol + +// Perspective determines if we're acting as a server or a client +type Perspective int + +// the perspectives +const ( + PerspectiveServer Perspective = 1 + PerspectiveClient Perspective = 2 +) + +// Opposite returns the perspective of the peer +func (p Perspective) Opposite() Perspective { + return 3 - p +} + +func (p Perspective) String() string { + switch p { + case PerspectiveServer: + return "server" + case PerspectiveClient: + return "client" + default: + return "invalid perspective" + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go new file mode 100644 index 000000000..d056cb9de --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go @@ -0,0 +1,152 @@ +package protocol + +import ( + "fmt" + "time" +) + +// The PacketType is the Long Header Type +type PacketType uint8 + +const ( + // PacketTypeInitial is the packet type of an Initial packet + PacketTypeInitial PacketType = 1 + iota + // PacketTypeRetry is the packet type of a Retry packet + PacketTypeRetry + // PacketTypeHandshake is the packet type of a Handshake packet + PacketTypeHandshake + // PacketType0RTT is the packet type of a 0-RTT packet + PacketType0RTT +) + +func (t PacketType) String() string { + switch t { + case PacketTypeInitial: + return "Initial" + case PacketTypeRetry: + return "Retry" + case PacketTypeHandshake: + return "Handshake" + case PacketType0RTT: + return "0-RTT Protected" + default: + return fmt.Sprintf("unknown packet type: %d", t) + } +} + +type ECN uint8 + +const ( + ECNUnsupported ECN = iota + ECNNon // 00 + ECT1 // 01 + ECT0 // 10 + ECNCE // 11 +) + +func ParseECNHeaderBits(bits byte) ECN { + switch bits { + case 0: + return ECNNon + case 0b00000010: + return ECT0 + case 0b00000001: + return ECT1 + case 0b00000011: + return ECNCE + default: + panic("invalid ECN bits") + } +} + +func (e ECN) ToHeaderBits() byte { + //nolint:exhaustive // There are only 4 values. + switch e { + case ECNNon: + return 0 + case ECT0: + return 0b00000010 + case ECT1: + return 0b00000001 + case ECNCE: + return 0b00000011 + default: + panic("ECN unsupported") + } +} + +func (e ECN) String() string { + switch e { + case ECNUnsupported: + return "ECN unsupported" + case ECNNon: + return "Not-ECT" + case ECT1: + return "ECT(1)" + case ECT0: + return "ECT(0)" + case ECNCE: + return "CE" + default: + return fmt.Sprintf("invalid ECN value: %d", e) + } +} + +// A ByteCount in QUIC +type ByteCount int64 + +// MaxByteCount is the maximum value of a ByteCount +const MaxByteCount = ByteCount(1<<62 - 1) + +// InvalidByteCount is an invalid byte count +const InvalidByteCount ByteCount = -1 + +// A StatelessResetToken is a stateless reset token. +type StatelessResetToken [16]byte + +// MaxPacketBufferSize maximum packet size of any QUIC packet, based on +// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header, +// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes. +// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452. +const MaxPacketBufferSize = 1452 + +// MaxLargePacketBufferSize is used when using GSO +const MaxLargePacketBufferSize = 20 * 1024 + +// MinInitialPacketSize is the minimum size an Initial packet is required to have. +const MinInitialPacketSize = 1200 + +// MinUnknownVersionPacketSize is the minimum size a packet with an unknown version +// needs to have in order to trigger a Version Negotiation packet. +const MinUnknownVersionPacketSize = MinInitialPacketSize + +// MinStatelessResetSize is the minimum size of a stateless reset packet that we send +const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */ + +// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet. +const MinConnectionIDLenInitial = 8 + +// DefaultAckDelayExponent is the default ack delay exponent +const DefaultAckDelayExponent = 3 + +// DefaultActiveConnectionIDLimit is the default active connection ID limit +const DefaultActiveConnectionIDLimit = 2 + +// MaxAckDelayExponent is the maximum ack delay exponent +const MaxAckDelayExponent = 20 + +// DefaultMaxAckDelay is the default max_ack_delay +const DefaultMaxAckDelay = 25 * time.Millisecond + +// MaxMaxAckDelay is the maximum max_ack_delay +const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond + +// MaxConnIDLen is the maximum length of the connection ID +const MaxConnIDLen = 20 + +// InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using +// AEAD_AES_128_GCM or AEAD_AES_265_GCM. +const InvalidPacketLimitAES = 1 << 52 + +// InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305. +const InvalidPacketLimitChaCha = 1 << 36 diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go b/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go new file mode 100644 index 000000000..ad7de864b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go @@ -0,0 +1,76 @@ +package protocol + +// StreamType encodes if this is a unidirectional or bidirectional stream +type StreamType uint8 + +const ( + // StreamTypeUni is a unidirectional stream + StreamTypeUni StreamType = iota + // StreamTypeBidi is a bidirectional stream + StreamTypeBidi +) + +// InvalidPacketNumber is a stream ID that is invalid. +// The first valid stream ID in QUIC is 0. +const InvalidStreamID StreamID = -1 + +// StreamNum is the stream number +type StreamNum int64 + +const ( + // InvalidStreamNum is an invalid stream number. + InvalidStreamNum = -1 + // MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames + // and as the stream count in the transport parameters + MaxStreamCount StreamNum = 1 << 60 +) + +// StreamID calculates the stream ID. +func (s StreamNum) StreamID(stype StreamType, pers Perspective) StreamID { + if s == 0 { + return InvalidStreamID + } + var first StreamID + switch stype { + case StreamTypeBidi: + switch pers { + case PerspectiveClient: + first = 0 + case PerspectiveServer: + first = 1 + } + case StreamTypeUni: + switch pers { + case PerspectiveClient: + first = 2 + case PerspectiveServer: + first = 3 + } + } + return first + 4*StreamID(s-1) +} + +// A StreamID in QUIC +type StreamID int64 + +// InitiatedBy says if the stream was initiated by the client or by the server +func (s StreamID) InitiatedBy() Perspective { + if s%2 == 0 { + return PerspectiveClient + } + return PerspectiveServer +} + +// Type says if this is a unidirectional or bidirectional stream +func (s StreamID) Type() StreamType { + if s%4 >= 2 { + return StreamTypeUni + } + return StreamTypeBidi +} + +// StreamNum returns how many streams in total are below this +// Example: for stream 9 it returns 3 (i.e. streams 1, 5 and 9) +func (s StreamID) StreamNum() StreamNum { + return StreamNum(s/4) + 1 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/version.go b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go new file mode 100644 index 000000000..025ade9b4 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go @@ -0,0 +1,114 @@ +package protocol + +import ( + "encoding/binary" + "fmt" + "math" + "sync" + "time" + + "golang.org/x/exp/rand" +) + +// Version is a version number as int +type Version uint32 + +// gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions +const ( + gquicVersion0 = 0x51303030 + maxGquicVersion = 0x51303439 +) + +// The version numbers, making grepping easier +const ( + VersionUnknown Version = math.MaxUint32 + versionDraft29 Version = 0xff00001d // draft-29 used to be a widely deployed version + Version1 Version = 0x1 + Version2 Version = 0x6b3343cf +) + +// SupportedVersions lists the versions that the server supports +// must be in sorted descending order +var SupportedVersions = []Version{Version1, Version2} + +// IsValidVersion says if the version is known to quic-go +func IsValidVersion(v Version) bool { + return v == Version1 || IsSupportedVersion(SupportedVersions, v) +} + +func (vn Version) String() string { + //nolint:exhaustive + switch vn { + case VersionUnknown: + return "unknown" + case versionDraft29: + return "draft-29" + case Version1: + return "v1" + case Version2: + return "v2" + default: + if vn.isGQUIC() { + return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) + } + return fmt.Sprintf("%#x", uint32(vn)) + } +} + +func (vn Version) isGQUIC() bool { + return vn > gquicVersion0 && vn <= maxGquicVersion +} + +func (vn Version) toGQUICVersion() int { + return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10) +} + +// IsSupportedVersion returns true if the server supports this version +func IsSupportedVersion(supported []Version, v Version) bool { + for _, t := range supported { + if t == v { + return true + } + } + return false +} + +// ChooseSupportedVersion finds the best version in the overlap of ours and theirs +// ours is a slice of versions that we support, sorted by our preference (descending) +// theirs is a slice of versions offered by the peer. The order does not matter. +// The bool returned indicates if a matching version was found. +func ChooseSupportedVersion(ours, theirs []Version) (Version, bool) { + for _, ourVer := range ours { + for _, theirVer := range theirs { + if ourVer == theirVer { + return ourVer, true + } + } + } + return 0, false +} + +var ( + versionNegotiationMx sync.Mutex + versionNegotiationRand = rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) +) + +// generateReservedVersion generates a reserved version (v & 0x0f0f0f0f == 0x0a0a0a0a) +func generateReservedVersion() Version { + var b [4]byte + _, _ = versionNegotiationRand.Read(b[:]) // ignore the error here. Failure to read random data doesn't break anything + return Version((binary.BigEndian.Uint32(b[:]) | 0x0a0a0a0a) & 0xfafafafa) +} + +// GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position. +// It doesn't modify the supported slice. +func GetGreasedVersions(supported []Version) []Version { + versionNegotiationMx.Lock() + defer versionNegotiationMx.Unlock() + randPos := rand.Intn(len(supported) + 1) + greased := make([]Version, len(supported)+1) + copy(greased, supported[:randPos]) + greased[randPos] = generateReservedVersion() + copy(greased[randPos+1:], supported[randPos:]) + return greased +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go new file mode 100644 index 000000000..00361308e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go @@ -0,0 +1,87 @@ +package qerr + +import ( + "crypto/tls" + "fmt" +) + +// TransportErrorCode is a QUIC transport error. +type TransportErrorCode uint64 + +// The error codes defined by QUIC +const ( + NoError TransportErrorCode = 0x0 + InternalError TransportErrorCode = 0x1 + ConnectionRefused TransportErrorCode = 0x2 + FlowControlError TransportErrorCode = 0x3 + StreamLimitError TransportErrorCode = 0x4 + StreamStateError TransportErrorCode = 0x5 + FinalSizeError TransportErrorCode = 0x6 + FrameEncodingError TransportErrorCode = 0x7 + TransportParameterError TransportErrorCode = 0x8 + ConnectionIDLimitError TransportErrorCode = 0x9 + ProtocolViolation TransportErrorCode = 0xa + InvalidToken TransportErrorCode = 0xb + ApplicationErrorErrorCode TransportErrorCode = 0xc + CryptoBufferExceeded TransportErrorCode = 0xd + KeyUpdateError TransportErrorCode = 0xe + AEADLimitReached TransportErrorCode = 0xf + NoViablePathError TransportErrorCode = 0x10 +) + +func (e TransportErrorCode) IsCryptoError() bool { + return e >= 0x100 && e < 0x200 +} + +// Message is a description of the error. +// It only returns a non-empty string for crypto errors. +func (e TransportErrorCode) Message() string { + if !e.IsCryptoError() { + return "" + } + return tls.AlertError(e - 0x100).Error() +} + +func (e TransportErrorCode) String() string { + switch e { + case NoError: + return "NO_ERROR" + case InternalError: + return "INTERNAL_ERROR" + case ConnectionRefused: + return "CONNECTION_REFUSED" + case FlowControlError: + return "FLOW_CONTROL_ERROR" + case StreamLimitError: + return "STREAM_LIMIT_ERROR" + case StreamStateError: + return "STREAM_STATE_ERROR" + case FinalSizeError: + return "FINAL_SIZE_ERROR" + case FrameEncodingError: + return "FRAME_ENCODING_ERROR" + case TransportParameterError: + return "TRANSPORT_PARAMETER_ERROR" + case ConnectionIDLimitError: + return "CONNECTION_ID_LIMIT_ERROR" + case ProtocolViolation: + return "PROTOCOL_VIOLATION" + case InvalidToken: + return "INVALID_TOKEN" + case ApplicationErrorErrorCode: + return "APPLICATION_ERROR" + case CryptoBufferExceeded: + return "CRYPTO_BUFFER_EXCEEDED" + case KeyUpdateError: + return "KEY_UPDATE_ERROR" + case AEADLimitReached: + return "AEAD_LIMIT_REACHED" + case NoViablePathError: + return "NO_VIABLE_PATH" + default: + if e.IsCryptoError() { + return fmt.Sprintf("CRYPTO_ERROR %#x", uint16(e)) + } + return fmt.Sprintf("unknown error code: %#x", uint16(e)) + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go new file mode 100644 index 000000000..8f5936dfa --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go @@ -0,0 +1,139 @@ +package qerr + +import ( + "fmt" + "net" + + "github.com/quic-go/quic-go/internal/protocol" +) + +var ( + ErrHandshakeTimeout = &HandshakeTimeoutError{} + ErrIdleTimeout = &IdleTimeoutError{} +) + +type TransportError struct { + Remote bool + FrameType uint64 + ErrorCode TransportErrorCode + ErrorMessage string + error error // only set for local errors, sometimes +} + +var _ error = &TransportError{} + +// NewLocalCryptoError create a new TransportError instance for a crypto error +func NewLocalCryptoError(tlsAlert uint8, err error) *TransportError { + return &TransportError{ + ErrorCode: 0x100 + TransportErrorCode(tlsAlert), + error: err, + } +} + +func (e *TransportError) Error() string { + str := fmt.Sprintf("%s (%s)", e.ErrorCode.String(), getRole(e.Remote)) + if e.FrameType != 0 { + str += fmt.Sprintf(" (frame type: %#x)", e.FrameType) + } + msg := e.ErrorMessage + if len(msg) == 0 && e.error != nil { + msg = e.error.Error() + } + if len(msg) == 0 { + msg = e.ErrorCode.Message() + } + if len(msg) == 0 { + return str + } + return str + ": " + msg +} + +func (e *TransportError) Is(target error) bool { + return target == net.ErrClosed +} + +func (e *TransportError) Unwrap() error { + return e.error +} + +// An ApplicationErrorCode is an application-defined error code. +type ApplicationErrorCode uint64 + +func (e *ApplicationError) Is(target error) bool { + return target == net.ErrClosed +} + +// A StreamErrorCode is an error code used to cancel streams. +type StreamErrorCode uint64 + +type ApplicationError struct { + Remote bool + ErrorCode ApplicationErrorCode + ErrorMessage string +} + +var _ error = &ApplicationError{} + +func (e *ApplicationError) Error() string { + if len(e.ErrorMessage) == 0 { + return fmt.Sprintf("Application error %#x (%s)", e.ErrorCode, getRole(e.Remote)) + } + return fmt.Sprintf("Application error %#x (%s): %s", e.ErrorCode, getRole(e.Remote), e.ErrorMessage) +} + +type IdleTimeoutError struct{} + +var _ error = &IdleTimeoutError{} + +func (e *IdleTimeoutError) Timeout() bool { return true } +func (e *IdleTimeoutError) Temporary() bool { return false } +func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" } +func (e *IdleTimeoutError) Is(target error) bool { return target == net.ErrClosed } + +type HandshakeTimeoutError struct{} + +var _ error = &HandshakeTimeoutError{} + +func (e *HandshakeTimeoutError) Timeout() bool { return true } +func (e *HandshakeTimeoutError) Temporary() bool { return false } +func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" } +func (e *HandshakeTimeoutError) Is(target error) bool { return target == net.ErrClosed } + +// A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version. +type VersionNegotiationError struct { + Ours []protocol.Version + Theirs []protocol.Version +} + +func (e *VersionNegotiationError) Error() string { + return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs) +} + +func (e *VersionNegotiationError) Is(target error) bool { + return target == net.ErrClosed +} + +// A StatelessResetError occurs when we receive a stateless reset. +type StatelessResetError struct { + Token protocol.StatelessResetToken +} + +var _ net.Error = &StatelessResetError{} + +func (e *StatelessResetError) Error() string { + return fmt.Sprintf("received a stateless reset with token %x", e.Token) +} + +func (e *StatelessResetError) Is(target error) bool { + return target == net.ErrClosed +} + +func (e *StatelessResetError) Timeout() bool { return false } +func (e *StatelessResetError) Temporary() bool { return true } + +func getRole(remote bool) string { + if remote { + return "remote" + } + return "local" +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite.go b/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite.go new file mode 100644 index 000000000..32a921cd5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite.go @@ -0,0 +1,52 @@ +package qtls + +import ( + "crypto/tls" + "fmt" + "unsafe" +) + +//go:linkname cipherSuitesTLS13 crypto/tls.cipherSuitesTLS13 +var cipherSuitesTLS13 []unsafe.Pointer + +//go:linkname defaultCipherSuitesTLS13 crypto/tls.defaultCipherSuitesTLS13 +var defaultCipherSuitesTLS13 []uint16 + +//go:linkname defaultCipherSuitesTLS13NoAES crypto/tls.defaultCipherSuitesTLS13NoAES +var defaultCipherSuitesTLS13NoAES []uint16 + +var cipherSuitesModified bool + +// SetCipherSuite modifies the cipherSuiteTLS13 slice of cipher suites inside qtls +// such that it only contains the cipher suite with the chosen id. +// The reset function returned resets them back to the original value. +func SetCipherSuite(id uint16) (reset func()) { + if cipherSuitesModified { + panic("cipher suites modified multiple times without resetting") + } + cipherSuitesModified = true + + origCipherSuitesTLS13 := append([]unsafe.Pointer{}, cipherSuitesTLS13...) + origDefaultCipherSuitesTLS13 := append([]uint16{}, defaultCipherSuitesTLS13...) + origDefaultCipherSuitesTLS13NoAES := append([]uint16{}, defaultCipherSuitesTLS13NoAES...) + // The order is given by the order of the slice elements in cipherSuitesTLS13 in qtls. + switch id { + case tls.TLS_AES_128_GCM_SHA256: + cipherSuitesTLS13 = cipherSuitesTLS13[:1] + case tls.TLS_CHACHA20_POLY1305_SHA256: + cipherSuitesTLS13 = cipherSuitesTLS13[1:2] + case tls.TLS_AES_256_GCM_SHA384: + cipherSuitesTLS13 = cipherSuitesTLS13[2:] + default: + panic(fmt.Sprintf("unexpected cipher suite: %d", id)) + } + defaultCipherSuitesTLS13 = []uint16{id} + defaultCipherSuitesTLS13NoAES = []uint16{id} + + return func() { + cipherSuitesTLS13 = origCipherSuitesTLS13 + defaultCipherSuitesTLS13 = origDefaultCipherSuitesTLS13 + defaultCipherSuitesTLS13NoAES = origDefaultCipherSuitesTLS13NoAES + cipherSuitesModified = false + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go b/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go new file mode 100644 index 000000000..4acac9e2e --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go @@ -0,0 +1,70 @@ +package qtls + +import ( + "crypto/tls" + "sync" +) + +type clientSessionCache struct { + mx sync.Mutex + getData func(earlyData bool) []byte + setData func(data []byte, earlyData bool) (allowEarlyData bool) + wrapped tls.ClientSessionCache +} + +var _ tls.ClientSessionCache = &clientSessionCache{} + +func (c *clientSessionCache) Put(key string, cs *tls.ClientSessionState) { + c.mx.Lock() + defer c.mx.Unlock() + + if cs == nil { + c.wrapped.Put(key, nil) + return + } + ticket, state, err := cs.ResumptionState() + if err != nil || state == nil { + c.wrapped.Put(key, cs) + return + } + state.Extra = append(state.Extra, addExtraPrefix(c.getData(state.EarlyData))) + newCS, err := tls.NewResumptionState(ticket, state) + if err != nil { + // It's not clear why this would error. Just save the original state. + c.wrapped.Put(key, cs) + return + } + c.wrapped.Put(key, newCS) +} + +func (c *clientSessionCache) Get(key string) (*tls.ClientSessionState, bool) { + c.mx.Lock() + defer c.mx.Unlock() + + cs, ok := c.wrapped.Get(key) + if !ok || cs == nil { + return cs, ok + } + ticket, state, err := cs.ResumptionState() + if err != nil { + // It's not clear why this would error. + // Remove the ticket from the session cache, so we don't run into this error over and over again + c.wrapped.Put(key, nil) + return nil, false + } + // restore QUIC transport parameters and RTT stored in state.Extra + if extra := findExtraData(state.Extra); extra != nil { + earlyData := c.setData(extra, state.EarlyData) + if state.EarlyData { + state.EarlyData = earlyData + } + } + session, err := tls.NewResumptionState(ticket, state) + if err != nil { + // It's not clear why this would error. + // Remove the ticket from the session cache, so we don't run into this error over and over again + c.wrapped.Put(key, nil) + return nil, false + } + return session, true +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/qtls.go b/vendor/github.com/quic-go/quic-go/internal/qtls/qtls.go new file mode 100644 index 000000000..ebcd9d4de --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/qtls.go @@ -0,0 +1,124 @@ +package qtls + +import ( + "bytes" + "crypto/tls" + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" +) + +func SetupConfigForServer(qconf *tls.QUICConfig, _ bool, getData func() []byte, handleSessionTicket func([]byte, bool) bool) { + conf := qconf.TLSConfig + + // Workaround for https://github.com/golang/go/issues/60506. + // This initializes the session tickets _before_ cloning the config. + _, _ = conf.DecryptTicket(nil, tls.ConnectionState{}) + + conf = conf.Clone() + conf.MinVersion = tls.VersionTLS13 + qconf.TLSConfig = conf + + // add callbacks to save transport parameters into the session ticket + origWrapSession := conf.WrapSession + conf.WrapSession = func(cs tls.ConnectionState, state *tls.SessionState) ([]byte, error) { + // Add QUIC session ticket + state.Extra = append(state.Extra, addExtraPrefix(getData())) + + if origWrapSession != nil { + return origWrapSession(cs, state) + } + b, err := conf.EncryptTicket(cs, state) + return b, err + } + origUnwrapSession := conf.UnwrapSession + // UnwrapSession might be called multiple times, as the client can use multiple session tickets. + // However, using 0-RTT is only possible with the first session ticket. + // crypto/tls guarantees that this callback is called in the same order as the session ticket in the ClientHello. + var unwrapCount int + conf.UnwrapSession = func(identity []byte, connState tls.ConnectionState) (*tls.SessionState, error) { + unwrapCount++ + var state *tls.SessionState + var err error + if origUnwrapSession != nil { + state, err = origUnwrapSession(identity, connState) + } else { + state, err = conf.DecryptTicket(identity, connState) + } + if err != nil || state == nil { + return nil, err + } + + extra := findExtraData(state.Extra) + if extra != nil { + state.EarlyData = handleSessionTicket(extra, state.EarlyData && unwrapCount == 1) + } else { + state.EarlyData = false + } + + return state, nil + } +} + +func SetupConfigForClient( + qconf *tls.QUICConfig, + getData func(earlyData bool) []byte, + setData func(data []byte, earlyData bool) (allowEarlyData bool), +) { + conf := qconf.TLSConfig + if conf.ClientSessionCache != nil { + origCache := conf.ClientSessionCache + conf.ClientSessionCache = &clientSessionCache{ + wrapped: origCache, + getData: getData, + setData: setData, + } + } +} + +func ToTLSEncryptionLevel(e protocol.EncryptionLevel) tls.QUICEncryptionLevel { + switch e { + case protocol.EncryptionInitial: + return tls.QUICEncryptionLevelInitial + case protocol.EncryptionHandshake: + return tls.QUICEncryptionLevelHandshake + case protocol.Encryption1RTT: + return tls.QUICEncryptionLevelApplication + case protocol.Encryption0RTT: + return tls.QUICEncryptionLevelEarly + default: + panic(fmt.Sprintf("unexpected encryption level: %s", e)) + } +} + +func FromTLSEncryptionLevel(e tls.QUICEncryptionLevel) protocol.EncryptionLevel { + switch e { + case tls.QUICEncryptionLevelInitial: + return protocol.EncryptionInitial + case tls.QUICEncryptionLevelHandshake: + return protocol.EncryptionHandshake + case tls.QUICEncryptionLevelApplication: + return protocol.Encryption1RTT + case tls.QUICEncryptionLevelEarly: + return protocol.Encryption0RTT + default: + panic(fmt.Sprintf("unexpect encryption level: %s", e)) + } +} + +const extraPrefix = "quic-go1" + +func addExtraPrefix(b []byte) []byte { + return append([]byte(extraPrefix), b...) +} + +func findExtraData(extras [][]byte) []byte { + prefix := []byte(extraPrefix) + for _, extra := range extras { + if len(extra) < len(prefix) || !bytes.Equal(prefix, extra[:len(prefix)]) { + continue + } + return extra[len(prefix):] + } + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go b/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go new file mode 100644 index 000000000..b5b9d6fc7 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go @@ -0,0 +1,26 @@ +package utils + +import ( + "bufio" + "io" +) + +type bufferedWriteCloser struct { + *bufio.Writer + io.Closer +} + +// NewBufferedWriteCloser creates an io.WriteCloser from a bufio.Writer and an io.Closer +func NewBufferedWriteCloser(writer *bufio.Writer, closer io.Closer) io.WriteCloser { + return &bufferedWriteCloser{ + Writer: writer, + Closer: closer, + } +} + +func (h bufferedWriteCloser) Close() error { + if err := h.Writer.Flush(); err != nil { + return err + } + return h.Closer.Close() +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go new file mode 100644 index 000000000..a9b715e2f --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go @@ -0,0 +1,21 @@ +package utils + +import ( + "bytes" + "io" +) + +// A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint32([]byte) uint32 + Uint24([]byte) uint32 + Uint16([]byte) uint16 + + ReadUint32(io.ByteReader) (uint32, error) + ReadUint24(io.ByteReader) (uint32, error) + ReadUint16(io.ByteReader) (uint16, error) + + WriteUint32(*bytes.Buffer, uint32) + WriteUint24(*bytes.Buffer, uint32) + WriteUint16(*bytes.Buffer, uint16) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go new file mode 100644 index 000000000..834a711b9 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go @@ -0,0 +1,103 @@ +package utils + +import ( + "bytes" + "encoding/binary" + "io" +) + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian ByteOrder = bigEndian{} + +type bigEndian struct{} + +var _ ByteOrder = &bigEndian{} + +// ReadUintN reads N bytes +func (bigEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) { + var res uint64 + for i := uint8(0); i < length; i++ { + bt, err := b.ReadByte() + if err != nil { + return 0, err + } + res ^= uint64(bt) << ((length - 1 - i) * 8) + } + return res, nil +} + +// ReadUint32 reads a uint32 +func (bigEndian) ReadUint32(b io.ByteReader) (uint32, error) { + var b1, b2, b3, b4 uint8 + var err error + if b4, err = b.ReadByte(); err != nil { + return 0, err + } + if b3, err = b.ReadByte(); err != nil { + return 0, err + } + if b2, err = b.ReadByte(); err != nil { + return 0, err + } + if b1, err = b.ReadByte(); err != nil { + return 0, err + } + return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16 + uint32(b4)<<24, nil +} + +// ReadUint24 reads a uint24 +func (bigEndian) ReadUint24(b io.ByteReader) (uint32, error) { + var b1, b2, b3 uint8 + var err error + if b3, err = b.ReadByte(); err != nil { + return 0, err + } + if b2, err = b.ReadByte(); err != nil { + return 0, err + } + if b1, err = b.ReadByte(); err != nil { + return 0, err + } + return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16, nil +} + +// ReadUint16 reads a uint16 +func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) { + var b1, b2 uint8 + var err error + if b2, err = b.ReadByte(); err != nil { + return 0, err + } + if b1, err = b.ReadByte(); err != nil { + return 0, err + } + return uint16(b1) + uint16(b2)<<8, nil +} + +func (bigEndian) Uint32(b []byte) uint32 { + return binary.BigEndian.Uint32(b) +} + +func (bigEndian) Uint24(b []byte) uint32 { + _ = b[2] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16 +} + +func (bigEndian) Uint16(b []byte) uint16 { + return binary.BigEndian.Uint16(b) +} + +// WriteUint32 writes a uint32 +func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) { + b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)}) +} + +// WriteUint24 writes a uint24 +func (bigEndian) WriteUint24(b *bytes.Buffer, i uint32) { + b.Write([]byte{uint8(i >> 16), uint8(i >> 8), uint8(i)}) +} + +// WriteUint16 writes a uint16 +func (bigEndian) WriteUint16(b *bytes.Buffer, i uint16) { + b.Write([]byte{uint8(i >> 8), uint8(i)}) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/ip.go b/vendor/github.com/quic-go/quic-go/internal/utils/ip.go new file mode 100644 index 000000000..7ac7ffec1 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/ip.go @@ -0,0 +1,10 @@ +package utils + +import "net" + +func IsIPv4(ip net.IP) bool { + // If ip is not an IPv4 address, To4 returns nil. + // Note that there might be some corner cases, where this is not correct. + // See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6. + return ip.To4() != nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md new file mode 100644 index 000000000..66482f4fb --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md @@ -0,0 +1,6 @@ +# Usage + +This is the Go standard library implementation of a linked list +(https://golang.org/src/container/list/list.go), with the following modifications: +* it uses Go generics +* it allows passing in a `sync.Pool` (via the `NewWithPool` constructor) to reduce allocations of `Element` structs diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go new file mode 100644 index 000000000..804a34444 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go @@ -0,0 +1,264 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package list implements a doubly linked list. +// +// To iterate over a list (where l is a *List[T]): +// +// for e := l.Front(); e != nil; e = e.Next() { +// // do something with e.Value +// } +package list + +import "sync" + +func NewPool[T any]() *sync.Pool { + return &sync.Pool{New: func() any { return &Element[T]{} }} +} + +// Element is an element of a linked list. +type Element[T any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Element[T] + + // The list to which this element belongs. + list *List[T] + + // The value stored with this element. + Value T +} + +// Next returns the next list element or nil. +func (e *Element[T]) Next() *Element[T] { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *Element[T]) Prev() *Element[T] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +func (e *Element[T]) List() *List[T] { + return e.list +} + +// List represents a doubly linked list. +// The zero value for List is an empty list ready to use. +type List[T any] struct { + root Element[T] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element + + pool *sync.Pool +} + +// Init initializes or clears list l. +func (l *List[T]) Init() *List[T] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// New returns an initialized list. +func New[T any]() *List[T] { return new(List[T]).Init() } + +// NewWithPool returns an initialized list, using a sync.Pool for list elements. +func NewWithPool[T any](pool *sync.Pool) *List[T] { + l := &List[T]{pool: pool} + return l.Init() +} + +// Len returns the number of elements of list l. +// The complexity is O(1). +func (l *List[T]) Len() int { return l.len } + +// Front returns the first element of list l or nil if the list is empty. +func (l *List[T]) Front() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *List[T]) Back() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List value. +func (l *List[T]) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *List[T]) insert(e, at *Element[T]) *Element[T] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { + var e *Element[T] + if l.pool != nil { + e = l.pool.Get().(*Element[T]) + } else { + e = &Element[T]{} + } + e.Value = v + return l.insert(e, at) +} + +// remove removes e from its list, decrements l.len +func (l *List[T]) remove(e *Element[T]) { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + if l.pool != nil { + l.pool.Put(e) + } + l.len-- +} + +// move moves e to next to at. +func (l *List[T]) move(e, at *Element[T]) { + if e == at { + return + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +// The element must not be nil. +func (l *List[T]) Remove(e *Element[T]) T { + v := e.Value + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) + } + return v +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List[T]) PushFront(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, &l.root) +} + +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List[T]) PushBack(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, l.root.prev) +} + +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) +} + +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToFront(e *Element[T]) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToBack(e *Element[T]) { + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// MoveBefore moves element e to its new position before mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveBefore(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveAfter(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark) +} + +// PushBackList inserts a copy of another list at the back of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushBackList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of another list at the front of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushFrontList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/log.go b/vendor/github.com/quic-go/quic-go/internal/utils/log.go new file mode 100644 index 000000000..89b52c0d9 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/log.go @@ -0,0 +1,131 @@ +package utils + +import ( + "fmt" + "log" + "os" + "strings" + "time" +) + +// LogLevel of quic-go +type LogLevel uint8 + +const ( + // LogLevelNothing disables + LogLevelNothing LogLevel = iota + // LogLevelError enables err logs + LogLevelError + // LogLevelInfo enables info logs (e.g. packets) + LogLevelInfo + // LogLevelDebug enables debug logs (e.g. packet contents) + LogLevelDebug +) + +const logEnv = "QUIC_GO_LOG_LEVEL" + +// A Logger logs. +type Logger interface { + SetLogLevel(LogLevel) + SetLogTimeFormat(format string) + WithPrefix(prefix string) Logger + Debug() bool + + Errorf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Debugf(format string, args ...interface{}) +} + +// DefaultLogger is used by quic-go for logging. +var DefaultLogger Logger + +type defaultLogger struct { + prefix string + + logLevel LogLevel + timeFormat string +} + +var _ Logger = &defaultLogger{} + +// SetLogLevel sets the log level +func (l *defaultLogger) SetLogLevel(level LogLevel) { + l.logLevel = level +} + +// SetLogTimeFormat sets the format of the timestamp +// an empty string disables the logging of timestamps +func (l *defaultLogger) SetLogTimeFormat(format string) { + log.SetFlags(0) // disable timestamp logging done by the log package + l.timeFormat = format +} + +// Debugf logs something +func (l *defaultLogger) Debugf(format string, args ...interface{}) { + if l.logLevel == LogLevelDebug { + l.logMessage(format, args...) + } +} + +// Infof logs something +func (l *defaultLogger) Infof(format string, args ...interface{}) { + if l.logLevel >= LogLevelInfo { + l.logMessage(format, args...) + } +} + +// Errorf logs something +func (l *defaultLogger) Errorf(format string, args ...interface{}) { + if l.logLevel >= LogLevelError { + l.logMessage(format, args...) + } +} + +func (l *defaultLogger) logMessage(format string, args ...interface{}) { + var pre string + + if len(l.timeFormat) > 0 { + pre = time.Now().Format(l.timeFormat) + " " + } + if len(l.prefix) > 0 { + pre += l.prefix + " " + } + log.Printf(pre+format, args...) +} + +func (l *defaultLogger) WithPrefix(prefix string) Logger { + if len(l.prefix) > 0 { + prefix = l.prefix + " " + prefix + } + return &defaultLogger{ + logLevel: l.logLevel, + timeFormat: l.timeFormat, + prefix: prefix, + } +} + +// Debug returns true if the log level is LogLevelDebug +func (l *defaultLogger) Debug() bool { + return l.logLevel == LogLevelDebug +} + +func init() { + DefaultLogger = &defaultLogger{} + DefaultLogger.SetLogLevel(readLoggingEnv()) +} + +func readLoggingEnv() LogLevel { + switch strings.ToLower(os.Getenv(logEnv)) { + case "": + return LogLevelNothing + case "debug": + return LogLevelDebug + case "info": + return LogLevelInfo + case "error": + return LogLevelError + default: + fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/quic-go/quic-go/wiki/Logging") + return LogLevelNothing + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go b/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go new file mode 100644 index 000000000..03a9c9a87 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go @@ -0,0 +1,36 @@ +package utils + +import ( + "math" + "time" +) + +// InfDuration is a duration of infinite length +const InfDuration = time.Duration(math.MaxInt64) + +// MinNonZeroDuration return the minimum duration that's not zero. +func MinNonZeroDuration(a, b time.Duration) time.Duration { + if a == 0 { + return b + } + if b == 0 { + return a + } + return min(a, b) +} + +// MinTime returns the earlier time +func MinTime(a, b time.Time) time.Time { + if a.After(b) { + return b + } + return a +} + +// MaxTime returns the later time +func MaxTime(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/rand.go b/vendor/github.com/quic-go/quic-go/internal/utils/rand.go new file mode 100644 index 000000000..30069144a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/rand.go @@ -0,0 +1,29 @@ +package utils + +import ( + "crypto/rand" + "encoding/binary" +) + +// Rand is a wrapper around crypto/rand that adds some convenience functions known from math/rand. +type Rand struct { + buf [4]byte +} + +func (r *Rand) Int31() int32 { + rand.Read(r.buf[:]) + return int32(binary.BigEndian.Uint32(r.buf[:]) & ^uint32(1<<31)) +} + +// copied from the standard library math/rand implementation of Int63n +func (r *Rand) Int31n(n int32) int32 { + if n&(n-1) == 0 { // n is power of two, can mask + return r.Int31() & (n - 1) + } + max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) + v := r.Int31() + for v > max { + v = r.Int31() + } + return v % n +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/ringbuffer/ringbuffer.go b/vendor/github.com/quic-go/quic-go/internal/utils/ringbuffer/ringbuffer.go new file mode 100644 index 000000000..f9b2c797b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/ringbuffer/ringbuffer.go @@ -0,0 +1,96 @@ +package ringbuffer + +// A RingBuffer is a ring buffer. +// It acts as a heap that doesn't cause any allocations. +type RingBuffer[T any] struct { + ring []T + headPos, tailPos int + full bool +} + +// Init preallocates a buffer with a certain size. +func (r *RingBuffer[T]) Init(size int) { + r.ring = make([]T, size) +} + +// Len returns the number of elements in the ring buffer. +func (r *RingBuffer[T]) Len() int { + if r.full { + return len(r.ring) + } + if r.tailPos >= r.headPos { + return r.tailPos - r.headPos + } + return r.tailPos - r.headPos + len(r.ring) +} + +// Empty says if the ring buffer is empty. +func (r *RingBuffer[T]) Empty() bool { + return !r.full && r.headPos == r.tailPos +} + +// PushBack adds a new element. +// If the ring buffer is full, its capacity is increased first. +func (r *RingBuffer[T]) PushBack(t T) { + if r.full || len(r.ring) == 0 { + r.grow() + } + r.ring[r.tailPos] = t + r.tailPos++ + if r.tailPos == len(r.ring) { + r.tailPos = 0 + } + if r.tailPos == r.headPos { + r.full = true + } +} + +// PopFront returns the next element. +// It must not be called when the buffer is empty, that means that +// callers might need to check if there are elements in the buffer first. +func (r *RingBuffer[T]) PopFront() T { + if r.Empty() { + panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: pop from an empty queue") + } + r.full = false + t := r.ring[r.headPos] + r.ring[r.headPos] = *new(T) + r.headPos++ + if r.headPos == len(r.ring) { + r.headPos = 0 + } + return t +} + +// PeekFront returns the next element. +// It must not be called when the buffer is empty, that means that +// callers might need to check if there are elements in the buffer first. +func (r *RingBuffer[T]) PeekFront() T { + if r.Empty() { + panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: peek from an empty queue") + } + return r.ring[r.headPos] +} + +// Grow the maximum size of the queue. +// This method assume the queue is full. +func (r *RingBuffer[T]) grow() { + oldRing := r.ring + newSize := len(oldRing) * 2 + if newSize == 0 { + newSize = 1 + } + r.ring = make([]T, newSize) + headLen := copy(r.ring, oldRing[r.headPos:]) + copy(r.ring[headLen:], oldRing[:r.headPos]) + r.headPos, r.tailPos, r.full = 0, len(oldRing), false +} + +// Clear removes all elements. +func (r *RingBuffer[T]) Clear() { + var zeroValue T + for i := range r.ring { + r.ring[i] = zeroValue + } + r.headPos, r.tailPos, r.full = 0, 0, false +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go new file mode 100644 index 000000000..463b95424 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go @@ -0,0 +1,131 @@ +package utils + +import ( + "time" + + "github.com/quic-go/quic-go/internal/protocol" +) + +const ( + rttAlpha = 0.125 + oneMinusAlpha = 1 - rttAlpha + rttBeta = 0.25 + oneMinusBeta = 1 - rttBeta + // The default RTT used before an RTT sample is taken. + defaultInitialRTT = 100 * time.Millisecond +) + +// RTTStats provides round-trip statistics +type RTTStats struct { + hasMeasurement bool + + minRTT time.Duration + latestRTT time.Duration + smoothedRTT time.Duration + meanDeviation time.Duration + + maxAckDelay time.Duration +} + +// NewRTTStats makes a properly initialized RTTStats object +func NewRTTStats() *RTTStats { + return &RTTStats{} +} + +// MinRTT Returns the minRTT for the entire connection. +// May return Zero if no valid updates have occurred. +func (r *RTTStats) MinRTT() time.Duration { return r.minRTT } + +// LatestRTT returns the most recent rtt measurement. +// May return Zero if no valid updates have occurred. +func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT } + +// SmoothedRTT returns the smoothed RTT for the connection. +// May return Zero if no valid updates have occurred. +func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT } + +// MeanDeviation gets the mean deviation +func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation } + +// MaxAckDelay gets the max_ack_delay advertised by the peer +func (r *RTTStats) MaxAckDelay() time.Duration { return r.maxAckDelay } + +// PTO gets the probe timeout duration. +func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration { + if r.SmoothedRTT() == 0 { + return 2 * defaultInitialRTT + } + pto := r.SmoothedRTT() + max(4*r.MeanDeviation(), protocol.TimerGranularity) + if includeMaxAckDelay { + pto += r.MaxAckDelay() + } + return pto +} + +// UpdateRTT updates the RTT based on a new sample. +func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) { + if sendDelta == InfDuration || sendDelta <= 0 { + return + } + + // Update r.minRTT first. r.minRTT does not use an rttSample corrected for + // ackDelay but the raw observed sendDelta, since poor clock granularity at + // the client may cause a high ackDelay to result in underestimation of the + // r.minRTT. + if r.minRTT == 0 || r.minRTT > sendDelta { + r.minRTT = sendDelta + } + + // Correct for ackDelay if information received from the peer results in a + // an RTT sample at least as large as minRTT. Otherwise, only use the + // sendDelta. + sample := sendDelta + if sample-r.minRTT >= ackDelay { + sample -= ackDelay + } + r.latestRTT = sample + // First time call. + if !r.hasMeasurement { + r.hasMeasurement = true + r.smoothedRTT = sample + r.meanDeviation = sample / 2 + } else { + r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32((r.smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond + r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond + } +} + +// SetMaxAckDelay sets the max_ack_delay +func (r *RTTStats) SetMaxAckDelay(mad time.Duration) { + r.maxAckDelay = mad +} + +// SetInitialRTT sets the initial RTT. +// It is used during the 0-RTT handshake when restoring the RTT stats from the session state. +func (r *RTTStats) SetInitialRTT(t time.Duration) { + // On the server side, by the time we get to process the session ticket, + // we might already have obtained an RTT measurement. + // This can happen if we received the ClientHello in multiple pieces, and one of those pieces was lost. + // Discard the restored value. A fresh measurement is always better. + if r.hasMeasurement { + return + } + r.smoothedRTT = t + r.latestRTT = t +} + +// OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset. +func (r *RTTStats) OnConnectionMigration() { + r.latestRTT = 0 + r.minRTT = 0 + r.smoothedRTT = 0 + r.meanDeviation = 0 +} + +// ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt +// is larger. The mean deviation is increased to the most recent deviation if +// it's larger. +func (r *RTTStats) ExpireSmoothedMetrics() { + r.meanDeviation = max(r.meanDeviation, (r.smoothedRTT - r.latestRTT).Abs()) + r.smoothedRTT = max(r.smoothedRTT, r.latestRTT) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/timer.go b/vendor/github.com/quic-go/quic-go/internal/utils/timer.go new file mode 100644 index 000000000..361106c8a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/utils/timer.go @@ -0,0 +1,57 @@ +package utils + +import ( + "math" + "time" +) + +// A Timer wrapper that behaves correctly when resetting +type Timer struct { + t *time.Timer + read bool + deadline time.Time +} + +// NewTimer creates a new timer that is not set +func NewTimer() *Timer { + return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))} +} + +// Chan returns the channel of the wrapped timer +func (t *Timer) Chan() <-chan time.Time { + return t.t.C +} + +// Reset the timer, no matter whether the value was read or not +func (t *Timer) Reset(deadline time.Time) { + if deadline.Equal(t.deadline) && !t.read { + // No need to reset the timer + return + } + + // We need to drain the timer if the value from its channel was not read yet. + // See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU + if !t.t.Stop() && !t.read { + <-t.t.C + } + if !deadline.IsZero() { + t.t.Reset(time.Until(deadline)) + } + + t.read = false + t.deadline = deadline +} + +// SetRead should be called after the value from the chan was read +func (t *Timer) SetRead() { + t.read = true +} + +func (t *Timer) Deadline() time.Time { + return t.deadline +} + +// Stop stops the timer +func (t *Timer) Stop() { + t.t.Stop() +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go new file mode 100644 index 000000000..a0f3feb06 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go @@ -0,0 +1,266 @@ +package wire + +import ( + "bytes" + "errors" + "sort" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/quicvarint" +) + +var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges") + +// An AckFrame is an ACK frame +type AckFrame struct { + AckRanges []AckRange // has to be ordered. The highest ACK range goes first, the lowest ACK range goes last + DelayTime time.Duration + + ECT0, ECT1, ECNCE uint64 +} + +// parseAckFrame reads an ACK frame +func parseAckFrame(frame *AckFrame, r *bytes.Reader, typ uint64, ackDelayExponent uint8, _ protocol.Version) error { + ecn := typ == ackECNFrameType + + la, err := quicvarint.Read(r) + if err != nil { + return err + } + largestAcked := protocol.PacketNumber(la) + delay, err := quicvarint.Read(r) + if err != nil { + return err + } + + delayTime := time.Duration(delay*1< largestAcked { + return errors.New("invalid first ACK range") + } + smallest := largestAcked - ackBlock + frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largestAcked}) + + // read all the other ACK ranges + for i := uint64(0); i < numBlocks; i++ { + g, err := quicvarint.Read(r) + if err != nil { + return err + } + gap := protocol.PacketNumber(g) + if smallest < gap+2 { + return errInvalidAckRanges + } + largest := smallest - gap - 2 + + ab, err := quicvarint.Read(r) + if err != nil { + return err + } + ackBlock := protocol.PacketNumber(ab) + + if ackBlock > largest { + return errInvalidAckRanges + } + smallest = largest - ackBlock + frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largest}) + } + + if !frame.validateAckRanges() { + return errInvalidAckRanges + } + + if ecn { + ect0, err := quicvarint.Read(r) + if err != nil { + return err + } + frame.ECT0 = ect0 + ect1, err := quicvarint.Read(r) + if err != nil { + return err + } + frame.ECT1 = ect1 + ecnce, err := quicvarint.Read(r) + if err != nil { + return err + } + frame.ECNCE = ecnce + } + + return nil +} + +// Append appends an ACK frame. +func (f *AckFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 + if hasECN { + b = append(b, ackECNFrameType) + } else { + b = append(b, ackFrameType) + } + b = quicvarint.Append(b, uint64(f.LargestAcked())) + b = quicvarint.Append(b, encodeAckDelay(f.DelayTime)) + + numRanges := f.numEncodableAckRanges() + b = quicvarint.Append(b, uint64(numRanges-1)) + + // write the first range + _, firstRange := f.encodeAckRange(0) + b = quicvarint.Append(b, firstRange) + + // write all the other range + for i := 1; i < numRanges; i++ { + gap, len := f.encodeAckRange(i) + b = quicvarint.Append(b, gap) + b = quicvarint.Append(b, len) + } + + if hasECN { + b = quicvarint.Append(b, f.ECT0) + b = quicvarint.Append(b, f.ECT1) + b = quicvarint.Append(b, f.ECNCE) + } + return b, nil +} + +// Length of a written frame +func (f *AckFrame) Length(_ protocol.Version) protocol.ByteCount { + largestAcked := f.AckRanges[0].Largest + numRanges := f.numEncodableAckRanges() + + length := 1 + quicvarint.Len(uint64(largestAcked)) + quicvarint.Len(encodeAckDelay(f.DelayTime)) + + length += quicvarint.Len(uint64(numRanges - 1)) + lowestInFirstRange := f.AckRanges[0].Smallest + length += quicvarint.Len(uint64(largestAcked - lowestInFirstRange)) + + for i := 1; i < numRanges; i++ { + gap, len := f.encodeAckRange(i) + length += quicvarint.Len(gap) + length += quicvarint.Len(len) + } + if f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 { + length += quicvarint.Len(f.ECT0) + length += quicvarint.Len(f.ECT1) + length += quicvarint.Len(f.ECNCE) + } + return length +} + +// gets the number of ACK ranges that can be encoded +// such that the resulting frame is smaller than the maximum ACK frame size +func (f *AckFrame) numEncodableAckRanges() int { + length := 1 + quicvarint.Len(uint64(f.LargestAcked())) + quicvarint.Len(encodeAckDelay(f.DelayTime)) + length += 2 // assume that the number of ranges will consume 2 bytes + for i := 1; i < len(f.AckRanges); i++ { + gap, len := f.encodeAckRange(i) + rangeLen := quicvarint.Len(gap) + quicvarint.Len(len) + if length+rangeLen > protocol.MaxAckFrameSize { + // Writing range i would exceed the MaxAckFrameSize. + // So encode one range less than that. + return i - 1 + } + length += rangeLen + } + return len(f.AckRanges) +} + +func (f *AckFrame) encodeAckRange(i int) (uint64 /* gap */, uint64 /* length */) { + if i == 0 { + return 0, uint64(f.AckRanges[0].Largest - f.AckRanges[0].Smallest) + } + return uint64(f.AckRanges[i-1].Smallest - f.AckRanges[i].Largest - 2), + uint64(f.AckRanges[i].Largest - f.AckRanges[i].Smallest) +} + +// HasMissingRanges returns if this frame reports any missing packets +func (f *AckFrame) HasMissingRanges() bool { + return len(f.AckRanges) > 1 +} + +func (f *AckFrame) validateAckRanges() bool { + if len(f.AckRanges) == 0 { + return false + } + + // check the validity of every single ACK range + for _, ackRange := range f.AckRanges { + if ackRange.Smallest > ackRange.Largest { + return false + } + } + + // check the consistency for ACK with multiple NACK ranges + for i, ackRange := range f.AckRanges { + if i == 0 { + continue + } + lastAckRange := f.AckRanges[i-1] + if lastAckRange.Smallest <= ackRange.Smallest { + return false + } + if lastAckRange.Smallest <= ackRange.Largest+1 { + return false + } + } + + return true +} + +// LargestAcked is the largest acked packet number +func (f *AckFrame) LargestAcked() protocol.PacketNumber { + return f.AckRanges[0].Largest +} + +// LowestAcked is the lowest acked packet number +func (f *AckFrame) LowestAcked() protocol.PacketNumber { + return f.AckRanges[len(f.AckRanges)-1].Smallest +} + +// AcksPacket determines if this ACK frame acks a certain packet number +func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool { + if p < f.LowestAcked() || p > f.LargestAcked() { + return false + } + + i := sort.Search(len(f.AckRanges), func(i int) bool { + return p >= f.AckRanges[i].Smallest + }) + // i will always be < len(f.AckRanges), since we checked above that p is not bigger than the largest acked + return p <= f.AckRanges[i].Largest +} + +func (f *AckFrame) Reset() { + f.DelayTime = 0 + f.ECT0 = 0 + f.ECT1 = 0 + f.ECNCE = 0 + for _, r := range f.AckRanges { + r.Largest = 0 + r.Smallest = 0 + } + f.AckRanges = f.AckRanges[:0] +} + +func encodeAckDelay(delay time.Duration) uint64 { + return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent))) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go new file mode 100644 index 000000000..03a1235ee --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go @@ -0,0 +1,14 @@ +package wire + +import "github.com/quic-go/quic-go/internal/protocol" + +// AckRange is an ACK range +type AckRange struct { + Smallest protocol.PacketNumber + Largest protocol.PacketNumber +} + +// Len returns the number of packets contained in this ACK range +func (r AckRange) Len() protocol.PacketNumber { + return r.Largest - r.Smallest + 1 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go new file mode 100644 index 000000000..df3624474 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go @@ -0,0 +1,78 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A ConnectionCloseFrame is a CONNECTION_CLOSE frame +type ConnectionCloseFrame struct { + IsApplicationError bool + ErrorCode uint64 + FrameType uint64 + ReasonPhrase string +} + +func parseConnectionCloseFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*ConnectionCloseFrame, error) { + f := &ConnectionCloseFrame{IsApplicationError: typ == applicationCloseFrameType} + ec, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + f.ErrorCode = ec + // read the Frame Type, if this is not an application error + if !f.IsApplicationError { + ft, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + f.FrameType = ft + } + var reasonPhraseLen uint64 + reasonPhraseLen, err = quicvarint.Read(r) + if err != nil { + return nil, err + } + // shortcut to prevent the unnecessary allocation of dataLen bytes + // if the dataLen is larger than the remaining length of the packet + // reading the whole reason phrase would result in EOF when attempting to READ + if int(reasonPhraseLen) > r.Len() { + return nil, io.EOF + } + + reasonPhrase := make([]byte, reasonPhraseLen) + if _, err := io.ReadFull(r, reasonPhrase); err != nil { + // this should never happen, since we already checked the reasonPhraseLen earlier + return nil, err + } + f.ReasonPhrase = string(reasonPhrase) + return f, nil +} + +// Length of a written frame +func (f *ConnectionCloseFrame) Length(protocol.Version) protocol.ByteCount { + length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase)) + if !f.IsApplicationError { + length += quicvarint.Len(f.FrameType) // for the frame type + } + return length +} + +func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + if f.IsApplicationError { + b = append(b, applicationCloseFrameType) + } else { + b = append(b, connectionCloseFrameType) + } + + b = quicvarint.Append(b, f.ErrorCode) + if !f.IsApplicationError { + b = quicvarint.Append(b, f.FrameType) + } + b = quicvarint.Append(b, uint64(len(f.ReasonPhrase))) + b = append(b, []byte(f.ReasonPhrase)...) + return b, nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go new file mode 100644 index 000000000..d42146391 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go @@ -0,0 +1,98 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A CryptoFrame is a CRYPTO frame +type CryptoFrame struct { + Offset protocol.ByteCount + Data []byte +} + +func parseCryptoFrame(r *bytes.Reader, _ protocol.Version) (*CryptoFrame, error) { + frame := &CryptoFrame{} + offset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.Offset = protocol.ByteCount(offset) + dataLen, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + if dataLen > uint64(r.Len()) { + return nil, io.EOF + } + if dataLen != 0 { + frame.Data = make([]byte, dataLen) + if _, err := io.ReadFull(r, frame.Data); err != nil { + // this should never happen, since we already checked the dataLen earlier + return nil, err + } + } + return frame, nil +} + +func (f *CryptoFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, cryptoFrameType) + b = quicvarint.Append(b, uint64(f.Offset)) + b = quicvarint.Append(b, uint64(len(f.Data))) + b = append(b, f.Data...) + return b, nil +} + +// Length of a written frame +func (f *CryptoFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.Offset)) + quicvarint.Len(uint64(len(f.Data))) + protocol.ByteCount(len(f.Data)) +} + +// MaxDataLen returns the maximum data length +func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount { + // pretend that the data size will be 1 bytes + // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards + headerLen := 1 + quicvarint.Len(uint64(f.Offset)) + 1 + if headerLen > maxSize { + return 0 + } + maxDataLen := maxSize - headerLen + if quicvarint.Len(uint64(maxDataLen)) != 1 { + maxDataLen-- + } + return maxDataLen +} + +// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. +// It returns if the frame was actually split. +// The frame might not be split if: +// * the size is large enough to fit the whole frame +// * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil. +func (f *CryptoFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.Version) (*CryptoFrame, bool /* was splitting required */) { + if f.Length(version) <= maxSize { + return nil, false + } + + n := f.MaxDataLen(maxSize) + if n == 0 { + return nil, true + } + + newLen := protocol.ByteCount(len(f.Data)) - n + + new := &CryptoFrame{} + new.Offset = f.Offset + new.Data = make([]byte, newLen) + + // swap the data slices + new.Data, f.Data = f.Data, new.Data + + copy(f.Data, new.Data[n:]) + new.Data = new.Data[:n] + f.Offset += n + + return new, true +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go new file mode 100644 index 000000000..8fe2acb54 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go @@ -0,0 +1,31 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A DataBlockedFrame is a DATA_BLOCKED frame +type DataBlockedFrame struct { + MaximumData protocol.ByteCount +} + +func parseDataBlockedFrame(r *bytes.Reader, _ protocol.Version) (*DataBlockedFrame, error) { + offset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + return &DataBlockedFrame{MaximumData: protocol.ByteCount(offset)}, nil +} + +func (f *DataBlockedFrame) Append(b []byte, version protocol.Version) ([]byte, error) { + b = append(b, dataBlockedFrameType) + return quicvarint.Append(b, uint64(f.MaximumData)), nil +} + +// Length of a written frame +func (f *DataBlockedFrame) Length(version protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.MaximumData)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go new file mode 100644 index 000000000..8e406f1ad --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go @@ -0,0 +1,86 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// MaxDatagramSize is the maximum size of a DATAGRAM frame (RFC 9221). +// By setting it to a large value, we allow all datagrams that fit into a QUIC packet. +// The value is chosen such that it can still be encoded as a 2 byte varint. +// This is a var and not a const so it can be set in tests. +var MaxDatagramSize protocol.ByteCount = 16383 + +// A DatagramFrame is a DATAGRAM frame +type DatagramFrame struct { + DataLenPresent bool + Data []byte +} + +func parseDatagramFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*DatagramFrame, error) { + f := &DatagramFrame{} + f.DataLenPresent = typ&0x1 > 0 + + var length uint64 + if f.DataLenPresent { + var err error + len, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + if len > uint64(r.Len()) { + return nil, io.EOF + } + length = len + } else { + length = uint64(r.Len()) + } + f.Data = make([]byte, length) + if _, err := io.ReadFull(r, f.Data); err != nil { + return nil, err + } + return f, nil +} + +func (f *DatagramFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + typ := uint8(0x30) + if f.DataLenPresent { + typ ^= 0b1 + } + b = append(b, typ) + if f.DataLenPresent { + b = quicvarint.Append(b, uint64(len(f.Data))) + } + b = append(b, f.Data...) + return b, nil +} + +// MaxDataLen returns the maximum data length +func (f *DatagramFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.Version) protocol.ByteCount { + headerLen := protocol.ByteCount(1) + if f.DataLenPresent { + // pretend that the data size will be 1 bytes + // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards + headerLen++ + } + if headerLen > maxSize { + return 0 + } + maxDataLen := maxSize - headerLen + if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 { + maxDataLen-- + } + return maxDataLen +} + +// Length of a written frame +func (f *DatagramFrame) Length(_ protocol.Version) protocol.ByteCount { + length := 1 + protocol.ByteCount(len(f.Data)) + if f.DataLenPresent { + length += quicvarint.Len(uint64(len(f.Data))) + } + return length +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go b/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go new file mode 100644 index 000000000..e04d91b78 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go @@ -0,0 +1,210 @@ +package wire + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/quicvarint" +) + +// ErrInvalidReservedBits is returned when the reserved bits are incorrect. +// When this error is returned, parsing continues, and an ExtendedHeader is returned. +// This is necessary because we need to decrypt the packet in that case, +// in order to avoid a timing side-channel. +var ErrInvalidReservedBits = errors.New("invalid reserved bits") + +// ExtendedHeader is the header of a QUIC packet. +type ExtendedHeader struct { + Header + + typeByte byte + + KeyPhase protocol.KeyPhaseBit + + PacketNumberLen protocol.PacketNumberLen + PacketNumber protocol.PacketNumber + + parsedLen protocol.ByteCount +} + +func (h *ExtendedHeader) parse(b *bytes.Reader, v protocol.Version) (bool /* reserved bits valid */, error) { + startLen := b.Len() + // read the (now unencrypted) first byte + var err error + h.typeByte, err = b.ReadByte() + if err != nil { + return false, err + } + if _, err := b.Seek(int64(h.Header.ParsedLen())-1, io.SeekCurrent); err != nil { + return false, err + } + reservedBitsValid, err := h.parseLongHeader(b, v) + if err != nil { + return false, err + } + h.parsedLen = protocol.ByteCount(startLen - b.Len()) + return reservedBitsValid, err +} + +func (h *ExtendedHeader) parseLongHeader(b *bytes.Reader, _ protocol.Version) (bool /* reserved bits valid */, error) { + if err := h.readPacketNumber(b); err != nil { + return false, err + } + if h.typeByte&0xc != 0 { + return false, nil + } + return true, nil +} + +func (h *ExtendedHeader) readPacketNumber(b *bytes.Reader) error { + h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1 + switch h.PacketNumberLen { + case protocol.PacketNumberLen1: + n, err := b.ReadByte() + if err != nil { + return err + } + h.PacketNumber = protocol.PacketNumber(n) + case protocol.PacketNumberLen2: + n, err := utils.BigEndian.ReadUint16(b) + if err != nil { + return err + } + h.PacketNumber = protocol.PacketNumber(n) + case protocol.PacketNumberLen3: + n, err := utils.BigEndian.ReadUint24(b) + if err != nil { + return err + } + h.PacketNumber = protocol.PacketNumber(n) + case protocol.PacketNumberLen4: + n, err := utils.BigEndian.ReadUint32(b) + if err != nil { + return err + } + h.PacketNumber = protocol.PacketNumber(n) + default: + return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen) + } + return nil +} + +// Append appends the Header. +func (h *ExtendedHeader) Append(b []byte, v protocol.Version) ([]byte, error) { + if h.DestConnectionID.Len() > protocol.MaxConnIDLen { + return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.DestConnectionID.Len()) + } + if h.SrcConnectionID.Len() > protocol.MaxConnIDLen { + return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.SrcConnectionID.Len()) + } + + var packetType uint8 + if v == protocol.Version2 { + //nolint:exhaustive + switch h.Type { + case protocol.PacketTypeInitial: + packetType = 0b01 + case protocol.PacketType0RTT: + packetType = 0b10 + case protocol.PacketTypeHandshake: + packetType = 0b11 + case protocol.PacketTypeRetry: + packetType = 0b00 + } + } else { + //nolint:exhaustive + switch h.Type { + case protocol.PacketTypeInitial: + packetType = 0b00 + case protocol.PacketType0RTT: + packetType = 0b01 + case protocol.PacketTypeHandshake: + packetType = 0b10 + case protocol.PacketTypeRetry: + packetType = 0b11 + } + } + firstByte := 0xc0 | packetType<<4 + if h.Type != protocol.PacketTypeRetry { + // Retry packets don't have a packet number + firstByte |= uint8(h.PacketNumberLen - 1) + } + + b = append(b, firstByte) + b = append(b, make([]byte, 4)...) + binary.BigEndian.PutUint32(b[len(b)-4:], uint32(h.Version)) + b = append(b, uint8(h.DestConnectionID.Len())) + b = append(b, h.DestConnectionID.Bytes()...) + b = append(b, uint8(h.SrcConnectionID.Len())) + b = append(b, h.SrcConnectionID.Bytes()...) + + //nolint:exhaustive + switch h.Type { + case protocol.PacketTypeRetry: + b = append(b, h.Token...) + return b, nil + case protocol.PacketTypeInitial: + b = quicvarint.Append(b, uint64(len(h.Token))) + b = append(b, h.Token...) + } + b = quicvarint.AppendWithLen(b, uint64(h.Length), 2) + return appendPacketNumber(b, h.PacketNumber, h.PacketNumberLen) +} + +// ParsedLen returns the number of bytes that were consumed when parsing the header +func (h *ExtendedHeader) ParsedLen() protocol.ByteCount { + return h.parsedLen +} + +// GetLength determines the length of the Header. +func (h *ExtendedHeader) GetLength(_ protocol.Version) protocol.ByteCount { + length := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn ID len */ + protocol.ByteCount(h.DestConnectionID.Len()) + 1 /* src conn ID len */ + protocol.ByteCount(h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + 2 /* length */ + if h.Type == protocol.PacketTypeInitial { + length += quicvarint.Len(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token)) + } + return length +} + +// Log logs the Header +func (h *ExtendedHeader) Log(logger utils.Logger) { + var token string + if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry { + if len(h.Token) == 0 { + token = "Token: (empty), " + } else { + token = fmt.Sprintf("Token: %#x, ", h.Token) + } + if h.Type == protocol.PacketTypeRetry { + logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.Version) + return + } + } + logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %d, PacketNumberLen: %d, Length: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.Length, h.Version) +} + +func appendPacketNumber(b []byte, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen) ([]byte, error) { + switch pnLen { + case protocol.PacketNumberLen1: + b = append(b, uint8(pn)) + case protocol.PacketNumberLen2: + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(pn)) + b = append(b, buf...) + case protocol.PacketNumberLen3: + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(pn)) + b = append(b, buf[1:]...) + case protocol.PacketNumberLen4: + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(pn)) + b = append(b, buf...) + default: + return nil, fmt.Errorf("invalid packet number length: %d", pnLen) + } + return b, nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go b/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go new file mode 100644 index 000000000..cf7d4cecd --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go @@ -0,0 +1,192 @@ +package wire + +import ( + "bytes" + "errors" + "fmt" + "reflect" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/quicvarint" +) + +const ( + pingFrameType = 0x1 + ackFrameType = 0x2 + ackECNFrameType = 0x3 + resetStreamFrameType = 0x4 + stopSendingFrameType = 0x5 + cryptoFrameType = 0x6 + newTokenFrameType = 0x7 + maxDataFrameType = 0x10 + maxStreamDataFrameType = 0x11 + bidiMaxStreamsFrameType = 0x12 + uniMaxStreamsFrameType = 0x13 + dataBlockedFrameType = 0x14 + streamDataBlockedFrameType = 0x15 + bidiStreamBlockedFrameType = 0x16 + uniStreamBlockedFrameType = 0x17 + newConnectionIDFrameType = 0x18 + retireConnectionIDFrameType = 0x19 + pathChallengeFrameType = 0x1a + pathResponseFrameType = 0x1b + connectionCloseFrameType = 0x1c + applicationCloseFrameType = 0x1d + handshakeDoneFrameType = 0x1e +) + +// The FrameParser parses QUIC frames, one by one. +type FrameParser struct { + r bytes.Reader // cached bytes.Reader, so we don't have to repeatedly allocate them + + ackDelayExponent uint8 + supportsDatagrams bool + + // To avoid allocating when parsing, keep a single ACK frame struct. + // It is used over and over again. + ackFrame *AckFrame +} + +// NewFrameParser creates a new frame parser. +func NewFrameParser(supportsDatagrams bool) *FrameParser { + return &FrameParser{ + r: *bytes.NewReader(nil), + supportsDatagrams: supportsDatagrams, + ackFrame: &AckFrame{}, + } +} + +// ParseNext parses the next frame. +// It skips PADDING frames. +func (p *FrameParser) ParseNext(data []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (int, Frame, error) { + startLen := len(data) + p.r.Reset(data) + frame, err := p.parseNext(&p.r, encLevel, v) + n := startLen - p.r.Len() + p.r.Reset(nil) + return n, frame, err +} + +func (p *FrameParser) parseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, error) { + for r.Len() != 0 { + typ, err := quicvarint.Read(r) + if err != nil { + return nil, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, + ErrorMessage: err.Error(), + } + } + if typ == 0x0 { // skip PADDING frames + continue + } + + f, err := p.parseFrame(r, typ, encLevel, v) + if err != nil { + return nil, &qerr.TransportError{ + FrameType: typ, + ErrorCode: qerr.FrameEncodingError, + ErrorMessage: err.Error(), + } + } + return f, nil + } + return nil, nil +} + +func (p *FrameParser) parseFrame(r *bytes.Reader, typ uint64, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, error) { + var frame Frame + var err error + if typ&0xf8 == 0x8 { + frame, err = parseStreamFrame(r, typ, v) + } else { + switch typ { + case pingFrameType: + frame = &PingFrame{} + case ackFrameType, ackECNFrameType: + ackDelayExponent := p.ackDelayExponent + if encLevel != protocol.Encryption1RTT { + ackDelayExponent = protocol.DefaultAckDelayExponent + } + p.ackFrame.Reset() + err = parseAckFrame(p.ackFrame, r, typ, ackDelayExponent, v) + frame = p.ackFrame + case resetStreamFrameType: + frame, err = parseResetStreamFrame(r, v) + case stopSendingFrameType: + frame, err = parseStopSendingFrame(r, v) + case cryptoFrameType: + frame, err = parseCryptoFrame(r, v) + case newTokenFrameType: + frame, err = parseNewTokenFrame(r, v) + case maxDataFrameType: + frame, err = parseMaxDataFrame(r, v) + case maxStreamDataFrameType: + frame, err = parseMaxStreamDataFrame(r, v) + case bidiMaxStreamsFrameType, uniMaxStreamsFrameType: + frame, err = parseMaxStreamsFrame(r, typ, v) + case dataBlockedFrameType: + frame, err = parseDataBlockedFrame(r, v) + case streamDataBlockedFrameType: + frame, err = parseStreamDataBlockedFrame(r, v) + case bidiStreamBlockedFrameType, uniStreamBlockedFrameType: + frame, err = parseStreamsBlockedFrame(r, typ, v) + case newConnectionIDFrameType: + frame, err = parseNewConnectionIDFrame(r, v) + case retireConnectionIDFrameType: + frame, err = parseRetireConnectionIDFrame(r, v) + case pathChallengeFrameType: + frame, err = parsePathChallengeFrame(r, v) + case pathResponseFrameType: + frame, err = parsePathResponseFrame(r, v) + case connectionCloseFrameType, applicationCloseFrameType: + frame, err = parseConnectionCloseFrame(r, typ, v) + case handshakeDoneFrameType: + frame = &HandshakeDoneFrame{} + case 0x30, 0x31: + if p.supportsDatagrams { + frame, err = parseDatagramFrame(r, typ, v) + break + } + fallthrough + default: + err = errors.New("unknown frame type") + } + } + if err != nil { + return nil, err + } + if !p.isAllowedAtEncLevel(frame, encLevel) { + return nil, fmt.Errorf("%s not allowed at encryption level %s", reflect.TypeOf(frame).Elem().Name(), encLevel) + } + return frame, nil +} + +func (p *FrameParser) isAllowedAtEncLevel(f Frame, encLevel protocol.EncryptionLevel) bool { + switch encLevel { + case protocol.EncryptionInitial, protocol.EncryptionHandshake: + switch f.(type) { + case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *PingFrame: + return true + default: + return false + } + case protocol.Encryption0RTT: + switch f.(type) { + case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *NewTokenFrame, *PathResponseFrame, *RetireConnectionIDFrame: + return false + default: + return true + } + case protocol.Encryption1RTT: + return true + default: + panic("unknown encryption level") + } +} + +// SetAckDelayExponent sets the acknowledgment delay exponent (sent in the transport parameters). +// This value is used to scale the ACK Delay field in the ACK frame. +func (p *FrameParser) SetAckDelayExponent(exp uint8) { + p.ackDelayExponent = exp +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go new file mode 100644 index 000000000..85dd64745 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go @@ -0,0 +1,17 @@ +package wire + +import ( + "github.com/quic-go/quic-go/internal/protocol" +) + +// A HandshakeDoneFrame is a HANDSHAKE_DONE frame +type HandshakeDoneFrame struct{} + +func (f *HandshakeDoneFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + return append(b, handshakeDoneFrameType), nil +} + +// Length of a written frame +func (f *HandshakeDoneFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/header.go b/vendor/github.com/quic-go/quic-go/internal/wire/header.go new file mode 100644 index 000000000..299116849 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/header.go @@ -0,0 +1,300 @@ +package wire + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/quicvarint" +) + +// ParseConnectionID parses the destination connection ID of a packet. +func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) { + if len(data) == 0 { + return protocol.ConnectionID{}, io.EOF + } + if !IsLongHeaderPacket(data[0]) { + if len(data) < shortHeaderConnIDLen+1 { + return protocol.ConnectionID{}, io.EOF + } + return protocol.ParseConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil + } + if len(data) < 6 { + return protocol.ConnectionID{}, io.EOF + } + destConnIDLen := int(data[5]) + if destConnIDLen > protocol.MaxConnIDLen { + return protocol.ConnectionID{}, protocol.ErrInvalidConnectionIDLen + } + if len(data) < 6+destConnIDLen { + return protocol.ConnectionID{}, io.EOF + } + return protocol.ParseConnectionID(data[6 : 6+destConnIDLen]), nil +} + +// ParseArbitraryLenConnectionIDs parses the most general form of a Long Header packet, +// using only the version-independent packet format as described in Section 5.1 of RFC 8999: +// https://datatracker.ietf.org/doc/html/rfc8999#section-5.1. +// This function should only be called on Long Header packets for which we don't support the version. +func ParseArbitraryLenConnectionIDs(data []byte) (bytesParsed int, dest, src protocol.ArbitraryLenConnectionID, _ error) { + r := bytes.NewReader(data) + remaining := r.Len() + src, dest, err := parseArbitraryLenConnectionIDs(r) + return remaining - r.Len(), src, dest, err +} + +func parseArbitraryLenConnectionIDs(r *bytes.Reader) (dest, src protocol.ArbitraryLenConnectionID, _ error) { + r.Seek(5, io.SeekStart) // skip first byte and version field + destConnIDLen, err := r.ReadByte() + if err != nil { + return nil, nil, err + } + destConnID := make(protocol.ArbitraryLenConnectionID, destConnIDLen) + if _, err := io.ReadFull(r, destConnID); err != nil { + if err == io.ErrUnexpectedEOF { + err = io.EOF + } + return nil, nil, err + } + srcConnIDLen, err := r.ReadByte() + if err != nil { + return nil, nil, err + } + srcConnID := make(protocol.ArbitraryLenConnectionID, srcConnIDLen) + if _, err := io.ReadFull(r, srcConnID); err != nil { + if err == io.ErrUnexpectedEOF { + err = io.EOF + } + return nil, nil, err + } + return destConnID, srcConnID, nil +} + +func IsPotentialQUICPacket(firstByte byte) bool { + return firstByte&0x40 > 0 +} + +// IsLongHeaderPacket says if this is a Long Header packet +func IsLongHeaderPacket(firstByte byte) bool { + return firstByte&0x80 > 0 +} + +// ParseVersion parses the QUIC version. +// It should only be called for Long Header packets (Short Header packets don't contain a version number). +func ParseVersion(data []byte) (protocol.Version, error) { + if len(data) < 5 { + return 0, io.EOF + } + return protocol.Version(binary.BigEndian.Uint32(data[1:5])), nil +} + +// IsVersionNegotiationPacket says if this is a version negotiation packet +func IsVersionNegotiationPacket(b []byte) bool { + if len(b) < 5 { + return false + } + return IsLongHeaderPacket(b[0]) && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 +} + +// Is0RTTPacket says if this is a 0-RTT packet. +// A packet sent with a version we don't understand can never be a 0-RTT packet. +func Is0RTTPacket(b []byte) bool { + if len(b) < 5 { + return false + } + if !IsLongHeaderPacket(b[0]) { + return false + } + version := protocol.Version(binary.BigEndian.Uint32(b[1:5])) + //nolint:exhaustive // We only need to test QUIC versions that we support. + switch version { + case protocol.Version1: + return b[0]>>4&0b11 == 0b01 + case protocol.Version2: + return b[0]>>4&0b11 == 0b10 + default: + return false + } +} + +var ErrUnsupportedVersion = errors.New("unsupported version") + +// The Header is the version independent part of the header +type Header struct { + typeByte byte + Type protocol.PacketType + + Version protocol.Version + SrcConnectionID protocol.ConnectionID + DestConnectionID protocol.ConnectionID + + Length protocol.ByteCount + + Token []byte + + parsedLen protocol.ByteCount // how many bytes were read while parsing this header +} + +// ParsePacket parses a packet. +// If the packet has a long header, the packet is cut according to the length field. +// If we understand the version, the packet is header up unto the packet number. +// Otherwise, only the invariant part of the header is parsed. +func ParsePacket(data []byte) (*Header, []byte, []byte, error) { + if len(data) == 0 || !IsLongHeaderPacket(data[0]) { + return nil, nil, nil, errors.New("not a long header packet") + } + hdr, err := parseHeader(bytes.NewReader(data)) + if err != nil { + if err == ErrUnsupportedVersion { + return hdr, nil, nil, ErrUnsupportedVersion + } + return nil, nil, nil, err + } + if protocol.ByteCount(len(data)) < hdr.ParsedLen()+hdr.Length { + return nil, nil, nil, fmt.Errorf("packet length (%d bytes) is smaller than the expected length (%d bytes)", len(data)-int(hdr.ParsedLen()), hdr.Length) + } + packetLen := int(hdr.ParsedLen() + hdr.Length) + return hdr, data[:packetLen], data[packetLen:], nil +} + +// ParseHeader parses the header. +// For short header packets: up to the packet number. +// For long header packets: +// * if we understand the version: up to the packet number +// * if not, only the invariant part of the header +func parseHeader(b *bytes.Reader) (*Header, error) { + startLen := b.Len() + typeByte, err := b.ReadByte() + if err != nil { + return nil, err + } + + h := &Header{typeByte: typeByte} + err = h.parseLongHeader(b) + h.parsedLen = protocol.ByteCount(startLen - b.Len()) + return h, err +} + +func (h *Header) parseLongHeader(b *bytes.Reader) error { + v, err := utils.BigEndian.ReadUint32(b) + if err != nil { + return err + } + h.Version = protocol.Version(v) + if h.Version != 0 && h.typeByte&0x40 == 0 { + return errors.New("not a QUIC packet") + } + destConnIDLen, err := b.ReadByte() + if err != nil { + return err + } + h.DestConnectionID, err = protocol.ReadConnectionID(b, int(destConnIDLen)) + if err != nil { + return err + } + srcConnIDLen, err := b.ReadByte() + if err != nil { + return err + } + h.SrcConnectionID, err = protocol.ReadConnectionID(b, int(srcConnIDLen)) + if err != nil { + return err + } + if h.Version == 0 { // version negotiation packet + return nil + } + // If we don't understand the version, we have no idea how to interpret the rest of the bytes + if !protocol.IsSupportedVersion(protocol.SupportedVersions, h.Version) { + return ErrUnsupportedVersion + } + + if h.Version == protocol.Version2 { + switch h.typeByte >> 4 & 0b11 { + case 0b00: + h.Type = protocol.PacketTypeRetry + case 0b01: + h.Type = protocol.PacketTypeInitial + case 0b10: + h.Type = protocol.PacketType0RTT + case 0b11: + h.Type = protocol.PacketTypeHandshake + } + } else { + switch h.typeByte >> 4 & 0b11 { + case 0b00: + h.Type = protocol.PacketTypeInitial + case 0b01: + h.Type = protocol.PacketType0RTT + case 0b10: + h.Type = protocol.PacketTypeHandshake + case 0b11: + h.Type = protocol.PacketTypeRetry + } + } + + if h.Type == protocol.PacketTypeRetry { + tokenLen := b.Len() - 16 + if tokenLen <= 0 { + return io.EOF + } + h.Token = make([]byte, tokenLen) + if _, err := io.ReadFull(b, h.Token); err != nil { + return err + } + _, err := b.Seek(16, io.SeekCurrent) + return err + } + + if h.Type == protocol.PacketTypeInitial { + tokenLen, err := quicvarint.Read(b) + if err != nil { + return err + } + if tokenLen > uint64(b.Len()) { + return io.EOF + } + h.Token = make([]byte, tokenLen) + if _, err := io.ReadFull(b, h.Token); err != nil { + return err + } + } + + pl, err := quicvarint.Read(b) + if err != nil { + return err + } + h.Length = protocol.ByteCount(pl) + return nil +} + +// ParsedLen returns the number of bytes that were consumed when parsing the header +func (h *Header) ParsedLen() protocol.ByteCount { + return h.parsedLen +} + +// ParseExtended parses the version dependent part of the header. +// The Reader has to be set such that it points to the first byte of the header. +func (h *Header) ParseExtended(b *bytes.Reader, ver protocol.Version) (*ExtendedHeader, error) { + extHdr := h.toExtendedHeader() + reservedBitsValid, err := extHdr.parse(b, ver) + if err != nil { + return nil, err + } + if !reservedBitsValid { + return extHdr, ErrInvalidReservedBits + } + return extHdr, nil +} + +func (h *Header) toExtendedHeader() *ExtendedHeader { + return &ExtendedHeader{Header: *h} +} + +// PacketType is the type of the packet, for logging purposes +func (h *Header) PacketType() string { + return h.Type.String() +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/interface.go b/vendor/github.com/quic-go/quic-go/internal/wire/interface.go new file mode 100644 index 000000000..bc17883b5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/interface.go @@ -0,0 +1,11 @@ +package wire + +import ( + "github.com/quic-go/quic-go/internal/protocol" +) + +// A Frame in QUIC +type Frame interface { + Append(b []byte, version protocol.Version) ([]byte, error) + Length(version protocol.Version) protocol.ByteCount +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/log.go b/vendor/github.com/quic-go/quic-go/internal/wire/log.go new file mode 100644 index 000000000..c8b28d924 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/log.go @@ -0,0 +1,74 @@ +package wire + +import ( + "fmt" + "strings" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +// LogFrame logs a frame, either sent or received +func LogFrame(logger utils.Logger, frame Frame, sent bool) { + if !logger.Debug() { + return + } + dir := "<-" + if sent { + dir = "->" + } + switch f := frame.(type) { + case *CryptoFrame: + dataLen := protocol.ByteCount(len(f.Data)) + logger.Debugf("\t%s &wire.CryptoFrame{Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.Offset, dataLen, f.Offset+dataLen) + case *StreamFrame: + logger.Debugf("\t%s &wire.StreamFrame{StreamID: %d, Fin: %t, Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.StreamID, f.Fin, f.Offset, f.DataLen(), f.Offset+f.DataLen()) + case *ResetStreamFrame: + logger.Debugf("\t%s &wire.ResetStreamFrame{StreamID: %d, ErrorCode: %#x, FinalSize: %d}", dir, f.StreamID, f.ErrorCode, f.FinalSize) + case *AckFrame: + hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 + var ecn string + if hasECN { + ecn = fmt.Sprintf(", ECT0: %d, ECT1: %d, CE: %d", f.ECT0, f.ECT1, f.ECNCE) + } + if len(f.AckRanges) > 1 { + ackRanges := make([]string, len(f.AckRanges)) + for i, r := range f.AckRanges { + ackRanges[i] = fmt.Sprintf("{Largest: %d, Smallest: %d}", r.Largest, r.Smallest) + } + logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, AckRanges: {%s}, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), strings.Join(ackRanges, ", "), f.DelayTime.String(), ecn) + } else { + logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), f.DelayTime.String(), ecn) + } + case *MaxDataFrame: + logger.Debugf("\t%s &wire.MaxDataFrame{MaximumData: %d}", dir, f.MaximumData) + case *MaxStreamDataFrame: + logger.Debugf("\t%s &wire.MaxStreamDataFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData) + case *DataBlockedFrame: + logger.Debugf("\t%s &wire.DataBlockedFrame{MaximumData: %d}", dir, f.MaximumData) + case *StreamDataBlockedFrame: + logger.Debugf("\t%s &wire.StreamDataBlockedFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData) + case *MaxStreamsFrame: + switch f.Type { + case protocol.StreamTypeUni: + logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: uni, MaxStreamNum: %d}", dir, f.MaxStreamNum) + case protocol.StreamTypeBidi: + logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: bidi, MaxStreamNum: %d}", dir, f.MaxStreamNum) + } + case *StreamsBlockedFrame: + switch f.Type { + case protocol.StreamTypeUni: + logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: uni, MaxStreams: %d}", dir, f.StreamLimit) + case protocol.StreamTypeBidi: + logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: bidi, MaxStreams: %d}", dir, f.StreamLimit) + } + case *NewConnectionIDFrame: + logger.Debugf("\t%s &wire.NewConnectionIDFrame{SequenceNumber: %d, RetirePriorTo: %d, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.RetirePriorTo, f.ConnectionID, f.StatelessResetToken) + case *RetireConnectionIDFrame: + logger.Debugf("\t%s &wire.RetireConnectionIDFrame{SequenceNumber: %d}", dir, f.SequenceNumber) + case *NewTokenFrame: + logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token) + default: + logger.Debugf("\t%s %#v", dir, frame) + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go new file mode 100644 index 000000000..3dfd76116 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go @@ -0,0 +1,35 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A MaxDataFrame carries flow control information for the connection +type MaxDataFrame struct { + MaximumData protocol.ByteCount +} + +// parseMaxDataFrame parses a MAX_DATA frame +func parseMaxDataFrame(r *bytes.Reader, _ protocol.Version) (*MaxDataFrame, error) { + frame := &MaxDataFrame{} + byteOffset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.MaximumData = protocol.ByteCount(byteOffset) + return frame, nil +} + +func (f *MaxDataFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, maxDataFrameType) + b = quicvarint.Append(b, uint64(f.MaximumData)) + return b, nil +} + +// Length of a written frame +func (f *MaxDataFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.MaximumData)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go new file mode 100644 index 000000000..cb5eab1b0 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go @@ -0,0 +1,42 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A MaxStreamDataFrame is a MAX_STREAM_DATA frame +type MaxStreamDataFrame struct { + StreamID protocol.StreamID + MaximumStreamData protocol.ByteCount +} + +func parseMaxStreamDataFrame(r *bytes.Reader, _ protocol.Version) (*MaxStreamDataFrame, error) { + sid, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + offset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + + return &MaxStreamDataFrame{ + StreamID: protocol.StreamID(sid), + MaximumStreamData: protocol.ByteCount(offset), + }, nil +} + +func (f *MaxStreamDataFrame) Append(b []byte, version protocol.Version) ([]byte, error) { + b = append(b, maxStreamDataFrameType) + b = quicvarint.Append(b, uint64(f.StreamID)) + b = quicvarint.Append(b, uint64(f.MaximumStreamData)) + return b, nil +} + +// Length of a written frame +func (f *MaxStreamDataFrame) Length(version protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go new file mode 100644 index 000000000..d90293383 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go @@ -0,0 +1,50 @@ +package wire + +import ( + "bytes" + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A MaxStreamsFrame is a MAX_STREAMS frame +type MaxStreamsFrame struct { + Type protocol.StreamType + MaxStreamNum protocol.StreamNum +} + +func parseMaxStreamsFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*MaxStreamsFrame, error) { + f := &MaxStreamsFrame{} + switch typ { + case bidiMaxStreamsFrameType: + f.Type = protocol.StreamTypeBidi + case uniMaxStreamsFrameType: + f.Type = protocol.StreamTypeUni + } + streamID, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + f.MaxStreamNum = protocol.StreamNum(streamID) + if f.MaxStreamNum > protocol.MaxStreamCount { + return nil, fmt.Errorf("%d exceeds the maximum stream count", f.MaxStreamNum) + } + return f, nil +} + +func (f *MaxStreamsFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + switch f.Type { + case protocol.StreamTypeBidi: + b = append(b, bidiMaxStreamsFrameType) + case protocol.StreamTypeUni: + b = append(b, uniMaxStreamsFrameType) + } + b = quicvarint.Append(b, uint64(f.MaxStreamNum)) + return b, nil +} + +// Length of a written frame +func (f *MaxStreamsFrame) Length(protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.MaxStreamNum)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go new file mode 100644 index 000000000..afae010ad --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go @@ -0,0 +1,77 @@ +package wire + +import ( + "bytes" + "errors" + "fmt" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A NewConnectionIDFrame is a NEW_CONNECTION_ID frame +type NewConnectionIDFrame struct { + SequenceNumber uint64 + RetirePriorTo uint64 + ConnectionID protocol.ConnectionID + StatelessResetToken protocol.StatelessResetToken +} + +func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.Version) (*NewConnectionIDFrame, error) { + seq, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + ret, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + if ret > seq { + //nolint:stylecheck + return nil, fmt.Errorf("Retire Prior To value (%d) larger than Sequence Number (%d)", ret, seq) + } + connIDLen, err := r.ReadByte() + if err != nil { + return nil, err + } + if connIDLen == 0 { + return nil, errors.New("invalid zero-length connection ID") + } + connID, err := protocol.ReadConnectionID(r, int(connIDLen)) + if err != nil { + return nil, err + } + frame := &NewConnectionIDFrame{ + SequenceNumber: seq, + RetirePriorTo: ret, + ConnectionID: connID, + } + if _, err := io.ReadFull(r, frame.StatelessResetToken[:]); err != nil { + if err == io.ErrUnexpectedEOF { + return nil, io.EOF + } + return nil, err + } + + return frame, nil +} + +func (f *NewConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, newConnectionIDFrameType) + b = quicvarint.Append(b, f.SequenceNumber) + b = quicvarint.Append(b, f.RetirePriorTo) + connIDLen := f.ConnectionID.Len() + if connIDLen > protocol.MaxConnIDLen { + return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen) + } + b = append(b, uint8(connIDLen)) + b = append(b, f.ConnectionID.Bytes()...) + b = append(b, f.StatelessResetToken[:]...) + return b, nil +} + +// Length of a written frame +func (f *NewConnectionIDFrame) Length(protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(f.SequenceNumber) + quicvarint.Len(f.RetirePriorTo) + 1 /* connection ID length */ + protocol.ByteCount(f.ConnectionID.Len()) + 16 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go new file mode 100644 index 000000000..6a2eac945 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go @@ -0,0 +1,45 @@ +package wire + +import ( + "bytes" + "errors" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A NewTokenFrame is a NEW_TOKEN frame +type NewTokenFrame struct { + Token []byte +} + +func parseNewTokenFrame(r *bytes.Reader, _ protocol.Version) (*NewTokenFrame, error) { + tokenLen, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + if uint64(r.Len()) < tokenLen { + return nil, io.EOF + } + if tokenLen == 0 { + return nil, errors.New("token must not be empty") + } + token := make([]byte, int(tokenLen)) + if _, err := io.ReadFull(r, token); err != nil { + return nil, err + } + return &NewTokenFrame{Token: token}, nil +} + +func (f *NewTokenFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, newTokenFrameType) + b = quicvarint.Append(b, uint64(len(f.Token))) + b = append(b, f.Token...) + return b, nil +} + +// Length of a written frame +func (f *NewTokenFrame) Length(protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(len(f.Token))) + protocol.ByteCount(len(f.Token)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go new file mode 100644 index 000000000..772041ac6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go @@ -0,0 +1,35 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// A PathChallengeFrame is a PATH_CHALLENGE frame +type PathChallengeFrame struct { + Data [8]byte +} + +func parsePathChallengeFrame(r *bytes.Reader, _ protocol.Version) (*PathChallengeFrame, error) { + frame := &PathChallengeFrame{} + if _, err := io.ReadFull(r, frame.Data[:]); err != nil { + if err == io.ErrUnexpectedEOF { + return nil, io.EOF + } + return nil, err + } + return frame, nil +} + +func (f *PathChallengeFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, pathChallengeFrameType) + b = append(b, f.Data[:]...) + return b, nil +} + +// Length of a written frame +func (f *PathChallengeFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + 8 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go new file mode 100644 index 000000000..86bbe619f --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go @@ -0,0 +1,35 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// A PathResponseFrame is a PATH_RESPONSE frame +type PathResponseFrame struct { + Data [8]byte +} + +func parsePathResponseFrame(r *bytes.Reader, _ protocol.Version) (*PathResponseFrame, error) { + frame := &PathResponseFrame{} + if _, err := io.ReadFull(r, frame.Data[:]); err != nil { + if err == io.ErrUnexpectedEOF { + return nil, io.EOF + } + return nil, err + } + return frame, nil +} + +func (f *PathResponseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, pathResponseFrameType) + b = append(b, f.Data[:]...) + return b, nil +} + +// Length of a written frame +func (f *PathResponseFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + 8 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go new file mode 100644 index 000000000..71f8d16c3 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go @@ -0,0 +1,17 @@ +package wire + +import ( + "github.com/quic-go/quic-go/internal/protocol" +) + +// A PingFrame is a PING frame +type PingFrame struct{} + +func (f *PingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + return append(b, pingFrameType), nil +} + +// Length of a written frame +func (f *PingFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/pool.go b/vendor/github.com/quic-go/quic-go/internal/wire/pool.go new file mode 100644 index 000000000..18ab43793 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/pool.go @@ -0,0 +1,33 @@ +package wire + +import ( + "sync" + + "github.com/quic-go/quic-go/internal/protocol" +) + +var pool sync.Pool + +func init() { + pool.New = func() interface{} { + return &StreamFrame{ + Data: make([]byte, 0, protocol.MaxPacketBufferSize), + fromPool: true, + } + } +} + +func GetStreamFrame() *StreamFrame { + f := pool.Get().(*StreamFrame) + return f +} + +func putStreamFrame(f *StreamFrame) { + if !f.fromPool { + return + } + if protocol.ByteCount(cap(f.Data)) != protocol.MaxPacketBufferSize { + panic("wire.PutStreamFrame called with packet of wrong size!") + } + pool.Put(f) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go new file mode 100644 index 000000000..e60f1db12 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go @@ -0,0 +1,54 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/quicvarint" +) + +// A ResetStreamFrame is a RESET_STREAM frame in QUIC +type ResetStreamFrame struct { + StreamID protocol.StreamID + ErrorCode qerr.StreamErrorCode + FinalSize protocol.ByteCount +} + +func parseResetStreamFrame(r *bytes.Reader, _ protocol.Version) (*ResetStreamFrame, error) { + var streamID protocol.StreamID + var byteOffset protocol.ByteCount + sid, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + streamID = protocol.StreamID(sid) + errorCode, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + bo, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + byteOffset = protocol.ByteCount(bo) + + return &ResetStreamFrame{ + StreamID: streamID, + ErrorCode: qerr.StreamErrorCode(errorCode), + FinalSize: byteOffset, + }, nil +} + +func (f *ResetStreamFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, resetStreamFrameType) + b = quicvarint.Append(b, uint64(f.StreamID)) + b = quicvarint.Append(b, uint64(f.ErrorCode)) + b = quicvarint.Append(b, uint64(f.FinalSize)) + return b, nil +} + +// Length of a written frame +func (f *ResetStreamFrame) Length(version protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(f.FinalSize)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go new file mode 100644 index 000000000..981536224 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go @@ -0,0 +1,32 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame +type RetireConnectionIDFrame struct { + SequenceNumber uint64 +} + +func parseRetireConnectionIDFrame(r *bytes.Reader, _ protocol.Version) (*RetireConnectionIDFrame, error) { + seq, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + return &RetireConnectionIDFrame{SequenceNumber: seq}, nil +} + +func (f *RetireConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, retireConnectionIDFrameType) + b = quicvarint.Append(b, f.SequenceNumber) + return b, nil +} + +// Length of a written frame +func (f *RetireConnectionIDFrame) Length(protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(f.SequenceNumber) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go b/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go new file mode 100644 index 000000000..69aa83411 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go @@ -0,0 +1,73 @@ +package wire + +import ( + "errors" + "fmt" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +// ParseShortHeader parses a short header packet. +// It must be called after header protection was removed. +// Otherwise, the check for the reserved bits will (most likely) fail. +func ParseShortHeader(data []byte, connIDLen int) (length int, _ protocol.PacketNumber, _ protocol.PacketNumberLen, _ protocol.KeyPhaseBit, _ error) { + if len(data) == 0 { + return 0, 0, 0, 0, io.EOF + } + if data[0]&0x80 > 0 { + return 0, 0, 0, 0, errors.New("not a short header packet") + } + if data[0]&0x40 == 0 { + return 0, 0, 0, 0, errors.New("not a QUIC packet") + } + pnLen := protocol.PacketNumberLen(data[0]&0b11) + 1 + if len(data) < 1+int(pnLen)+connIDLen { + return 0, 0, 0, 0, io.EOF + } + + pos := 1 + connIDLen + var pn protocol.PacketNumber + switch pnLen { + case protocol.PacketNumberLen1: + pn = protocol.PacketNumber(data[pos]) + case protocol.PacketNumberLen2: + pn = protocol.PacketNumber(utils.BigEndian.Uint16(data[pos : pos+2])) + case protocol.PacketNumberLen3: + pn = protocol.PacketNumber(utils.BigEndian.Uint24(data[pos : pos+3])) + case protocol.PacketNumberLen4: + pn = protocol.PacketNumber(utils.BigEndian.Uint32(data[pos : pos+4])) + default: + return 0, 0, 0, 0, fmt.Errorf("invalid packet number length: %d", pnLen) + } + kp := protocol.KeyPhaseZero + if data[0]&0b100 > 0 { + kp = protocol.KeyPhaseOne + } + + var err error + if data[0]&0x18 != 0 { + err = ErrInvalidReservedBits + } + return 1 + connIDLen + int(pnLen), pn, pnLen, kp, err +} + +// AppendShortHeader writes a short header. +func AppendShortHeader(b []byte, connID protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) ([]byte, error) { + typeByte := 0x40 | uint8(pnLen-1) + if kp == protocol.KeyPhaseOne { + typeByte |= byte(1 << 2) + } + b = append(b, typeByte) + b = append(b, connID.Bytes()...) + return appendPacketNumber(b, pn, pnLen) +} + +func ShortHeaderLen(dest protocol.ConnectionID, pnLen protocol.PacketNumberLen) protocol.ByteCount { + return 1 + protocol.ByteCount(dest.Len()) + protocol.ByteCount(pnLen) +} + +func LogShortHeader(logger utils.Logger, dest protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) { + logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %d, PacketNumberLen: %d, KeyPhase: %s}", dest, pn, pnLen, kp) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go new file mode 100644 index 000000000..d314a5698 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go @@ -0,0 +1,44 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/quicvarint" +) + +// A StopSendingFrame is a STOP_SENDING frame +type StopSendingFrame struct { + StreamID protocol.StreamID + ErrorCode qerr.StreamErrorCode +} + +// parseStopSendingFrame parses a STOP_SENDING frame +func parseStopSendingFrame(r *bytes.Reader, _ protocol.Version) (*StopSendingFrame, error) { + streamID, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + errorCode, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + + return &StopSendingFrame{ + StreamID: protocol.StreamID(streamID), + ErrorCode: qerr.StreamErrorCode(errorCode), + }, nil +} + +// Length of a written frame +func (f *StopSendingFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) +} + +func (f *StopSendingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, stopSendingFrameType) + b = quicvarint.Append(b, uint64(f.StreamID)) + b = quicvarint.Append(b, uint64(f.ErrorCode)) + return b, nil +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go new file mode 100644 index 000000000..f79740f98 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go @@ -0,0 +1,42 @@ +package wire + +import ( + "bytes" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame +type StreamDataBlockedFrame struct { + StreamID protocol.StreamID + MaximumStreamData protocol.ByteCount +} + +func parseStreamDataBlockedFrame(r *bytes.Reader, _ protocol.Version) (*StreamDataBlockedFrame, error) { + sid, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + offset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + + return &StreamDataBlockedFrame{ + StreamID: protocol.StreamID(sid), + MaximumStreamData: protocol.ByteCount(offset), + }, nil +} + +func (f *StreamDataBlockedFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = append(b, 0x15) + b = quicvarint.Append(b, uint64(f.StreamID)) + b = quicvarint.Append(b, uint64(f.MaximumStreamData)) + return b, nil +} + +// Length of a written frame +func (f *StreamDataBlockedFrame) Length(version protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go new file mode 100644 index 000000000..0f6c00da2 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go @@ -0,0 +1,184 @@ +package wire + +import ( + "bytes" + "errors" + "io" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A StreamFrame of QUIC +type StreamFrame struct { + StreamID protocol.StreamID + Offset protocol.ByteCount + Data []byte + Fin bool + DataLenPresent bool + + fromPool bool +} + +func parseStreamFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*StreamFrame, error) { + hasOffset := typ&0b100 > 0 + fin := typ&0b1 > 0 + hasDataLen := typ&0b10 > 0 + + streamID, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + var offset uint64 + if hasOffset { + offset, err = quicvarint.Read(r) + if err != nil { + return nil, err + } + } + + var dataLen uint64 + if hasDataLen { + var err error + dataLen, err = quicvarint.Read(r) + if err != nil { + return nil, err + } + } else { + // The rest of the packet is data + dataLen = uint64(r.Len()) + } + + var frame *StreamFrame + if dataLen < protocol.MinStreamFrameBufferSize { + frame = &StreamFrame{Data: make([]byte, dataLen)} + } else { + frame = GetStreamFrame() + // The STREAM frame can't be larger than the StreamFrame we obtained from the buffer, + // since those StreamFrames have a buffer length of the maximum packet size. + if dataLen > uint64(cap(frame.Data)) { + return nil, io.EOF + } + frame.Data = frame.Data[:dataLen] + } + + frame.StreamID = protocol.StreamID(streamID) + frame.Offset = protocol.ByteCount(offset) + frame.Fin = fin + frame.DataLenPresent = hasDataLen + + if dataLen != 0 { + if _, err := io.ReadFull(r, frame.Data); err != nil { + return nil, err + } + } + if frame.Offset+frame.DataLen() > protocol.MaxByteCount { + return nil, errors.New("stream data overflows maximum offset") + } + return frame, nil +} + +// Write writes a STREAM frame +func (f *StreamFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + if len(f.Data) == 0 && !f.Fin { + return nil, errors.New("StreamFrame: attempting to write empty frame without FIN") + } + + typ := byte(0x8) + if f.Fin { + typ ^= 0b1 + } + hasOffset := f.Offset != 0 + if f.DataLenPresent { + typ ^= 0b10 + } + if hasOffset { + typ ^= 0b100 + } + b = append(b, typ) + b = quicvarint.Append(b, uint64(f.StreamID)) + if hasOffset { + b = quicvarint.Append(b, uint64(f.Offset)) + } + if f.DataLenPresent { + b = quicvarint.Append(b, uint64(f.DataLen())) + } + b = append(b, f.Data...) + return b, nil +} + +// Length returns the total length of the STREAM frame +func (f *StreamFrame) Length(version protocol.Version) protocol.ByteCount { + length := 1 + quicvarint.Len(uint64(f.StreamID)) + if f.Offset != 0 { + length += quicvarint.Len(uint64(f.Offset)) + } + if f.DataLenPresent { + length += quicvarint.Len(uint64(f.DataLen())) + } + return length + f.DataLen() +} + +// DataLen gives the length of data in bytes +func (f *StreamFrame) DataLen() protocol.ByteCount { + return protocol.ByteCount(len(f.Data)) +} + +// MaxDataLen returns the maximum data length +// If 0 is returned, writing will fail (a STREAM frame must contain at least 1 byte of data). +func (f *StreamFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.Version) protocol.ByteCount { + headerLen := 1 + quicvarint.Len(uint64(f.StreamID)) + if f.Offset != 0 { + headerLen += quicvarint.Len(uint64(f.Offset)) + } + if f.DataLenPresent { + // pretend that the data size will be 1 bytes + // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards + headerLen++ + } + if headerLen > maxSize { + return 0 + } + maxDataLen := maxSize - headerLen + if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 { + maxDataLen-- + } + return maxDataLen +} + +// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. +// It returns if the frame was actually split. +// The frame might not be split if: +// * the size is large enough to fit the whole frame +// * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil. +func (f *StreamFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.Version) (*StreamFrame, bool /* was splitting required */) { + if maxSize >= f.Length(version) { + return nil, false + } + + n := f.MaxDataLen(maxSize, version) + if n == 0 { + return nil, true + } + + new := GetStreamFrame() + new.StreamID = f.StreamID + new.Offset = f.Offset + new.Fin = false + new.DataLenPresent = f.DataLenPresent + + // swap the data slices + new.Data, f.Data = f.Data, new.Data + new.fromPool, f.fromPool = f.fromPool, new.fromPool + + f.Data = f.Data[:protocol.ByteCount(len(new.Data))-n] + copy(f.Data, new.Data[n:]) + new.Data = new.Data[:n] + f.Offset += n + + return new, true +} + +func (f *StreamFrame) PutBack() { + putStreamFrame(f) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go new file mode 100644 index 000000000..b24619ab0 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go @@ -0,0 +1,50 @@ +package wire + +import ( + "bytes" + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// A StreamsBlockedFrame is a STREAMS_BLOCKED frame +type StreamsBlockedFrame struct { + Type protocol.StreamType + StreamLimit protocol.StreamNum +} + +func parseStreamsBlockedFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*StreamsBlockedFrame, error) { + f := &StreamsBlockedFrame{} + switch typ { + case bidiStreamBlockedFrameType: + f.Type = protocol.StreamTypeBidi + case uniStreamBlockedFrameType: + f.Type = protocol.StreamTypeUni + } + streamLimit, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + f.StreamLimit = protocol.StreamNum(streamLimit) + if f.StreamLimit > protocol.MaxStreamCount { + return nil, fmt.Errorf("%d exceeds the maximum stream count", f.StreamLimit) + } + return f, nil +} + +func (f *StreamsBlockedFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + switch f.Type { + case protocol.StreamTypeBidi: + b = append(b, bidiStreamBlockedFrameType) + case protocol.StreamTypeUni: + b = append(b, uniStreamBlockedFrameType) + } + b = quicvarint.Append(b, uint64(f.StreamLimit)) + return b, nil +} + +// Length of a written frame +func (f *StreamsBlockedFrame) Length(_ protocol.Version) protocol.ByteCount { + return 1 + quicvarint.Len(uint64(f.StreamLimit)) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go new file mode 100644 index 000000000..c03be3cd7 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go @@ -0,0 +1,520 @@ +package wire + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "net/netip" + "sort" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/quicvarint" +) + +// AdditionalTransportParametersClient are additional transport parameters that will be added +// to the client's transport parameters. +// This is not intended for production use, but _only_ to increase the size of the ClientHello beyond +// the usual size of less than 1 MTU. +var AdditionalTransportParametersClient map[uint64][]byte + +const transportParameterMarshalingVersion = 1 + +type transportParameterID uint64 + +const ( + originalDestinationConnectionIDParameterID transportParameterID = 0x0 + maxIdleTimeoutParameterID transportParameterID = 0x1 + statelessResetTokenParameterID transportParameterID = 0x2 + maxUDPPayloadSizeParameterID transportParameterID = 0x3 + initialMaxDataParameterID transportParameterID = 0x4 + initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5 + initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6 + initialMaxStreamDataUniParameterID transportParameterID = 0x7 + initialMaxStreamsBidiParameterID transportParameterID = 0x8 + initialMaxStreamsUniParameterID transportParameterID = 0x9 + ackDelayExponentParameterID transportParameterID = 0xa + maxAckDelayParameterID transportParameterID = 0xb + disableActiveMigrationParameterID transportParameterID = 0xc + preferredAddressParameterID transportParameterID = 0xd + activeConnectionIDLimitParameterID transportParameterID = 0xe + initialSourceConnectionIDParameterID transportParameterID = 0xf + retrySourceConnectionIDParameterID transportParameterID = 0x10 + // RFC 9221 + maxDatagramFrameSizeParameterID transportParameterID = 0x20 +) + +// PreferredAddress is the value encoding in the preferred_address transport parameter +type PreferredAddress struct { + IPv4, IPv6 netip.AddrPort + ConnectionID protocol.ConnectionID + StatelessResetToken protocol.StatelessResetToken +} + +// TransportParameters are parameters sent to the peer during the handshake +type TransportParameters struct { + InitialMaxStreamDataBidiLocal protocol.ByteCount + InitialMaxStreamDataBidiRemote protocol.ByteCount + InitialMaxStreamDataUni protocol.ByteCount + InitialMaxData protocol.ByteCount + + MaxAckDelay time.Duration + AckDelayExponent uint8 + + DisableActiveMigration bool + + MaxUDPPayloadSize protocol.ByteCount + + MaxUniStreamNum protocol.StreamNum + MaxBidiStreamNum protocol.StreamNum + + MaxIdleTimeout time.Duration + + PreferredAddress *PreferredAddress + + OriginalDestinationConnectionID protocol.ConnectionID + InitialSourceConnectionID protocol.ConnectionID + RetrySourceConnectionID *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters + + StatelessResetToken *protocol.StatelessResetToken + ActiveConnectionIDLimit uint64 + + MaxDatagramFrameSize protocol.ByteCount +} + +// Unmarshal the transport parameters +func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error { + if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil { + return &qerr.TransportError{ + ErrorCode: qerr.TransportParameterError, + ErrorMessage: err.Error(), + } + } + return nil +} + +func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspective, fromSessionTicket bool) error { + // needed to check that every parameter is only sent at most once + var parameterIDs []transportParameterID + + var ( + readOriginalDestinationConnectionID bool + readInitialSourceConnectionID bool + readActiveConnectionIDLimit bool + ) + + p.AckDelayExponent = protocol.DefaultAckDelayExponent + p.MaxAckDelay = protocol.DefaultMaxAckDelay + p.MaxDatagramFrameSize = protocol.InvalidByteCount + + for r.Len() > 0 { + paramIDInt, err := quicvarint.Read(r) + if err != nil { + return err + } + paramID := transportParameterID(paramIDInt) + paramLen, err := quicvarint.Read(r) + if err != nil { + return err + } + if uint64(r.Len()) < paramLen { + return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen) + } + parameterIDs = append(parameterIDs, paramID) + switch paramID { + case activeConnectionIDLimitParameterID: + readActiveConnectionIDLimit = true + fallthrough + case maxIdleTimeoutParameterID, + maxUDPPayloadSizeParameterID, + initialMaxDataParameterID, + initialMaxStreamDataBidiLocalParameterID, + initialMaxStreamDataBidiRemoteParameterID, + initialMaxStreamDataUniParameterID, + initialMaxStreamsBidiParameterID, + initialMaxStreamsUniParameterID, + maxAckDelayParameterID, + maxDatagramFrameSizeParameterID, + ackDelayExponentParameterID: + if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil { + return err + } + case preferredAddressParameterID: + if sentBy == protocol.PerspectiveClient { + return errors.New("client sent a preferred_address") + } + if err := p.readPreferredAddress(r, int(paramLen)); err != nil { + return err + } + case disableActiveMigrationParameterID: + if paramLen != 0 { + return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", paramLen) + } + p.DisableActiveMigration = true + case statelessResetTokenParameterID: + if sentBy == protocol.PerspectiveClient { + return errors.New("client sent a stateless_reset_token") + } + if paramLen != 16 { + return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen) + } + var token protocol.StatelessResetToken + r.Read(token[:]) + p.StatelessResetToken = &token + case originalDestinationConnectionIDParameterID: + if sentBy == protocol.PerspectiveClient { + return errors.New("client sent an original_destination_connection_id") + } + p.OriginalDestinationConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) + readOriginalDestinationConnectionID = true + case initialSourceConnectionIDParameterID: + p.InitialSourceConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) + readInitialSourceConnectionID = true + case retrySourceConnectionIDParameterID: + if sentBy == protocol.PerspectiveClient { + return errors.New("client sent a retry_source_connection_id") + } + connID, _ := protocol.ReadConnectionID(r, int(paramLen)) + p.RetrySourceConnectionID = &connID + default: + r.Seek(int64(paramLen), io.SeekCurrent) + } + } + + if !readActiveConnectionIDLimit { + p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit + } + if !fromSessionTicket { + if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { + return errors.New("missing original_destination_connection_id") + } + if p.MaxUDPPayloadSize == 0 { + p.MaxUDPPayloadSize = protocol.MaxByteCount + } + if !readInitialSourceConnectionID { + return errors.New("missing initial_source_connection_id") + } + } + + // check that every transport parameter was sent at most once + sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] }) + for i := 0; i < len(parameterIDs)-1; i++ { + if parameterIDs[i] == parameterIDs[i+1] { + return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i]) + } + } + + return nil +} + +func (p *TransportParameters) readPreferredAddress(r *bytes.Reader, expectedLen int) error { + remainingLen := r.Len() + pa := &PreferredAddress{} + var ipv4 [4]byte + if _, err := io.ReadFull(r, ipv4[:]); err != nil { + return err + } + port, err := utils.BigEndian.ReadUint16(r) + if err != nil { + return err + } + pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port) + var ipv6 [16]byte + if _, err := io.ReadFull(r, ipv6[:]); err != nil { + return err + } + port, err = utils.BigEndian.ReadUint16(r) + if err != nil { + return err + } + pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port) + connIDLen, err := r.ReadByte() + if err != nil { + return err + } + if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen { + return fmt.Errorf("invalid connection ID length: %d", connIDLen) + } + connID, err := protocol.ReadConnectionID(r, int(connIDLen)) + if err != nil { + return err + } + pa.ConnectionID = connID + if _, err := io.ReadFull(r, pa.StatelessResetToken[:]); err != nil { + return err + } + if bytesRead := remainingLen - r.Len(); bytesRead != expectedLen { + return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", expectedLen, bytesRead) + } + p.PreferredAddress = pa + return nil +} + +func (p *TransportParameters) readNumericTransportParameter( + r *bytes.Reader, + paramID transportParameterID, + expectedLen int, +) error { + remainingLen := r.Len() + val, err := quicvarint.Read(r) + if err != nil { + return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) + } + if remainingLen-r.Len() != expectedLen { + return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID) + } + //nolint:exhaustive // This only covers the numeric transport parameters. + switch paramID { + case initialMaxStreamDataBidiLocalParameterID: + p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val) + case initialMaxStreamDataBidiRemoteParameterID: + p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val) + case initialMaxStreamDataUniParameterID: + p.InitialMaxStreamDataUni = protocol.ByteCount(val) + case initialMaxDataParameterID: + p.InitialMaxData = protocol.ByteCount(val) + case initialMaxStreamsBidiParameterID: + p.MaxBidiStreamNum = protocol.StreamNum(val) + if p.MaxBidiStreamNum > protocol.MaxStreamCount { + return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", p.MaxBidiStreamNum, protocol.MaxStreamCount) + } + case initialMaxStreamsUniParameterID: + p.MaxUniStreamNum = protocol.StreamNum(val) + if p.MaxUniStreamNum > protocol.MaxStreamCount { + return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", p.MaxUniStreamNum, protocol.MaxStreamCount) + } + case maxIdleTimeoutParameterID: + p.MaxIdleTimeout = max(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) + case maxUDPPayloadSizeParameterID: + if val < 1200 { + return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val) + } + p.MaxUDPPayloadSize = protocol.ByteCount(val) + case ackDelayExponentParameterID: + if val > protocol.MaxAckDelayExponent { + return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent) + } + p.AckDelayExponent = uint8(val) + case maxAckDelayParameterID: + if val > uint64(protocol.MaxMaxAckDelay/time.Millisecond) { + return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", val, protocol.MaxMaxAckDelay/time.Millisecond) + } + p.MaxAckDelay = time.Duration(val) * time.Millisecond + case activeConnectionIDLimitParameterID: + if val < 2 { + return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", val) + } + p.ActiveConnectionIDLimit = val + case maxDatagramFrameSizeParameterID: + p.MaxDatagramFrameSize = protocol.ByteCount(val) + default: + return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) + } + return nil +} + +// Marshal the transport parameters +func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { + // Typical Transport Parameters consume around 110 bytes, depending on the exact values, + // especially the lengths of the Connection IDs. + // Allocate 256 bytes, so we won't have to grow the slice in any case. + b := make([]byte, 0, 256) + + // add a greased value + random := make([]byte, 18) + rand.Read(random) + b = quicvarint.Append(b, 27+31*uint64(random[0])) + length := random[1] % 16 + b = quicvarint.Append(b, uint64(length)) + b = append(b, random[2:2+length]...) + + // initial_max_stream_data_bidi_local + b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) + // initial_max_stream_data_bidi_remote + b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) + // initial_max_stream_data_uni + b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) + // initial_max_data + b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) + // initial_max_bidi_streams + b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) + // initial_max_uni_streams + b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) + // idle_timeout + b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond)) + // max_packet_size + b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize)) + // max_ack_delay + // Only send it if is different from the default value. + if p.MaxAckDelay != protocol.DefaultMaxAckDelay { + b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond)) + } + // ack_delay_exponent + // Only send it if is different from the default value. + if p.AckDelayExponent != protocol.DefaultAckDelayExponent { + b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent)) + } + // disable_active_migration + if p.DisableActiveMigration { + b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID)) + b = quicvarint.Append(b, 0) + } + if pers == protocol.PerspectiveServer { + // stateless_reset_token + if p.StatelessResetToken != nil { + b = quicvarint.Append(b, uint64(statelessResetTokenParameterID)) + b = quicvarint.Append(b, 16) + b = append(b, p.StatelessResetToken[:]...) + } + // original_destination_connection_id + b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID)) + b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len())) + b = append(b, p.OriginalDestinationConnectionID.Bytes()...) + // preferred_address + if p.PreferredAddress != nil { + b = quicvarint.Append(b, uint64(preferredAddressParameterID)) + b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) + ip4 := p.PreferredAddress.IPv4.Addr().As4() + b = append(b, ip4[:]...) + b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) + ip6 := p.PreferredAddress.IPv6.Addr().As16() + b = append(b, ip6[:]...) + b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) + b = append(b, uint8(p.PreferredAddress.ConnectionID.Len())) + b = append(b, p.PreferredAddress.ConnectionID.Bytes()...) + b = append(b, p.PreferredAddress.StatelessResetToken[:]...) + } + } + // active_connection_id_limit + if p.ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit { + b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) + } + // initial_source_connection_id + b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) + b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len())) + b = append(b, p.InitialSourceConnectionID.Bytes()...) + // retry_source_connection_id + if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil { + b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID)) + b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len())) + b = append(b, p.RetrySourceConnectionID.Bytes()...) + } + if p.MaxDatagramFrameSize != protocol.InvalidByteCount { + b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) + } + + if pers == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 { + for k, v := range AdditionalTransportParametersClient { + b = quicvarint.Append(b, k) + b = quicvarint.Append(b, uint64(len(v))) + b = append(b, v...) + } + } + + return b +} + +func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte { + b = quicvarint.Append(b, uint64(id)) + b = quicvarint.Append(b, uint64(quicvarint.Len(val))) + return quicvarint.Append(b, val) +} + +// MarshalForSessionTicket marshals the transport parameters we save in the session ticket. +// When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters. +// The client will remember the transport parameters used in the last session, +// and apply those to the 0-RTT data it sends. +// Saving the transport parameters in the ticket gives the server the option to reject 0-RTT +// if the transport parameters changed. +// Since the session ticket is encrypted, the serialization format is defined by the server. +// For convenience, we use the same format that we also use for sending the transport parameters. +func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { + b = quicvarint.Append(b, transportParameterMarshalingVersion) + + // initial_max_stream_data_bidi_local + b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) + // initial_max_stream_data_bidi_remote + b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) + // initial_max_stream_data_uni + b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) + // initial_max_data + b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) + // initial_max_bidi_streams + b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) + // initial_max_uni_streams + b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) + // max_datagram_frame_size + if p.MaxDatagramFrameSize != protocol.InvalidByteCount { + b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) + } + // active_connection_id_limit + return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) +} + +// UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. +func (p *TransportParameters) UnmarshalFromSessionTicket(r *bytes.Reader) error { + version, err := quicvarint.Read(r) + if err != nil { + return err + } + if version != transportParameterMarshalingVersion { + return fmt.Errorf("unknown transport parameter marshaling version: %d", version) + } + return p.unmarshal(r, protocol.PerspectiveServer, true) +} + +// ValidFor0RTT checks if the transport parameters match those saved in the session ticket. +func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { + if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { + return false + } + return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && + p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && + p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && + p.InitialMaxData >= saved.InitialMaxData && + p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && + p.MaxUniStreamNum >= saved.MaxUniStreamNum && + p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit +} + +// ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection. +// It is only used on the client side. +func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { + if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { + return false + } + return p.ActiveConnectionIDLimit >= saved.ActiveConnectionIDLimit && + p.InitialMaxData >= saved.InitialMaxData && + p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && + p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && + p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && + p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && + p.MaxUniStreamNum >= saved.MaxUniStreamNum +} + +// String returns a string representation, intended for logging. +func (p *TransportParameters) String() string { + logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " + logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} + if p.RetrySourceConnectionID != nil { + logString += "RetrySourceConnectionID: %s, " + logParams = append(logParams, p.RetrySourceConnectionID) + } + logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d" + logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) + if p.StatelessResetToken != nil { // the client never sends a stateless reset token + logString += ", StatelessResetToken: %#x" + logParams = append(logParams, *p.StatelessResetToken) + } + if p.MaxDatagramFrameSize != protocol.InvalidByteCount { + logString += ", MaxDatagramFrameSize: %d" + logParams = append(logParams, p.MaxDatagramFrameSize) + } + logString += "}" + return fmt.Sprintf(logString, logParams...) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go new file mode 100644 index 000000000..a551aa8c9 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go @@ -0,0 +1,53 @@ +package wire + +import ( + "crypto/rand" + "encoding/binary" + "errors" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// ParseVersionNegotiationPacket parses a Version Negotiation packet. +func ParseVersionNegotiationPacket(b []byte) (dest, src protocol.ArbitraryLenConnectionID, _ []protocol.Version, _ error) { + n, dest, src, err := ParseArbitraryLenConnectionIDs(b) + if err != nil { + return nil, nil, nil, err + } + b = b[n:] + if len(b) == 0 { + //nolint:stylecheck + return nil, nil, nil, errors.New("Version Negotiation packet has empty version list") + } + if len(b)%4 != 0 { + //nolint:stylecheck + return nil, nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length") + } + versions := make([]protocol.Version, len(b)/4) + for i := 0; len(b) > 0; i++ { + versions[i] = protocol.Version(binary.BigEndian.Uint32(b[:4])) + b = b[4:] + } + return dest, src, versions, nil +} + +// ComposeVersionNegotiation composes a Version Negotiation +func ComposeVersionNegotiation(destConnID, srcConnID protocol.ArbitraryLenConnectionID, versions []protocol.Version) []byte { + greasedVersions := protocol.GetGreasedVersions(versions) + expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4 + buf := make([]byte, 1+4 /* type byte and version field */, expectedLen) + _, _ = rand.Read(buf[:1]) // ignore the error here. It is not critical to have perfect random here. + // Setting the "QUIC bit" (0x40) is not required by the RFC, + // but it allows clients to demultiplex QUIC with a long list of other protocols. + // See RFC 9443 and https://mailarchive.ietf.org/arch/msg/quic/oR4kxGKY6mjtPC1CZegY1ED4beg/ for details. + buf[0] |= 0xc0 + // The next 4 bytes are left at 0 (version number). + buf = append(buf, uint8(destConnID.Len())) + buf = append(buf, destConnID.Bytes()...) + buf = append(buf, uint8(srcConnID.Len())) + buf = append(buf, srcConnID.Bytes()...) + for _, v := range greasedVersions { + buf = binary.BigEndian.AppendUint32(buf, uint32(v)) + } + return buf +} diff --git a/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go b/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go new file mode 100644 index 000000000..7f54d6cda --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go @@ -0,0 +1,263 @@ +package logging + +import ( + "net" + "time" +) + +// A ConnectionTracer records events. +type ConnectionTracer struct { + StartedConnection func(local, remote net.Addr, srcConnID, destConnID ConnectionID) + NegotiatedVersion func(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) + ClosedConnection func(error) + SentTransportParameters func(*TransportParameters) + ReceivedTransportParameters func(*TransportParameters) + RestoredTransportParameters func(parameters *TransportParameters) // for 0-RTT + SentLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, *AckFrame, []Frame) + SentShortHeaderPacket func(*ShortHeader, ByteCount, ECN, *AckFrame, []Frame) + ReceivedVersionNegotiationPacket func(dest, src ArbitraryLenConnectionID, _ []VersionNumber) + ReceivedRetry func(*Header) + ReceivedLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, []Frame) + ReceivedShortHeaderPacket func(*ShortHeader, ByteCount, ECN, []Frame) + BufferedPacket func(PacketType, ByteCount) + DroppedPacket func(PacketType, PacketNumber, ByteCount, PacketDropReason) + UpdatedMetrics func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) + AcknowledgedPacket func(EncryptionLevel, PacketNumber) + LostPacket func(EncryptionLevel, PacketNumber, PacketLossReason) + UpdatedCongestionState func(CongestionState) + UpdatedPTOCount func(value uint32) + UpdatedKeyFromTLS func(EncryptionLevel, Perspective) + UpdatedKey func(keyPhase KeyPhase, remote bool) + DroppedEncryptionLevel func(EncryptionLevel) + DroppedKey func(keyPhase KeyPhase) + SetLossTimer func(TimerType, EncryptionLevel, time.Time) + LossTimerExpired func(TimerType, EncryptionLevel) + LossTimerCanceled func() + ECNStateUpdated func(state ECNState, trigger ECNStateTrigger) + ChoseALPN func(protocol string) + // Close is called when the connection is closed. + Close func() + Debug func(name, msg string) +} + +// NewMultiplexedConnectionTracer creates a new connection tracer that multiplexes events to multiple tracers. +func NewMultiplexedConnectionTracer(tracers ...*ConnectionTracer) *ConnectionTracer { + if len(tracers) == 0 { + return nil + } + if len(tracers) == 1 { + return tracers[0] + } + return &ConnectionTracer{ + StartedConnection: func(local, remote net.Addr, srcConnID, destConnID ConnectionID) { + for _, t := range tracers { + if t.StartedConnection != nil { + t.StartedConnection(local, remote, srcConnID, destConnID) + } + } + }, + NegotiatedVersion: func(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) { + for _, t := range tracers { + if t.NegotiatedVersion != nil { + t.NegotiatedVersion(chosen, clientVersions, serverVersions) + } + } + }, + ClosedConnection: func(e error) { + for _, t := range tracers { + if t.ClosedConnection != nil { + t.ClosedConnection(e) + } + } + }, + SentTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.SentTransportParameters != nil { + t.SentTransportParameters(tp) + } + } + }, + ReceivedTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.ReceivedTransportParameters != nil { + t.ReceivedTransportParameters(tp) + } + } + }, + RestoredTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.RestoredTransportParameters != nil { + t.RestoredTransportParameters(tp) + } + } + }, + SentLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { + for _, t := range tracers { + if t.SentLongHeaderPacket != nil { + t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) + } + } + }, + SentShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { + for _, t := range tracers { + if t.SentShortHeaderPacket != nil { + t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) + } + } + }, + ReceivedVersionNegotiationPacket: func(dest, src ArbitraryLenConnectionID, versions []VersionNumber) { + for _, t := range tracers { + if t.ReceivedVersionNegotiationPacket != nil { + t.ReceivedVersionNegotiationPacket(dest, src, versions) + } + } + }, + ReceivedRetry: func(hdr *Header) { + for _, t := range tracers { + if t.ReceivedRetry != nil { + t.ReceivedRetry(hdr) + } + } + }, + ReceivedLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, frames []Frame) { + for _, t := range tracers { + if t.ReceivedLongHeaderPacket != nil { + t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) + } + } + }, + ReceivedShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, frames []Frame) { + for _, t := range tracers { + if t.ReceivedShortHeaderPacket != nil { + t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) + } + } + }, + BufferedPacket: func(typ PacketType, size ByteCount) { + for _, t := range tracers { + if t.BufferedPacket != nil { + t.BufferedPacket(typ, size) + } + } + }, + DroppedPacket: func(typ PacketType, pn PacketNumber, size ByteCount, reason PacketDropReason) { + for _, t := range tracers { + if t.DroppedPacket != nil { + t.DroppedPacket(typ, pn, size, reason) + } + } + }, + UpdatedMetrics: func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) { + for _, t := range tracers { + if t.UpdatedMetrics != nil { + t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) + } + } + }, + AcknowledgedPacket: func(encLevel EncryptionLevel, pn PacketNumber) { + for _, t := range tracers { + if t.AcknowledgedPacket != nil { + t.AcknowledgedPacket(encLevel, pn) + } + } + }, + LostPacket: func(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { + for _, t := range tracers { + if t.LostPacket != nil { + t.LostPacket(encLevel, pn, reason) + } + } + }, + UpdatedCongestionState: func(state CongestionState) { + for _, t := range tracers { + if t.UpdatedCongestionState != nil { + t.UpdatedCongestionState(state) + } + } + }, + UpdatedPTOCount: func(value uint32) { + for _, t := range tracers { + if t.UpdatedPTOCount != nil { + t.UpdatedPTOCount(value) + } + } + }, + UpdatedKeyFromTLS: func(encLevel EncryptionLevel, perspective Perspective) { + for _, t := range tracers { + if t.UpdatedKeyFromTLS != nil { + t.UpdatedKeyFromTLS(encLevel, perspective) + } + } + }, + UpdatedKey: func(generation KeyPhase, remote bool) { + for _, t := range tracers { + if t.UpdatedKey != nil { + t.UpdatedKey(generation, remote) + } + } + }, + DroppedEncryptionLevel: func(encLevel EncryptionLevel) { + for _, t := range tracers { + if t.DroppedEncryptionLevel != nil { + t.DroppedEncryptionLevel(encLevel) + } + } + }, + DroppedKey: func(generation KeyPhase) { + for _, t := range tracers { + if t.DroppedKey != nil { + t.DroppedKey(generation) + } + } + }, + SetLossTimer: func(typ TimerType, encLevel EncryptionLevel, exp time.Time) { + for _, t := range tracers { + if t.SetLossTimer != nil { + t.SetLossTimer(typ, encLevel, exp) + } + } + }, + LossTimerExpired: func(typ TimerType, encLevel EncryptionLevel) { + for _, t := range tracers { + if t.LossTimerExpired != nil { + t.LossTimerExpired(typ, encLevel) + } + } + }, + LossTimerCanceled: func() { + for _, t := range tracers { + if t.LossTimerCanceled != nil { + t.LossTimerCanceled() + } + } + }, + ECNStateUpdated: func(state ECNState, trigger ECNStateTrigger) { + for _, t := range tracers { + if t.ECNStateUpdated != nil { + t.ECNStateUpdated(state, trigger) + } + } + }, + ChoseALPN: func(protocol string) { + for _, t := range tracers { + if t.ChoseALPN != nil { + t.ChoseALPN(protocol) + } + } + }, + Close: func() { + for _, t := range tracers { + if t.Close != nil { + t.Close() + } + } + }, + Debug: func(name, msg string) { + for _, t := range tracers { + if t.Debug != nil { + t.Debug(name, msg) + } + } + }, + } +} diff --git a/vendor/github.com/quic-go/quic-go/logging/frame.go b/vendor/github.com/quic-go/quic-go/logging/frame.go new file mode 100644 index 000000000..9a055db35 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/frame.go @@ -0,0 +1,66 @@ +package logging + +import "github.com/quic-go/quic-go/internal/wire" + +// A Frame is a QUIC frame +type Frame interface{} + +// The AckRange is used within the AckFrame. +// It is a range of packet numbers that is being acknowledged. +type AckRange = wire.AckRange + +type ( + // An AckFrame is an ACK frame. + AckFrame = wire.AckFrame + // A ConnectionCloseFrame is a CONNECTION_CLOSE frame. + ConnectionCloseFrame = wire.ConnectionCloseFrame + // A DataBlockedFrame is a DATA_BLOCKED frame. + DataBlockedFrame = wire.DataBlockedFrame + // A HandshakeDoneFrame is a HANDSHAKE_DONE frame. + HandshakeDoneFrame = wire.HandshakeDoneFrame + // A MaxDataFrame is a MAX_DATA frame. + MaxDataFrame = wire.MaxDataFrame + // A MaxStreamDataFrame is a MAX_STREAM_DATA frame. + MaxStreamDataFrame = wire.MaxStreamDataFrame + // A MaxStreamsFrame is a MAX_STREAMS_FRAME. + MaxStreamsFrame = wire.MaxStreamsFrame + // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame. + NewConnectionIDFrame = wire.NewConnectionIDFrame + // A NewTokenFrame is a NEW_TOKEN frame. + NewTokenFrame = wire.NewTokenFrame + // A PathChallengeFrame is a PATH_CHALLENGE frame. + PathChallengeFrame = wire.PathChallengeFrame + // A PathResponseFrame is a PATH_RESPONSE frame. + PathResponseFrame = wire.PathResponseFrame + // A PingFrame is a PING frame. + PingFrame = wire.PingFrame + // A ResetStreamFrame is a RESET_STREAM frame. + ResetStreamFrame = wire.ResetStreamFrame + // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame. + RetireConnectionIDFrame = wire.RetireConnectionIDFrame + // A StopSendingFrame is a STOP_SENDING frame. + StopSendingFrame = wire.StopSendingFrame + // A StreamsBlockedFrame is a STREAMS_BLOCKED frame. + StreamsBlockedFrame = wire.StreamsBlockedFrame + // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame. + StreamDataBlockedFrame = wire.StreamDataBlockedFrame +) + +// A CryptoFrame is a CRYPTO frame. +type CryptoFrame struct { + Offset ByteCount + Length ByteCount +} + +// A StreamFrame is a STREAM frame. +type StreamFrame struct { + StreamID StreamID + Offset ByteCount + Length ByteCount + Fin bool +} + +// A DatagramFrame is a DATAGRAM frame. +type DatagramFrame struct { + Length ByteCount +} diff --git a/vendor/github.com/quic-go/quic-go/logging/interface.go b/vendor/github.com/quic-go/quic-go/logging/interface.go new file mode 100644 index 000000000..a618a1893 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/interface.go @@ -0,0 +1,111 @@ +// Package logging defines a logging interface for quic-go. +// This package should not be considered stable +package logging + +import ( + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +type ( + // A ByteCount is used to count bytes. + ByteCount = protocol.ByteCount + // ECN is the ECN value + ECN = protocol.ECN + // A ConnectionID is a QUIC Connection ID. + ConnectionID = protocol.ConnectionID + // An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long. + ArbitraryLenConnectionID = protocol.ArbitraryLenConnectionID + // The EncryptionLevel is the encryption level of a packet. + EncryptionLevel = protocol.EncryptionLevel + // The KeyPhase is the key phase of the 1-RTT keys. + KeyPhase = protocol.KeyPhase + // The KeyPhaseBit is the value of the key phase bit of the 1-RTT packets. + KeyPhaseBit = protocol.KeyPhaseBit + // The PacketNumber is the packet number of a packet. + PacketNumber = protocol.PacketNumber + // The Perspective is the role of a QUIC endpoint (client or server). + Perspective = protocol.Perspective + // A StatelessResetToken is a stateless reset token. + StatelessResetToken = protocol.StatelessResetToken + // The StreamID is the stream ID. + StreamID = protocol.StreamID + // The StreamNum is the number of the stream. + StreamNum = protocol.StreamNum + // The StreamType is the type of the stream (unidirectional or bidirectional). + StreamType = protocol.StreamType + // The VersionNumber is the QUIC version. + VersionNumber = protocol.Version + + // The Header is the QUIC packet header, before removing header protection. + Header = wire.Header + // The ExtendedHeader is the QUIC Long Header packet header, after removing header protection. + ExtendedHeader = wire.ExtendedHeader + // The TransportParameters are QUIC transport parameters. + TransportParameters = wire.TransportParameters + // The PreferredAddress is the preferred address sent in the transport parameters. + PreferredAddress = wire.PreferredAddress + + // A TransportError is a transport-level error code. + TransportError = qerr.TransportErrorCode + // An ApplicationError is an application-defined error code. + ApplicationError = qerr.TransportErrorCode + + // The RTTStats contain statistics used by the congestion controller. + RTTStats = utils.RTTStats +) + +const ( + // ECNUnsupported means that no ECN value was set / received + ECNUnsupported = protocol.ECNUnsupported + // ECTNot is Not-ECT + ECTNot = protocol.ECNNon + // ECT0 is ECT(0) + ECT0 = protocol.ECT0 + // ECT1 is ECT(1) + ECT1 = protocol.ECT1 + // ECNCE is CE + ECNCE = protocol.ECNCE +) + +const ( + // KeyPhaseZero is key phase bit 0 + KeyPhaseZero KeyPhaseBit = protocol.KeyPhaseZero + // KeyPhaseOne is key phase bit 1 + KeyPhaseOne KeyPhaseBit = protocol.KeyPhaseOne +) + +const ( + // PerspectiveServer is used for a QUIC server + PerspectiveServer Perspective = protocol.PerspectiveServer + // PerspectiveClient is used for a QUIC client + PerspectiveClient Perspective = protocol.PerspectiveClient +) + +const ( + // EncryptionInitial is the Initial encryption level + EncryptionInitial EncryptionLevel = protocol.EncryptionInitial + // EncryptionHandshake is the Handshake encryption level + EncryptionHandshake EncryptionLevel = protocol.EncryptionHandshake + // Encryption1RTT is the 1-RTT encryption level + Encryption1RTT EncryptionLevel = protocol.Encryption1RTT + // Encryption0RTT is the 0-RTT encryption level + Encryption0RTT EncryptionLevel = protocol.Encryption0RTT +) + +const ( + // StreamTypeUni is a unidirectional stream + StreamTypeUni = protocol.StreamTypeUni + // StreamTypeBidi is a bidirectional stream + StreamTypeBidi = protocol.StreamTypeBidi +) + +// The ShortHeader is the QUIC Short Header packet header, after removing header protection. +type ShortHeader struct { + DestConnectionID ConnectionID + PacketNumber PacketNumber + PacketNumberLen protocol.PacketNumberLen + KeyPhase KeyPhaseBit +} diff --git a/vendor/github.com/quic-go/quic-go/logging/packet_header.go b/vendor/github.com/quic-go/quic-go/logging/packet_header.go new file mode 100644 index 000000000..6b8df58d8 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/packet_header.go @@ -0,0 +1,24 @@ +package logging + +import ( + "github.com/quic-go/quic-go/internal/protocol" +) + +// PacketTypeFromHeader determines the packet type from a *wire.Header. +func PacketTypeFromHeader(hdr *Header) PacketType { + if hdr.Version == 0 { + return PacketTypeVersionNegotiation + } + switch hdr.Type { + case protocol.PacketTypeInitial: + return PacketTypeInitial + case protocol.PacketTypeHandshake: + return PacketTypeHandshake + case protocol.PacketType0RTT: + return PacketType0RTT + case protocol.PacketTypeRetry: + return PacketTypeRetry + default: + return PacketTypeNotDetermined + } +} diff --git a/vendor/github.com/quic-go/quic-go/logging/tracer.go b/vendor/github.com/quic-go/quic-go/logging/tracer.go new file mode 100644 index 000000000..edd85dbaa --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/tracer.go @@ -0,0 +1,59 @@ +package logging + +import "net" + +// A Tracer traces events. +type Tracer struct { + SentPacket func(net.Addr, *Header, ByteCount, []Frame) + SentVersionNegotiationPacket func(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) + DroppedPacket func(net.Addr, PacketType, ByteCount, PacketDropReason) + Debug func(name, msg string) + Close func() +} + +// NewMultiplexedTracer creates a new tracer that multiplexes events to multiple tracers. +func NewMultiplexedTracer(tracers ...*Tracer) *Tracer { + if len(tracers) == 0 { + return nil + } + if len(tracers) == 1 { + return tracers[0] + } + return &Tracer{ + SentPacket: func(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { + for _, t := range tracers { + if t.SentPacket != nil { + t.SentPacket(remote, hdr, size, frames) + } + } + }, + SentVersionNegotiationPacket: func(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []VersionNumber) { + for _, t := range tracers { + if t.SentVersionNegotiationPacket != nil { + t.SentVersionNegotiationPacket(remote, dest, src, versions) + } + } + }, + DroppedPacket: func(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) { + for _, t := range tracers { + if t.DroppedPacket != nil { + t.DroppedPacket(remote, typ, size, reason) + } + } + }, + Debug: func(name, msg string) { + for _, t := range tracers { + if t.Debug != nil { + t.Debug(name, msg) + } + } + }, + Close: func() { + for _, t := range tracers { + if t.Close != nil { + t.Close() + } + } + }, + } +} diff --git a/vendor/github.com/quic-go/quic-go/logging/types.go b/vendor/github.com/quic-go/quic-go/logging/types.go new file mode 100644 index 000000000..0d79b0a90 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/types.go @@ -0,0 +1,128 @@ +package logging + +// PacketType is the packet type of a QUIC packet +type PacketType uint8 + +const ( + // PacketTypeInitial is the packet type of an Initial packet + PacketTypeInitial PacketType = iota + // PacketTypeHandshake is the packet type of a Handshake packet + PacketTypeHandshake + // PacketTypeRetry is the packet type of a Retry packet + PacketTypeRetry + // PacketType0RTT is the packet type of a 0-RTT packet + PacketType0RTT + // PacketTypeVersionNegotiation is the packet type of a Version Negotiation packet + PacketTypeVersionNegotiation + // PacketType1RTT is a 1-RTT packet + PacketType1RTT + // PacketTypeStatelessReset is a stateless reset + PacketTypeStatelessReset + // PacketTypeNotDetermined is the packet type when it could not be determined + PacketTypeNotDetermined +) + +type PacketLossReason uint8 + +const ( + // PacketLossReorderingThreshold: when a packet is deemed lost due to reordering threshold + PacketLossReorderingThreshold PacketLossReason = iota + // PacketLossTimeThreshold: when a packet is deemed lost due to time threshold + PacketLossTimeThreshold +) + +type PacketDropReason uint8 + +const ( + // PacketDropKeyUnavailable is used when a packet is dropped because keys are unavailable + PacketDropKeyUnavailable PacketDropReason = iota + // PacketDropUnknownConnectionID is used when a packet is dropped because the connection ID is unknown + PacketDropUnknownConnectionID + // PacketDropHeaderParseError is used when a packet is dropped because header parsing failed + PacketDropHeaderParseError + // PacketDropPayloadDecryptError is used when a packet is dropped because decrypting the payload failed + PacketDropPayloadDecryptError + // PacketDropProtocolViolation is used when a packet is dropped due to a protocol violation + PacketDropProtocolViolation + // PacketDropDOSPrevention is used when a packet is dropped to mitigate a DoS attack + PacketDropDOSPrevention + // PacketDropUnsupportedVersion is used when a packet is dropped because the version is not supported + PacketDropUnsupportedVersion + // PacketDropUnexpectedPacket is used when an unexpected packet is received + PacketDropUnexpectedPacket + // PacketDropUnexpectedSourceConnectionID is used when a packet with an unexpected source connection ID is received + PacketDropUnexpectedSourceConnectionID + // PacketDropUnexpectedVersion is used when a packet with an unexpected version is received + PacketDropUnexpectedVersion + // PacketDropDuplicate is used when a duplicate packet is received + PacketDropDuplicate +) + +// TimerType is the type of the loss detection timer +type TimerType uint8 + +const ( + // TimerTypeACK is the timer type for the early retransmit timer + TimerTypeACK TimerType = iota + // TimerTypePTO is the timer type for the PTO retransmit timer + TimerTypePTO +) + +// TimeoutReason is the reason why a connection is closed +type TimeoutReason uint8 + +const ( + // TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout + // This reason is not defined in the qlog draft, but very useful for debugging. + TimeoutReasonHandshake TimeoutReason = iota + // TimeoutReasonIdle is used when the connection is closed due to an idle timeout + // This reason is not defined in the qlog draft, but very useful for debugging. + TimeoutReasonIdle +) + +type CongestionState uint8 + +const ( + // CongestionStateSlowStart is the slow start phase of Reno / Cubic + CongestionStateSlowStart CongestionState = iota + // CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic + CongestionStateCongestionAvoidance + // CongestionStateRecovery is the recovery phase of Reno / Cubic + CongestionStateRecovery + // CongestionStateApplicationLimited means that the congestion controller is application limited + CongestionStateApplicationLimited +) + +// ECNState is the state of the ECN state machine (see Appendix A.4 of RFC 9000) +type ECNState uint8 + +const ( + // ECNStateTesting is the testing state + ECNStateTesting ECNState = 1 + iota + // ECNStateUnknown is the unknown state + ECNStateUnknown + // ECNStateFailed is the failed state + ECNStateFailed + // ECNStateCapable is the capable state + ECNStateCapable +) + +// ECNStateTrigger is a trigger for an ECN state transition. +type ECNStateTrigger uint8 + +const ( + ECNTriggerNoTrigger ECNStateTrigger = iota + // ECNFailedNoECNCounts is emitted when an ACK acknowledges ECN-marked packets, + // but doesn't contain any ECN counts + ECNFailedNoECNCounts + // ECNFailedDecreasedECNCounts is emitted when an ACK frame decreases ECN counts + ECNFailedDecreasedECNCounts + // ECNFailedLostAllTestingPackets is emitted when all ECN testing packets are declared lost + ECNFailedLostAllTestingPackets + // ECNFailedMoreECNCountsThanSent is emitted when an ACK contains more ECN counts than ECN-marked packets were sent + ECNFailedMoreECNCountsThanSent + // ECNFailedTooFewECNCounts is emitted when an ACK contains fewer ECN counts than it acknowledges packets + ECNFailedTooFewECNCounts + // ECNFailedManglingDetected is emitted when the path marks all ECN-marked packets as CE + ECNFailedManglingDetected +) diff --git a/vendor/github.com/quic-go/quic-go/mockgen.go b/vendor/github.com/quic-go/quic-go/mockgen.go new file mode 100644 index 000000000..81cc4a5ef --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/mockgen.go @@ -0,0 +1,76 @@ +//go:build gomock || generate + +package quic + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" +type SendConn = sendConn + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn" +type RawConn = rawConn + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" +type Sender = sender + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI" +type StreamI = streamI + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_stream_test.go github.com/quic-go/quic-go CryptoStream" +type CryptoStream = cryptoStream + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI" +type ReceiveStreamI = receiveStreamI + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI" +type SendStreamI = sendStreamI + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_getter_test.go github.com/quic-go/quic-go StreamGetter" +type StreamGetter = streamGetter + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" +type StreamSender = streamSender + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_data_handler_test.go github.com/quic-go/quic-go CryptoDataHandler" +type CryptoDataHandler = cryptoDataHandler + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" +type FrameSource = frameSource + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" +type AckFrameSource = ackFrameSource + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager" +type StreamManager = streamManager + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" +type SealingManager = sealingManager + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" +type Unpacker = unpacker + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" +type Packer = packer + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" +type MTUDiscoverer = mtuDiscoverer + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" +type ConnRunner = connRunner + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn" +type QUICConn = quicConn + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" +type PacketHandler = packetHandler + +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" +type PacketHandlerManager = packetHandlerManager + +// Need to use source mode for the batchConn, since reflect mode follows type aliases. +// See https://github.com/golang/mock/issues/244 for details. +// +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn" + +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_token_store_test.go github.com/quic-go/quic-go TokenStore" +//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" diff --git a/vendor/github.com/quic-go/quic-go/mtu_discoverer.go b/vendor/github.com/quic-go/quic-go/mtu_discoverer.go new file mode 100644 index 000000000..317b09292 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/mtu_discoverer.go @@ -0,0 +1,119 @@ +package quic + +import ( + "net" + "time" + + "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +type mtuDiscoverer interface { + // Start starts the MTU discovery process. + // It's unnecessary to call ShouldSendProbe before that. + Start(maxPacketSize protocol.ByteCount) + ShouldSendProbe(now time.Time) bool + CurrentSize() protocol.ByteCount + GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount) +} + +const ( + // At some point, we have to stop searching for a higher MTU. + // We're happy to send a packet that's 10 bytes smaller than the actual MTU. + maxMTUDiff = 20 + // send a probe packet every mtuProbeDelay RTTs + mtuProbeDelay = 5 +) + +func getMaxPacketSize(addr net.Addr) protocol.ByteCount { + maxSize := protocol.ByteCount(protocol.MinInitialPacketSize) + // If this is not a UDP address, we don't know anything about the MTU. + // Use the minimum size of an Initial packet as the max packet size. + if udpAddr, ok := addr.(*net.UDPAddr); ok { + if utils.IsIPv4(udpAddr.IP) { + maxSize = protocol.InitialPacketSizeIPv4 + } else { + maxSize = protocol.InitialPacketSizeIPv6 + } + } + return maxSize +} + +type mtuFinder struct { + lastProbeTime time.Time + mtuIncreased func(protocol.ByteCount) + + rttStats *utils.RTTStats + inFlight protocol.ByteCount // the size of the probe packet currently in flight. InvalidByteCount if none is in flight + current protocol.ByteCount + max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer) +} + +var _ mtuDiscoverer = &mtuFinder{} + +func newMTUDiscoverer(rttStats *utils.RTTStats, start protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) *mtuFinder { + return &mtuFinder{ + inFlight: protocol.InvalidByteCount, + current: start, + rttStats: rttStats, + mtuIncreased: mtuIncreased, + } +} + +func (f *mtuFinder) done() bool { + return f.max-f.current <= maxMTUDiff+1 +} + +func (f *mtuFinder) Start(maxPacketSize protocol.ByteCount) { + f.lastProbeTime = time.Now() // makes sure the first probe packet is not sent immediately + f.max = maxPacketSize +} + +func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { + if f.max == 0 || f.lastProbeTime.IsZero() { + return false + } + if f.inFlight != protocol.InvalidByteCount || f.done() { + return false + } + return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())) +} + +func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { + size := (f.max + f.current) / 2 + f.lastProbeTime = time.Now() + f.inFlight = size + return ackhandler.Frame{ + Frame: &wire.PingFrame{}, + Handler: (*mtuFinderAckHandler)(f), + }, size +} + +func (f *mtuFinder) CurrentSize() protocol.ByteCount { + return f.current +} + +type mtuFinderAckHandler mtuFinder + +var _ ackhandler.FrameHandler = &mtuFinderAckHandler{} + +func (h *mtuFinderAckHandler) OnAcked(wire.Frame) { + size := h.inFlight + if size == protocol.InvalidByteCount { + panic("OnAcked callback called although there's no MTU probe packet in flight") + } + h.inFlight = protocol.InvalidByteCount + h.current = size + h.mtuIncreased(size) +} + +func (h *mtuFinderAckHandler) OnLost(wire.Frame) { + size := h.inFlight + if size == protocol.InvalidByteCount { + panic("OnLost callback called although there's no MTU probe packet in flight") + } + h.max = size + h.inFlight = protocol.InvalidByteCount +} diff --git a/vendor/github.com/quic-go/quic-go/multiplexer.go b/vendor/github.com/quic-go/quic-go/multiplexer.go new file mode 100644 index 000000000..85f7f4034 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/multiplexer.go @@ -0,0 +1,75 @@ +package quic + +import ( + "fmt" + "net" + "sync" + + "github.com/quic-go/quic-go/internal/utils" +) + +var ( + connMuxerOnce sync.Once + connMuxer multiplexer +) + +type indexableConn interface{ LocalAddr() net.Addr } + +type multiplexer interface { + AddConn(conn indexableConn) + RemoveConn(indexableConn) error +} + +// The connMultiplexer listens on multiple net.PacketConns and dispatches +// incoming packets to the connection handler. +type connMultiplexer struct { + mutex sync.Mutex + + conns map[string] /* LocalAddr().String() */ indexableConn + logger utils.Logger +} + +var _ multiplexer = &connMultiplexer{} + +func getMultiplexer() multiplexer { + connMuxerOnce.Do(func() { + connMuxer = &connMultiplexer{ + conns: make(map[string]indexableConn), + logger: utils.DefaultLogger.WithPrefix("muxer"), + } + }) + return connMuxer +} + +func (m *connMultiplexer) index(addr net.Addr) string { + return addr.Network() + " " + addr.String() +} + +func (m *connMultiplexer) AddConn(c indexableConn) { + m.mutex.Lock() + defer m.mutex.Unlock() + + connIndex := m.index(c.LocalAddr()) + p, ok := m.conns[connIndex] + if ok { + // Panics if we're already listening on this connection. + // This is a safeguard because we're introducing a breaking API change, see + // https://github.com/quic-go/quic-go/issues/3727 for details. + // We'll remove this at a later time, when most users of the library have made the switch. + panic("connection already exists") // TODO: write a nice message + } + m.conns[connIndex] = p +} + +func (m *connMultiplexer) RemoveConn(c indexableConn) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + connIndex := m.index(c.LocalAddr()) + if _, ok := m.conns[connIndex]; !ok { + return fmt.Errorf("cannote remove connection, connection is unknown") + } + + delete(m.conns, connIndex) + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/oss-fuzz.sh b/vendor/github.com/quic-go/quic-go/oss-fuzz.sh new file mode 100644 index 000000000..22a577fe1 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/oss-fuzz.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Install Go manually, since oss-fuzz ships with an outdated Go version. +# See https://github.com/google/oss-fuzz/pull/10643. +export CXX="${CXX} -lresolv" # required by Go 1.20 +wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz \ + && mkdir temp-go \ + && rm -rf /root/.go/* \ + && tar -C temp-go/ -xzf go1.22.0.linux-amd64.tar.gz \ + && mv temp-go/go/* /root/.go/ \ + && rm -rf temp-go go1.22.0.linux-amd64.tar.gz + +( +# fuzz qpack +compile_go_fuzzer github.com/quic-go/qpack/fuzzing Fuzz qpack_fuzzer +) + +( +# fuzz quic-go +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/frames Fuzz frame_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/header Fuzz header_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/transportparameters Fuzz transportparameter_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/tokens Fuzz token_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/handshake Fuzz handshake_fuzzer + +if [ $SANITIZER == "coverage" ]; then + # no need for corpora if coverage + exit 0 +fi + +# generate seed corpora +cd $GOPATH/src/github.com/quic-go/quic-go/ +go generate -x ./fuzzing/... + +zip --quiet -r $OUT/header_fuzzer_seed_corpus.zip fuzzing/header/corpus +zip --quiet -r $OUT/frame_fuzzer_seed_corpus.zip fuzzing/frames/corpus +zip --quiet -r $OUT/transportparameter_fuzzer_seed_corpus.zip fuzzing/transportparameters/corpus +zip --quiet -r $OUT/handshake_fuzzer_seed_corpus.zip fuzzing/handshake/corpus +) + +# for debugging +ls -al $OUT diff --git a/vendor/github.com/quic-go/quic-go/packet_handler_map.go b/vendor/github.com/quic-go/quic-go/packet_handler_map.go new file mode 100644 index 000000000..7840202cc --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/packet_handler_map.go @@ -0,0 +1,255 @@ +package quic + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "hash" + "io" + "net" + "sync" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +type connCapabilities struct { + // This connection has the Don't Fragment (DF) bit set. + // This means it makes to run DPLPMTUD. + DF bool + // GSO (Generic Segmentation Offload) supported + GSO bool + // ECN (Explicit Congestion Notifications) supported + ECN bool +} + +// rawConn is a connection that allow reading of a receivedPackeh. +type rawConn interface { + ReadPacket() (receivedPacket, error) + // WritePacket writes a packet on the wire. + // gsoSize is the size of a single packet, or 0 to disable GSO. + // It is invalid to set gsoSize if capabilities.GSO is not set. + WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) + LocalAddr() net.Addr + SetReadDeadline(time.Time) error + io.Closer + + capabilities() connCapabilities +} + +type closePacket struct { + payload []byte + addr net.Addr + info packetInfo +} + +type packetHandlerMap struct { + mutex sync.Mutex + handlers map[protocol.ConnectionID]packetHandler + resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler + + closed bool + closeChan chan struct{} + + enqueueClosePacket func(closePacket) + + deleteRetiredConnsAfter time.Duration + + statelessResetMutex sync.Mutex + statelessResetHasher hash.Hash + + logger utils.Logger +} + +var _ packetHandlerManager = &packetHandlerMap{} + +func newPacketHandlerMap(key *StatelessResetKey, enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { + h := &packetHandlerMap{ + closeChan: make(chan struct{}), + handlers: make(map[protocol.ConnectionID]packetHandler), + resetTokens: make(map[protocol.StatelessResetToken]packetHandler), + deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, + enqueueClosePacket: enqueueClosePacket, + logger: logger, + } + if key != nil { + h.statelessResetHasher = hmac.New(sha256.New, key[:]) + } + if h.logger.Debug() { + go h.logUsage() + } + return h +} + +func (h *packetHandlerMap) logUsage() { + ticker := time.NewTicker(2 * time.Second) + var printedZero bool + for { + select { + case <-h.closeChan: + return + case <-ticker.C: + } + + h.mutex.Lock() + numHandlers := len(h.handlers) + numTokens := len(h.resetTokens) + h.mutex.Unlock() + // If the number tracked handlers and tokens is zero, only print it a single time. + hasZero := numHandlers == 0 && numTokens == 0 + if !hasZero || (hasZero && !printedZero) { + h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens) + printedZero = false + if hasZero { + printedZero = true + } + } + } +} + +func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { + h.mutex.Lock() + defer h.mutex.Unlock() + + handler, ok := h.handlers[id] + return handler, ok +} + +func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { + h.mutex.Lock() + defer h.mutex.Unlock() + + if _, ok := h.handlers[id]; ok { + h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) + return false + } + h.handlers[id] = handler + h.logger.Debugf("Adding connection ID %s.", id) + return true +} + +func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, handler packetHandler) bool { + h.mutex.Lock() + defer h.mutex.Unlock() + + if _, ok := h.handlers[clientDestConnID]; ok { + h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) + return false + } + h.handlers[clientDestConnID] = handler + h.handlers[newConnID] = handler + h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) + return true +} + +func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { + h.mutex.Lock() + delete(h.handlers, id) + h.mutex.Unlock() + h.logger.Debugf("Removing connection ID %s.", id) +} + +func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { + h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) + time.AfterFunc(h.deleteRetiredConnsAfter, func() { + h.mutex.Lock() + delete(h.handlers, id) + h.mutex.Unlock() + h.logger.Debugf("Removing connection ID %s after it has been retired.", id) + }) +} + +// ReplaceWithClosed is called when a connection is closed. +// Depending on which side closed the connection, we need to: +// * remote close: absorb delayed packets +// * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost +func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, connClosePacket []byte) { + var handler packetHandler + if connClosePacket != nil { + handler = newClosedLocalConn( + func(addr net.Addr, info packetInfo) { + h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) + }, + h.logger, + ) + } else { + handler = newClosedRemoteConn() + } + + h.mutex.Lock() + for _, id := range ids { + h.handlers[id] = handler + } + h.mutex.Unlock() + h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids) + + time.AfterFunc(h.deleteRetiredConnsAfter, func() { + h.mutex.Lock() + for _, id := range ids { + delete(h.handlers, id) + } + h.mutex.Unlock() + h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids) + }) +} + +func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { + h.mutex.Lock() + h.resetTokens[token] = handler + h.mutex.Unlock() +} + +func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) { + h.mutex.Lock() + delete(h.resetTokens, token) + h.mutex.Unlock() +} + +func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { + h.mutex.Lock() + defer h.mutex.Unlock() + + handler, ok := h.resetTokens[token] + return handler, ok +} + +func (h *packetHandlerMap) Close(e error) { + h.mutex.Lock() + + if h.closed { + h.mutex.Unlock() + return + } + + close(h.closeChan) + + var wg sync.WaitGroup + for _, handler := range h.handlers { + wg.Add(1) + go func(handler packetHandler) { + handler.destroy(e) + wg.Done() + }(handler) + } + h.closed = true + h.mutex.Unlock() + wg.Wait() +} + +func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { + var token protocol.StatelessResetToken + if h.statelessResetHasher == nil { + // Return a random stateless reset token. + // This token will be sent in the server's transport parameters. + // By using a random token, an off-path attacker won't be able to disrupt the connection. + rand.Read(token[:]) + return token + } + h.statelessResetMutex.Lock() + h.statelessResetHasher.Write(connID.Bytes()) + copy(token[:], h.statelessResetHasher.Sum(nil)) + h.statelessResetHasher.Reset() + h.statelessResetMutex.Unlock() + return token +} diff --git a/vendor/github.com/quic-go/quic-go/packet_packer.go b/vendor/github.com/quic-go/quic-go/packet_packer.go new file mode 100644 index 000000000..e707734fb --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/packet_packer.go @@ -0,0 +1,911 @@ +package quic + +import ( + crand "crypto/rand" + "encoding/binary" + "errors" + "fmt" + + "golang.org/x/exp/rand" + + "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/wire" +) + +var errNothingToPack = errors.New("nothing to pack") + +type packer interface { + PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) + PackAckOnlyPacket(maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) + AppendPacket(buf *packetBuffer, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) + MaybePackProbePacket(protocol.EncryptionLevel, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) + PackConnectionClose(*qerr.TransportError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) + PackApplicationClose(*qerr.ApplicationError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) + PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) + + SetToken([]byte) +} + +type sealer interface { + handshake.LongHeaderSealer +} + +type payload struct { + streamFrames []ackhandler.StreamFrame + frames []ackhandler.Frame + ack *wire.AckFrame + length protocol.ByteCount +} + +type longHeaderPacket struct { + header *wire.ExtendedHeader + ack *wire.AckFrame + frames []ackhandler.Frame + streamFrames []ackhandler.StreamFrame // only used for 0-RTT packets + + length protocol.ByteCount +} + +type shortHeaderPacket struct { + PacketNumber protocol.PacketNumber + Frames []ackhandler.Frame + StreamFrames []ackhandler.StreamFrame + Ack *wire.AckFrame + Length protocol.ByteCount + IsPathMTUProbePacket bool + + // used for logging + DestConnID protocol.ConnectionID + PacketNumberLen protocol.PacketNumberLen + KeyPhase protocol.KeyPhaseBit +} + +func (p *shortHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.Frames) } + +type coalescedPacket struct { + buffer *packetBuffer + longHdrPackets []*longHeaderPacket + shortHdrPacket *shortHeaderPacket +} + +// IsOnlyShortHeaderPacket says if this packet only contains a short header packet (and no long header packets). +func (p *coalescedPacket) IsOnlyShortHeaderPacket() bool { + return len(p.longHdrPackets) == 0 && p.shortHdrPacket != nil +} + +func (p *longHeaderPacket) EncryptionLevel() protocol.EncryptionLevel { + //nolint:exhaustive // Will never be called for Retry packets (and they don't have encrypted data). + switch p.header.Type { + case protocol.PacketTypeInitial: + return protocol.EncryptionInitial + case protocol.PacketTypeHandshake: + return protocol.EncryptionHandshake + case protocol.PacketType0RTT: + return protocol.Encryption0RTT + default: + panic("can't determine encryption level") + } +} + +func (p *longHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.frames) } + +type packetNumberManager interface { + PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) + PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber +} + +type sealingManager interface { + GetInitialSealer() (handshake.LongHeaderSealer, error) + GetHandshakeSealer() (handshake.LongHeaderSealer, error) + Get0RTTSealer() (handshake.LongHeaderSealer, error) + Get1RTTSealer() (handshake.ShortHeaderSealer, error) +} + +type frameSource interface { + HasData() bool + AppendStreamFrames([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) + AppendControlFrames([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) +} + +type ackFrameSource interface { + GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame +} + +type packetPacker struct { + srcConnID protocol.ConnectionID + getDestConnID func() protocol.ConnectionID + + perspective protocol.Perspective + cryptoSetup sealingManager + + initialStream cryptoStream + handshakeStream cryptoStream + + token []byte + + pnManager packetNumberManager + framer frameSource + acks ackFrameSource + datagramQueue *datagramQueue + retransmissionQueue *retransmissionQueue + rand rand.Rand + + numNonAckElicitingAcks int +} + +var _ packer = &packetPacker{} + +func newPacketPacker( + srcConnID protocol.ConnectionID, + getDestConnID func() protocol.ConnectionID, + initialStream, handshakeStream cryptoStream, + packetNumberManager packetNumberManager, + retransmissionQueue *retransmissionQueue, + cryptoSetup sealingManager, + framer frameSource, + acks ackFrameSource, + datagramQueue *datagramQueue, + perspective protocol.Perspective, +) *packetPacker { + var b [8]byte + _, _ = crand.Read(b[:]) + + return &packetPacker{ + cryptoSetup: cryptoSetup, + getDestConnID: getDestConnID, + srcConnID: srcConnID, + initialStream: initialStream, + handshakeStream: handshakeStream, + retransmissionQueue: retransmissionQueue, + datagramQueue: datagramQueue, + perspective: perspective, + framer: framer, + acks: acks, + rand: *rand.New(rand.NewSource(binary.BigEndian.Uint64(b[:]))), + pnManager: packetNumberManager, + } +} + +// PackConnectionClose packs a packet that closes the connection with a transport error. +func (p *packetPacker) PackConnectionClose(e *qerr.TransportError, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { + var reason string + // don't send details of crypto errors + if !e.ErrorCode.IsCryptoError() { + reason = e.ErrorMessage + } + return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason, maxPacketSize, v) +} + +// PackApplicationClose packs a packet that closes the connection with an application error. +func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { + return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage, maxPacketSize, v) +} + +func (p *packetPacker) packConnectionClose( + isApplicationError bool, + errorCode uint64, + frameType uint64, + reason string, + maxPacketSize protocol.ByteCount, + v protocol.Version, +) (*coalescedPacket, error) { + var sealers [4]sealer + var hdrs [3]*wire.ExtendedHeader + var payloads [4]payload + var size protocol.ByteCount + var connID protocol.ConnectionID + var oneRTTPacketNumber protocol.PacketNumber + var oneRTTPacketNumberLen protocol.PacketNumberLen + var keyPhase protocol.KeyPhaseBit // only set for 1-RTT + var numLongHdrPackets uint8 + encLevels := [4]protocol.EncryptionLevel{protocol.EncryptionInitial, protocol.EncryptionHandshake, protocol.Encryption0RTT, protocol.Encryption1RTT} + for i, encLevel := range encLevels { + if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT { + continue + } + ccf := &wire.ConnectionCloseFrame{ + IsApplicationError: isApplicationError, + ErrorCode: errorCode, + FrameType: frameType, + ReasonPhrase: reason, + } + // don't send application errors in Initial or Handshake packets + if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) { + ccf.IsApplicationError = false + ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode) + ccf.ReasonPhrase = "" + } + pl := payload{ + frames: []ackhandler.Frame{{Frame: ccf}}, + length: ccf.Length(v), + } + + var sealer sealer + var err error + switch encLevel { + case protocol.EncryptionInitial: + sealer, err = p.cryptoSetup.GetInitialSealer() + case protocol.EncryptionHandshake: + sealer, err = p.cryptoSetup.GetHandshakeSealer() + case protocol.Encryption0RTT: + sealer, err = p.cryptoSetup.Get0RTTSealer() + case protocol.Encryption1RTT: + var s handshake.ShortHeaderSealer + s, err = p.cryptoSetup.Get1RTTSealer() + if err == nil { + keyPhase = s.KeyPhase() + } + sealer = s + } + if err == handshake.ErrKeysNotYetAvailable || err == handshake.ErrKeysDropped { + continue + } + if err != nil { + return nil, err + } + sealers[i] = sealer + var hdr *wire.ExtendedHeader + if encLevel == protocol.Encryption1RTT { + connID = p.getDestConnID() + oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, pl) + } else { + hdr = p.getLongHeader(encLevel, v) + hdrs[i] = hdr + size += p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead()) + numLongHdrPackets++ + } + payloads[i] = pl + } + buffer := getPacketBuffer() + packet := &coalescedPacket{ + buffer: buffer, + longHdrPackets: make([]*longHeaderPacket, 0, numLongHdrPackets), + } + for i, encLevel := range encLevels { + if sealers[i] == nil { + continue + } + var paddingLen protocol.ByteCount + if encLevel == protocol.EncryptionInitial { + paddingLen = p.initialPaddingLen(payloads[i].frames, size, maxPacketSize) + } + if encLevel == protocol.Encryption1RTT { + shp, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, keyPhase, payloads[i], paddingLen, maxPacketSize, sealers[i], false, v) + if err != nil { + return nil, err + } + packet.shortHdrPacket = &shp + } else { + longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdrs[i], payloads[i], paddingLen, encLevel, sealers[i], v) + if err != nil { + return nil, err + } + packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket) + } + } + return packet, nil +} + +// longHeaderPacketLength calculates the length of a serialized long header packet. +// It takes into account that packets that have a tiny payload need to be padded, +// such that len(payload) + packet number len >= 4 + AEAD overhead +func (p *packetPacker) longHeaderPacketLength(hdr *wire.ExtendedHeader, pl payload, v protocol.Version) protocol.ByteCount { + var paddingLen protocol.ByteCount + pnLen := protocol.ByteCount(hdr.PacketNumberLen) + if pl.length < 4-pnLen { + paddingLen = 4 - pnLen - pl.length + } + return hdr.GetLength(v) + pl.length + paddingLen +} + +// shortHeaderPacketLength calculates the length of a serialized short header packet. +// It takes into account that packets that have a tiny payload need to be padded, +// such that len(payload) + packet number len >= 4 + AEAD overhead +func (p *packetPacker) shortHeaderPacketLength(connID protocol.ConnectionID, pnLen protocol.PacketNumberLen, pl payload) protocol.ByteCount { + var paddingLen protocol.ByteCount + if pl.length < 4-protocol.ByteCount(pnLen) { + paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length + } + return wire.ShortHeaderLen(connID, pnLen) + pl.length + paddingLen +} + +// size is the expected size of the packet, if no padding was applied. +func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, currentSize, maxPacketSize protocol.ByteCount) protocol.ByteCount { + // For the server, only ack-eliciting Initial packets need to be padded. + if p.perspective == protocol.PerspectiveServer && !ackhandler.HasAckElicitingFrames(frames) { + return 0 + } + if currentSize >= maxPacketSize { + return 0 + } + return maxPacketSize - currentSize +} + +// PackCoalescedPacket packs a new packet. +// It packs an Initial / Handshake if there is data to send in these packet number spaces. +// It should only be called before the handshake is confirmed. +func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { + var ( + initialHdr, handshakeHdr, zeroRTTHdr *wire.ExtendedHeader + initialPayload, handshakePayload, zeroRTTPayload, oneRTTPayload payload + oneRTTPacketNumber protocol.PacketNumber + oneRTTPacketNumberLen protocol.PacketNumberLen + ) + // Try packing an Initial packet. + initialSealer, err := p.cryptoSetup.GetInitialSealer() + if err != nil && err != handshake.ErrKeysDropped { + return nil, err + } + var size protocol.ByteCount + if initialSealer != nil { + initialHdr, initialPayload = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(initialSealer.Overhead()), protocol.EncryptionInitial, onlyAck, true, v) + if initialPayload.length > 0 { + size += p.longHeaderPacketLength(initialHdr, initialPayload, v) + protocol.ByteCount(initialSealer.Overhead()) + } + } + + // Add a Handshake packet. + var handshakeSealer sealer + if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) { + var err error + handshakeSealer, err = p.cryptoSetup.GetHandshakeSealer() + if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { + return nil, err + } + if handshakeSealer != nil { + handshakeHdr, handshakePayload = p.maybeGetCryptoPacket(maxPacketSize-size-protocol.ByteCount(handshakeSealer.Overhead()), protocol.EncryptionHandshake, onlyAck, size == 0, v) + if handshakePayload.length > 0 { + s := p.longHeaderPacketLength(handshakeHdr, handshakePayload, v) + protocol.ByteCount(handshakeSealer.Overhead()) + size += s + } + } + } + + // Add a 0-RTT / 1-RTT packet. + var zeroRTTSealer sealer + var oneRTTSealer handshake.ShortHeaderSealer + var connID protocol.ConnectionID + var kp protocol.KeyPhaseBit + if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) { + var err error + oneRTTSealer, err = p.cryptoSetup.Get1RTTSealer() + if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { + return nil, err + } + if err == nil { // 1-RTT + kp = oneRTTSealer.KeyPhase() + connID = p.getDestConnID() + oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + hdrLen := wire.ShortHeaderLen(connID, oneRTTPacketNumberLen) + oneRTTPayload = p.maybeGetShortHeaderPacket(oneRTTSealer, hdrLen, maxPacketSize-size, onlyAck, size == 0, v) + if oneRTTPayload.length > 0 { + size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, oneRTTPayload) + protocol.ByteCount(oneRTTSealer.Overhead()) + } + } else if p.perspective == protocol.PerspectiveClient && !onlyAck { // 0-RTT packets can't contain ACK frames + var err error + zeroRTTSealer, err = p.cryptoSetup.Get0RTTSealer() + if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { + return nil, err + } + if zeroRTTSealer != nil { + zeroRTTHdr, zeroRTTPayload = p.maybeGetAppDataPacketFor0RTT(zeroRTTSealer, maxPacketSize-size, v) + if zeroRTTPayload.length > 0 { + size += p.longHeaderPacketLength(zeroRTTHdr, zeroRTTPayload, v) + protocol.ByteCount(zeroRTTSealer.Overhead()) + } + } + } + } + + if initialPayload.length == 0 && handshakePayload.length == 0 && zeroRTTPayload.length == 0 && oneRTTPayload.length == 0 { + return nil, nil + } + + buffer := getPacketBuffer() + packet := &coalescedPacket{ + buffer: buffer, + longHdrPackets: make([]*longHeaderPacket, 0, 3), + } + if initialPayload.length > 0 { + padding := p.initialPaddingLen(initialPayload.frames, size, maxPacketSize) + cont, err := p.appendLongHeaderPacket(buffer, initialHdr, initialPayload, padding, protocol.EncryptionInitial, initialSealer, v) + if err != nil { + return nil, err + } + packet.longHdrPackets = append(packet.longHdrPackets, cont) + } + if handshakePayload.length > 0 { + cont, err := p.appendLongHeaderPacket(buffer, handshakeHdr, handshakePayload, 0, protocol.EncryptionHandshake, handshakeSealer, v) + if err != nil { + return nil, err + } + packet.longHdrPackets = append(packet.longHdrPackets, cont) + } + if zeroRTTPayload.length > 0 { + longHdrPacket, err := p.appendLongHeaderPacket(buffer, zeroRTTHdr, zeroRTTPayload, 0, protocol.Encryption0RTT, zeroRTTSealer, v) + if err != nil { + return nil, err + } + packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket) + } else if oneRTTPayload.length > 0 { + shp, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, kp, oneRTTPayload, 0, maxPacketSize, oneRTTSealer, false, v) + if err != nil { + return nil, err + } + packet.shortHdrPacket = &shp + } + return packet, nil +} + +// PackAckOnlyPacket packs a packet containing only an ACK in the application data packet number space. +// It should be called after the handshake is confirmed. +func (p *packetPacker) PackAckOnlyPacket(maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { + buf := getPacketBuffer() + packet, err := p.appendPacket(buf, true, maxPacketSize, v) + return packet, buf, err +} + +// AppendPacket packs a packet in the application data packet number space. +// It should be called after the handshake is confirmed. +func (p *packetPacker) AppendPacket(buf *packetBuffer, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) { + return p.appendPacket(buf, false, maxPacketSize, v) +} + +func (p *packetPacker) appendPacket(buf *packetBuffer, onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) { + sealer, err := p.cryptoSetup.Get1RTTSealer() + if err != nil { + return shortHeaderPacket{}, err + } + pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + connID := p.getDestConnID() + hdrLen := wire.ShortHeaderLen(connID, pnLen) + pl := p.maybeGetShortHeaderPacket(sealer, hdrLen, maxPacketSize, onlyAck, true, v) + if pl.length == 0 { + return shortHeaderPacket{}, errNothingToPack + } + kp := sealer.KeyPhase() + + return p.appendShortHeaderPacket(buf, connID, pn, pnLen, kp, pl, 0, maxPacketSize, sealer, false, v) +} + +func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize protocol.ByteCount, encLevel protocol.EncryptionLevel, onlyAck, ackAllowed bool, v protocol.Version) (*wire.ExtendedHeader, payload) { + if onlyAck { + if ack := p.acks.GetAckFrame(encLevel, true); ack != nil { + return p.getLongHeader(encLevel, v), payload{ + ack: ack, + length: ack.Length(v), + } + } + return nil, payload{} + } + + var s cryptoStream + var handler ackhandler.FrameHandler + var hasRetransmission bool + //nolint:exhaustive // Initial and Handshake are the only two encryption levels here. + switch encLevel { + case protocol.EncryptionInitial: + s = p.initialStream + handler = p.retransmissionQueue.InitialAckHandler() + hasRetransmission = p.retransmissionQueue.HasInitialData() + case protocol.EncryptionHandshake: + s = p.handshakeStream + handler = p.retransmissionQueue.HandshakeAckHandler() + hasRetransmission = p.retransmissionQueue.HasHandshakeData() + } + + hasData := s.HasData() + var ack *wire.AckFrame + if ackAllowed { + ack = p.acks.GetAckFrame(encLevel, !hasRetransmission && !hasData) + } + if !hasData && !hasRetransmission && ack == nil { + // nothing to send + return nil, payload{} + } + + var pl payload + if ack != nil { + pl.ack = ack + pl.length = ack.Length(v) + maxPacketSize -= pl.length + } + hdr := p.getLongHeader(encLevel, v) + maxPacketSize -= hdr.GetLength(v) + if hasRetransmission { + for { + var f ackhandler.Frame + //nolint:exhaustive // 0-RTT packets can't contain any retransmission.s + switch encLevel { + case protocol.EncryptionInitial: + f.Frame = p.retransmissionQueue.GetInitialFrame(maxPacketSize, v) + f.Handler = p.retransmissionQueue.InitialAckHandler() + case protocol.EncryptionHandshake: + f.Frame = p.retransmissionQueue.GetHandshakeFrame(maxPacketSize, v) + f.Handler = p.retransmissionQueue.HandshakeAckHandler() + } + if f.Frame == nil { + break + } + pl.frames = append(pl.frames, f) + frameLen := f.Frame.Length(v) + pl.length += frameLen + maxPacketSize -= frameLen + } + } else if s.HasData() { + cf := s.PopCryptoFrame(maxPacketSize) + pl.frames = []ackhandler.Frame{{Frame: cf, Handler: handler}} + pl.length += cf.Length(v) + } + return hdr, pl +} + +func (p *packetPacker) maybeGetAppDataPacketFor0RTT(sealer sealer, maxPacketSize protocol.ByteCount, v protocol.Version) (*wire.ExtendedHeader, payload) { + if p.perspective != protocol.PerspectiveClient { + return nil, payload{} + } + + hdr := p.getLongHeader(protocol.Encryption0RTT, v) + maxPayloadSize := maxPacketSize - hdr.GetLength(v) - protocol.ByteCount(sealer.Overhead()) + return hdr, p.maybeGetAppDataPacket(maxPayloadSize, false, false, v) +} + +func (p *packetPacker) maybeGetShortHeaderPacket(sealer handshake.ShortHeaderSealer, hdrLen protocol.ByteCount, maxPacketSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { + maxPayloadSize := maxPacketSize - hdrLen - protocol.ByteCount(sealer.Overhead()) + return p.maybeGetAppDataPacket(maxPayloadSize, onlyAck, ackAllowed, v) +} + +func (p *packetPacker) maybeGetAppDataPacket(maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { + pl := p.composeNextPacket(maxPayloadSize, onlyAck, ackAllowed, v) + + // check if we have anything to send + if len(pl.frames) == 0 && len(pl.streamFrames) == 0 { + if pl.ack == nil { + return payload{} + } + // the packet only contains an ACK + if p.numNonAckElicitingAcks >= protocol.MaxNonAckElicitingAcks { + ping := &wire.PingFrame{} + pl.frames = append(pl.frames, ackhandler.Frame{Frame: ping}) + pl.length += ping.Length(v) + p.numNonAckElicitingAcks = 0 + } else { + p.numNonAckElicitingAcks++ + } + } else { + p.numNonAckElicitingAcks = 0 + } + return pl +} + +func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { + if onlyAck { + if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, true); ack != nil { + return payload{ack: ack, length: ack.Length(v)} + } + return payload{} + } + + hasData := p.framer.HasData() + hasRetransmission := p.retransmissionQueue.HasAppData() + + var hasAck bool + var pl payload + if ackAllowed { + if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData); ack != nil { + pl.ack = ack + pl.length += ack.Length(v) + hasAck = true + } + } + + if p.datagramQueue != nil { + if f := p.datagramQueue.Peek(); f != nil { + size := f.Length(v) + if size <= maxFrameSize-pl.length { // DATAGRAM frame fits + pl.frames = append(pl.frames, ackhandler.Frame{Frame: f}) + pl.length += size + p.datagramQueue.Pop() + } else if !hasAck { + // The DATAGRAM frame doesn't fit, and the packet doesn't contain an ACK. + // Discard this frame. There's no point in retrying this in the next packet, + // as it's unlikely that the available packet size will increase. + p.datagramQueue.Pop() + } + // If the DATAGRAM frame was too large and the packet contained an ACK, we'll try to send it out later. + } + } + + if hasAck && !hasData && !hasRetransmission { + return pl + } + + if hasRetransmission { + for { + remainingLen := maxFrameSize - pl.length + if remainingLen < protocol.MinStreamFrameSize { + break + } + f := p.retransmissionQueue.GetAppDataFrame(remainingLen, v) + if f == nil { + break + } + pl.frames = append(pl.frames, ackhandler.Frame{Frame: f, Handler: p.retransmissionQueue.AppDataAckHandler()}) + pl.length += f.Length(v) + } + } + + if hasData { + var lengthAdded protocol.ByteCount + startLen := len(pl.frames) + pl.frames, lengthAdded = p.framer.AppendControlFrames(pl.frames, maxFrameSize-pl.length, v) + pl.length += lengthAdded + // add handlers for the control frames that were added + for i := startLen; i < len(pl.frames); i++ { + switch pl.frames[i].Frame.(type) { + case *wire.PathChallengeFrame, *wire.PathResponseFrame: + // Path probing is currently not supported, therefore we don't need to set the OnAcked callback yet. + // PATH_CHALLENGE and PATH_RESPONSE are never retransmitted. + default: + pl.frames[i].Handler = p.retransmissionQueue.AppDataAckHandler() + } + } + + pl.streamFrames, lengthAdded = p.framer.AppendStreamFrames(pl.streamFrames, maxFrameSize-pl.length, v) + pl.length += lengthAdded + } + return pl +} + +func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { + if encLevel == protocol.Encryption1RTT { + s, err := p.cryptoSetup.Get1RTTSealer() + if err != nil { + return nil, err + } + kp := s.KeyPhase() + connID := p.getDestConnID() + pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + hdrLen := wire.ShortHeaderLen(connID, pnLen) + pl := p.maybeGetAppDataPacket(maxPacketSize-protocol.ByteCount(s.Overhead())-hdrLen, false, true, v) + if pl.length == 0 { + return nil, nil + } + buffer := getPacketBuffer() + packet := &coalescedPacket{buffer: buffer} + shp, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, maxPacketSize, s, false, v) + if err != nil { + return nil, err + } + packet.shortHdrPacket = &shp + return packet, nil + } + + var hdr *wire.ExtendedHeader + var pl payload + var sealer handshake.LongHeaderSealer + //nolint:exhaustive // Probe packets are never sent for 0-RTT. + switch encLevel { + case protocol.EncryptionInitial: + var err error + sealer, err = p.cryptoSetup.GetInitialSealer() + if err != nil { + return nil, err + } + hdr, pl = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionInitial, false, true, v) + case protocol.EncryptionHandshake: + var err error + sealer, err = p.cryptoSetup.GetHandshakeSealer() + if err != nil { + return nil, err + } + hdr, pl = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionHandshake, false, true, v) + default: + panic("unknown encryption level") + } + + if pl.length == 0 { + return nil, nil + } + buffer := getPacketBuffer() + packet := &coalescedPacket{buffer: buffer} + size := p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead()) + var padding protocol.ByteCount + if encLevel == protocol.EncryptionInitial { + padding = p.initialPaddingLen(pl.frames, size, maxPacketSize) + } + + longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdr, pl, padding, encLevel, sealer, v) + if err != nil { + return nil, err + } + packet.longHdrPackets = []*longHeaderPacket{longHdrPacket} + return packet, nil +} + +func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { + pl := payload{ + frames: []ackhandler.Frame{ping}, + length: ping.Frame.Length(v), + } + buffer := getPacketBuffer() + s, err := p.cryptoSetup.Get1RTTSealer() + if err != nil { + return shortHeaderPacket{}, nil, err + } + connID := p.getDestConnID() + pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + padding := size - p.shortHeaderPacketLength(connID, pnLen, pl) - protocol.ByteCount(s.Overhead()) + kp := s.KeyPhase() + packet, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, padding, size, s, true, v) + return packet, buffer, err +} + +func (p *packetPacker) getLongHeader(encLevel protocol.EncryptionLevel, v protocol.Version) *wire.ExtendedHeader { + pn, pnLen := p.pnManager.PeekPacketNumber(encLevel) + hdr := &wire.ExtendedHeader{ + PacketNumber: pn, + PacketNumberLen: pnLen, + } + hdr.Version = v + hdr.SrcConnectionID = p.srcConnID + hdr.DestConnectionID = p.getDestConnID() + + //nolint:exhaustive // 1-RTT packets are not long header packets. + switch encLevel { + case protocol.EncryptionInitial: + hdr.Type = protocol.PacketTypeInitial + hdr.Token = p.token + case protocol.EncryptionHandshake: + hdr.Type = protocol.PacketTypeHandshake + case protocol.Encryption0RTT: + hdr.Type = protocol.PacketType0RTT + } + return hdr +} + +func (p *packetPacker) appendLongHeaderPacket(buffer *packetBuffer, header *wire.ExtendedHeader, pl payload, padding protocol.ByteCount, encLevel protocol.EncryptionLevel, sealer sealer, v protocol.Version) (*longHeaderPacket, error) { + var paddingLen protocol.ByteCount + pnLen := protocol.ByteCount(header.PacketNumberLen) + if pl.length < 4-pnLen { + paddingLen = 4 - pnLen - pl.length + } + paddingLen += padding + header.Length = pnLen + protocol.ByteCount(sealer.Overhead()) + pl.length + paddingLen + + startLen := len(buffer.Data) + raw := buffer.Data[startLen:] + raw, err := header.Append(raw, v) + if err != nil { + return nil, err + } + payloadOffset := protocol.ByteCount(len(raw)) + + raw, err = p.appendPacketPayload(raw, pl, paddingLen, v) + if err != nil { + return nil, err + } + raw = p.encryptPacket(raw, sealer, header.PacketNumber, payloadOffset, pnLen) + buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)] + + if pn := p.pnManager.PopPacketNumber(encLevel); pn != header.PacketNumber { + return nil, fmt.Errorf("packetPacker BUG: Peeked and Popped packet numbers do not match: expected %d, got %d", pn, header.PacketNumber) + } + return &longHeaderPacket{ + header: header, + ack: pl.ack, + frames: pl.frames, + streamFrames: pl.streamFrames, + length: protocol.ByteCount(len(raw)), + }, nil +} + +func (p *packetPacker) appendShortHeaderPacket( + buffer *packetBuffer, + connID protocol.ConnectionID, + pn protocol.PacketNumber, + pnLen protocol.PacketNumberLen, + kp protocol.KeyPhaseBit, + pl payload, + padding, maxPacketSize protocol.ByteCount, + sealer sealer, + isMTUProbePacket bool, + v protocol.Version, +) (shortHeaderPacket, error) { + var paddingLen protocol.ByteCount + if pl.length < 4-protocol.ByteCount(pnLen) { + paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length + } + paddingLen += padding + + startLen := len(buffer.Data) + raw := buffer.Data[startLen:] + raw, err := wire.AppendShortHeader(raw, connID, pn, pnLen, kp) + if err != nil { + return shortHeaderPacket{}, err + } + payloadOffset := protocol.ByteCount(len(raw)) + + raw, err = p.appendPacketPayload(raw, pl, paddingLen, v) + if err != nil { + return shortHeaderPacket{}, err + } + if !isMTUProbePacket { + if size := protocol.ByteCount(len(raw) + sealer.Overhead()); size > maxPacketSize { + return shortHeaderPacket{}, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, maxPacketSize) + } + } + raw = p.encryptPacket(raw, sealer, pn, payloadOffset, protocol.ByteCount(pnLen)) + buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)] + + if newPN := p.pnManager.PopPacketNumber(protocol.Encryption1RTT); newPN != pn { + return shortHeaderPacket{}, fmt.Errorf("packetPacker BUG: Peeked and Popped packet numbers do not match: expected %d, got %d", pn, newPN) + } + return shortHeaderPacket{ + PacketNumber: pn, + PacketNumberLen: pnLen, + KeyPhase: kp, + StreamFrames: pl.streamFrames, + Frames: pl.frames, + Ack: pl.ack, + Length: protocol.ByteCount(len(raw)), + DestConnID: connID, + IsPathMTUProbePacket: isMTUProbePacket, + }, nil +} + +// appendPacketPayload serializes the payload of a packet into the raw byte slice. +// It modifies the order of payload.frames. +func (p *packetPacker) appendPacketPayload(raw []byte, pl payload, paddingLen protocol.ByteCount, v protocol.Version) ([]byte, error) { + payloadOffset := len(raw) + if pl.ack != nil { + var err error + raw, err = pl.ack.Append(raw, v) + if err != nil { + return nil, err + } + } + if paddingLen > 0 { + raw = append(raw, make([]byte, paddingLen)...) + } + // Randomize the order of the control frames. + // This makes sure that the receiver doesn't rely on the order in which frames are packed. + if len(pl.frames) > 1 { + p.rand.Shuffle(len(pl.frames), func(i, j int) { pl.frames[i], pl.frames[j] = pl.frames[j], pl.frames[i] }) + } + for _, f := range pl.frames { + var err error + raw, err = f.Frame.Append(raw, v) + if err != nil { + return nil, err + } + } + for _, f := range pl.streamFrames { + var err error + raw, err = f.Frame.Append(raw, v) + if err != nil { + return nil, err + } + } + + if payloadSize := protocol.ByteCount(len(raw)-payloadOffset) - paddingLen; payloadSize != pl.length { + return nil, fmt.Errorf("PacketPacker BUG: payload size inconsistent (expected %d, got %d bytes)", pl.length, payloadSize) + } + return raw, nil +} + +func (p *packetPacker) encryptPacket(raw []byte, sealer sealer, pn protocol.PacketNumber, payloadOffset, pnLen protocol.ByteCount) []byte { + _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], pn, raw[:payloadOffset]) + raw = raw[:len(raw)+sealer.Overhead()] + // apply header protection + pnOffset := payloadOffset - pnLen + sealer.EncryptHeader(raw[pnOffset+4:pnOffset+4+16], &raw[0], raw[pnOffset:payloadOffset]) + return raw +} + +func (p *packetPacker) SetToken(token []byte) { + p.token = token +} diff --git a/vendor/github.com/quic-go/quic-go/packet_unpacker.go b/vendor/github.com/quic-go/quic-go/packet_unpacker.go new file mode 100644 index 000000000..1034aab1d --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/packet_unpacker.go @@ -0,0 +1,226 @@ +package quic + +import ( + "bytes" + "fmt" + "time" + + "github.com/quic-go/quic-go/internal/handshake" + "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 headerDecryptor interface { + DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) +} + +type headerParseError struct { + err error +} + +func (e *headerParseError) Unwrap() error { + return e.err +} + +func (e *headerParseError) Error() string { + return e.err.Error() +} + +type unpackedPacket struct { + hdr *wire.ExtendedHeader + encryptionLevel protocol.EncryptionLevel + data []byte +} + +// The packetUnpacker unpacks QUIC packets. +type packetUnpacker struct { + cs handshake.CryptoSetup + + shortHdrConnIDLen int +} + +var _ unpacker = &packetUnpacker{} + +func newPacketUnpacker(cs handshake.CryptoSetup, shortHdrConnIDLen int) *packetUnpacker { + return &packetUnpacker{ + cs: cs, + shortHdrConnIDLen: shortHdrConnIDLen, + } +} + +// UnpackLongHeader unpacks a Long Header packet. +// If the reserved bits are invalid, the error is wire.ErrInvalidReservedBits. +// If any other error occurred when parsing the header, the error is of type headerParseError. +// If decrypting the payload fails for any reason, the error is the error returned by the AEAD. +func (u *packetUnpacker) UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte, v protocol.Version) (*unpackedPacket, error) { + var encLevel protocol.EncryptionLevel + var extHdr *wire.ExtendedHeader + var decrypted []byte + //nolint:exhaustive // Retry packets can't be unpacked. + switch hdr.Type { + case protocol.PacketTypeInitial: + encLevel = protocol.EncryptionInitial + opener, err := u.cs.GetInitialOpener() + if err != nil { + return nil, err + } + extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v) + if err != nil { + return nil, err + } + case protocol.PacketTypeHandshake: + encLevel = protocol.EncryptionHandshake + opener, err := u.cs.GetHandshakeOpener() + if err != nil { + return nil, err + } + extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v) + if err != nil { + return nil, err + } + case protocol.PacketType0RTT: + encLevel = protocol.Encryption0RTT + opener, err := u.cs.Get0RTTOpener() + if err != nil { + return nil, err + } + extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unknown packet type: %s", hdr.Type) + } + + if len(decrypted) == 0 { + return nil, &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "empty packet", + } + } + + return &unpackedPacket{ + hdr: extHdr, + encryptionLevel: encLevel, + data: decrypted, + }, nil +} + +func (u *packetUnpacker) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { + opener, err := u.cs.Get1RTTOpener() + if err != nil { + return 0, 0, 0, nil, err + } + pn, pnLen, kp, decrypted, err := u.unpackShortHeaderPacket(opener, rcvTime, data) + if err != nil { + return 0, 0, 0, nil, err + } + if len(decrypted) == 0 { + return 0, 0, 0, nil, &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "empty packet", + } + } + return pn, pnLen, kp, decrypted, nil +} + +func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpener, hdr *wire.Header, data []byte, v protocol.Version) (*wire.ExtendedHeader, []byte, error) { + extHdr, parseErr := u.unpackLongHeader(opener, hdr, data, v) + // If the reserved bits are set incorrectly, we still need to continue unpacking. + // This avoids a timing side-channel, which otherwise might allow an attacker + // to gain information about the header encryption. + if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { + return nil, nil, parseErr + } + extHdrLen := extHdr.ParsedLen() + extHdr.PacketNumber = opener.DecodePacketNumber(extHdr.PacketNumber, extHdr.PacketNumberLen) + decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], extHdr.PacketNumber, data[:extHdrLen]) + if err != nil { + return nil, nil, err + } + if parseErr != nil { + return nil, nil, parseErr + } + return extHdr, decrypted, nil +} + +func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { + l, pn, pnLen, kp, parseErr := u.unpackShortHeader(opener, data) + // If the reserved bits are set incorrectly, we still need to continue unpacking. + // This avoids a timing side-channel, which otherwise might allow an attacker + // to gain information about the header encryption. + if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { + return 0, 0, 0, nil, &headerParseError{parseErr} + } + pn = opener.DecodePacketNumber(pn, pnLen) + decrypted, err := opener.Open(data[l:l], data[l:], rcvTime, pn, kp, data[:l]) + if err != nil { + return 0, 0, 0, nil, err + } + return pn, pnLen, kp, decrypted, parseErr +} + +func (u *packetUnpacker) unpackShortHeader(hd headerDecryptor, data []byte) (int, protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, error) { + hdrLen := 1 /* first header byte */ + u.shortHdrConnIDLen + if len(data) < hdrLen+4+16 { + return 0, 0, 0, 0, fmt.Errorf("packet too small, expected at least 20 bytes after the header, got %d", len(data)-hdrLen) + } + origPNBytes := make([]byte, 4) + copy(origPNBytes, data[hdrLen:hdrLen+4]) + // 2. decrypt the header, assuming a 4 byte packet number + hd.DecryptHeader( + data[hdrLen+4:hdrLen+4+16], + &data[0], + data[hdrLen:hdrLen+4], + ) + // 3. parse the header (and learn the actual length of the packet number) + l, pn, pnLen, kp, parseErr := wire.ParseShortHeader(data, u.shortHdrConnIDLen) + if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { + return l, pn, pnLen, kp, parseErr + } + // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier + if pnLen != protocol.PacketNumberLen4 { + copy(data[hdrLen+int(pnLen):hdrLen+4], origPNBytes[int(pnLen):]) + } + return l, pn, pnLen, kp, parseErr +} + +// The error is either nil, a wire.ErrInvalidReservedBits or of type headerParseError. +func (u *packetUnpacker) unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte, v protocol.Version) (*wire.ExtendedHeader, error) { + extHdr, err := unpackLongHeader(hd, hdr, data, v) + if err != nil && err != wire.ErrInvalidReservedBits { + return nil, &headerParseError{err: err} + } + return extHdr, err +} + +func unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte, v protocol.Version) (*wire.ExtendedHeader, error) { + r := bytes.NewReader(data) + + hdrLen := hdr.ParsedLen() + if protocol.ByteCount(len(data)) < hdrLen+4+16 { + //nolint:stylecheck + return nil, fmt.Errorf("Packet too small. Expected at least 20 bytes after the header, got %d", protocol.ByteCount(len(data))-hdrLen) + } + // The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it. + // 1. save a copy of the 4 bytes + origPNBytes := make([]byte, 4) + copy(origPNBytes, data[hdrLen:hdrLen+4]) + // 2. decrypt the header, assuming a 4 byte packet number + hd.DecryptHeader( + data[hdrLen+4:hdrLen+4+16], + &data[0], + data[hdrLen:hdrLen+4], + ) + // 3. parse the header (and learn the actual length of the packet number) + extHdr, parseErr := hdr.ParseExtended(r, v) + if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { + return nil, parseErr + } + // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier + if extHdr.PacketNumberLen != protocol.PacketNumberLen4 { + copy(data[extHdr.ParsedLen():hdrLen+4], origPNBytes[int(extHdr.PacketNumberLen):]) + } + return extHdr, parseErr +} diff --git a/vendor/github.com/quic-go/quic-go/quicvarint/io.go b/vendor/github.com/quic-go/quic-go/quicvarint/io.go new file mode 100644 index 000000000..9368d1c58 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/quicvarint/io.go @@ -0,0 +1,68 @@ +package quicvarint + +import ( + "bytes" + "io" +) + +// Reader implements both the io.ByteReader and io.Reader interfaces. +type Reader interface { + io.ByteReader + io.Reader +} + +var _ Reader = &bytes.Reader{} + +type byteReader struct { + io.Reader +} + +var _ Reader = &byteReader{} + +// NewReader returns a Reader for r. +// If r already implements both io.ByteReader and io.Reader, NewReader returns r. +// Otherwise, r is wrapped to add the missing interfaces. +func NewReader(r io.Reader) Reader { + if r, ok := r.(Reader); ok { + return r + } + return &byteReader{r} +} + +func (r *byteReader) ReadByte() (byte, error) { + var b [1]byte + n, err := r.Reader.Read(b[:]) + if n == 1 && err == io.EOF { + err = nil + } + return b[0], err +} + +// Writer implements both the io.ByteWriter and io.Writer interfaces. +type Writer interface { + io.ByteWriter + io.Writer +} + +var _ Writer = &bytes.Buffer{} + +type byteWriter struct { + io.Writer +} + +var _ Writer = &byteWriter{} + +// NewWriter returns a Writer for w. +// If r already implements both io.ByteWriter and io.Writer, NewWriter returns w. +// Otherwise, w is wrapped to add the missing interfaces. +func NewWriter(w io.Writer) Writer { + if w, ok := w.(Writer); ok { + return w + } + return &byteWriter{w} +} + +func (w *byteWriter) WriteByte(c byte) error { + _, err := w.Writer.Write([]byte{c}) + return err +} diff --git a/vendor/github.com/quic-go/quic-go/quicvarint/varint.go b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go new file mode 100644 index 000000000..3f12c0760 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go @@ -0,0 +1,141 @@ +package quicvarint + +import ( + "fmt" + "io" + + "github.com/quic-go/quic-go/internal/protocol" +) + +// taken from the QUIC draft +const ( + // Min is the minimum value allowed for a QUIC varint. + Min = 0 + + // Max is the maximum allowed value for a QUIC varint (2^62-1). + Max = maxVarInt8 + + maxVarInt1 = 63 + maxVarInt2 = 16383 + maxVarInt4 = 1073741823 + maxVarInt8 = 4611686018427387903 +) + +// Read reads a number in the QUIC varint format from r. +func Read(r io.ByteReader) (uint64, error) { + firstByte, err := r.ReadByte() + if err != nil { + return 0, err + } + // the first two bits of the first byte encode the length + len := 1 << ((firstByte & 0xc0) >> 6) + b1 := firstByte & (0xff - 0xc0) + if len == 1 { + return uint64(b1), nil + } + b2, err := r.ReadByte() + if err != nil { + return 0, err + } + if len == 2 { + return uint64(b2) + uint64(b1)<<8, nil + } + b3, err := r.ReadByte() + if err != nil { + return 0, err + } + b4, err := r.ReadByte() + if err != nil { + return 0, err + } + if len == 4 { + return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil + } + b5, err := r.ReadByte() + if err != nil { + return 0, err + } + b6, err := r.ReadByte() + if err != nil { + return 0, err + } + b7, err := r.ReadByte() + if err != nil { + return 0, err + } + b8, err := r.ReadByte() + if err != nil { + return 0, err + } + return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil +} + +// Append appends i in the QUIC varint format. +func Append(b []byte, i uint64) []byte { + if i <= maxVarInt1 { + return append(b, uint8(i)) + } + if i <= maxVarInt2 { + return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...) + } + if i <= maxVarInt4 { + return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...) + } + if i <= maxVarInt8 { + return append(b, []byte{ + uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32), + uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i), + }...) + } + panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) +} + +// AppendWithLen append i in the QUIC varint format with the desired length. +func AppendWithLen(b []byte, i uint64, length protocol.ByteCount) []byte { + if length != 1 && length != 2 && length != 4 && length != 8 { + panic("invalid varint length") + } + l := Len(i) + if l == length { + return Append(b, i) + } + if l > length { + panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length)) + } + if length == 2 { + b = append(b, 0b01000000) + } else if length == 4 { + b = append(b, 0b10000000) + } else if length == 8 { + b = append(b, 0b11000000) + } + for j := protocol.ByteCount(1); j < length-l; j++ { + b = append(b, 0) + } + for j := protocol.ByteCount(0); j < l; j++ { + b = append(b, uint8(i>>(8*(l-1-j)))) + } + return b +} + +// Len determines the number of bytes that will be needed to write the number i. +func Len(i uint64) protocol.ByteCount { + if i <= maxVarInt1 { + return 1 + } + if i <= maxVarInt2 { + return 2 + } + if i <= maxVarInt4 { + return 4 + } + if i <= maxVarInt8 { + return 8 + } + // Don't use a fmt.Sprintf here to format the error message. + // The function would then exceed the inlining budget. + panic(struct { + message string + num uint64 + }{"value doesn't fit into 62 bits: ", i}) +} diff --git a/vendor/github.com/quic-go/quic-go/receive_stream.go b/vendor/github.com/quic-go/quic-go/receive_stream.go new file mode 100644 index 000000000..1235ff0e7 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/receive_stream.go @@ -0,0 +1,323 @@ +package quic + +import ( + "fmt" + "io" + "sync" + "time" + + "github.com/quic-go/quic-go/internal/flowcontrol" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +type receiveStreamI interface { + ReceiveStream + + handleStreamFrame(*wire.StreamFrame) error + handleResetStreamFrame(*wire.ResetStreamFrame) error + closeForShutdown(error) + getWindowUpdate() protocol.ByteCount +} + +type receiveStream struct { + mutex sync.Mutex + + streamID protocol.StreamID + + sender streamSender + + frameQueue *frameSorter + finalOffset protocol.ByteCount + + currentFrame []byte + currentFrameDone func() + readPosInFrame int + currentFrameIsLast bool // is the currentFrame the last frame on this stream + + finRead bool // set once we read a frame with a Fin + closeForShutdownErr error + cancelReadErr error + resetRemotelyErr *StreamError + + readChan chan struct{} + readOnce chan struct{} // cap: 1, to protect against concurrent use of Read + deadline time.Time + + flowController flowcontrol.StreamFlowController +} + +var ( + _ ReceiveStream = &receiveStream{} + _ receiveStreamI = &receiveStream{} +) + +func newReceiveStream( + streamID protocol.StreamID, + sender streamSender, + flowController flowcontrol.StreamFlowController, +) *receiveStream { + return &receiveStream{ + streamID: streamID, + sender: sender, + flowController: flowController, + frameQueue: newFrameSorter(), + readChan: make(chan struct{}, 1), + readOnce: make(chan struct{}, 1), + finalOffset: protocol.MaxByteCount, + } +} + +func (s *receiveStream) StreamID() protocol.StreamID { + return s.streamID +} + +// Read implements io.Reader. It is not thread safe! +func (s *receiveStream) Read(p []byte) (int, error) { + // Concurrent use of Read is not permitted (and doesn't make any sense), + // but sometimes people do it anyway. + // Make sure that we only execute one call at any given time to avoid hard to debug failures. + s.readOnce <- struct{}{} + defer func() { <-s.readOnce }() + + s.mutex.Lock() + completed, n, err := s.readImpl(p) + s.mutex.Unlock() + + if completed { + s.sender.onStreamCompleted(s.streamID) + } + return n, err +} + +func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, error) { + if s.finRead { + return false, 0, io.EOF + } + if s.cancelReadErr != nil { + return false, 0, s.cancelReadErr + } + if s.resetRemotelyErr != nil { + return false, 0, s.resetRemotelyErr + } + if s.closeForShutdownErr != nil { + return false, 0, s.closeForShutdownErr + } + + var bytesRead int + var deadlineTimer *utils.Timer + for bytesRead < len(p) { + if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) { + s.dequeueNextFrame() + } + if s.currentFrame == nil && bytesRead > 0 { + return false, bytesRead, s.closeForShutdownErr + } + + for { + // Stop waiting on errors + if s.closeForShutdownErr != nil { + return false, bytesRead, s.closeForShutdownErr + } + if s.cancelReadErr != nil { + return false, bytesRead, s.cancelReadErr + } + if s.resetRemotelyErr != nil { + return false, bytesRead, s.resetRemotelyErr + } + + deadline := s.deadline + if !deadline.IsZero() { + if !time.Now().Before(deadline) { + return false, bytesRead, errDeadline + } + if deadlineTimer == nil { + deadlineTimer = utils.NewTimer() + defer deadlineTimer.Stop() + } + deadlineTimer.Reset(deadline) + } + + if s.currentFrame != nil || s.currentFrameIsLast { + break + } + + s.mutex.Unlock() + if deadline.IsZero() { + <-s.readChan + } else { + select { + case <-s.readChan: + case <-deadlineTimer.Chan(): + deadlineTimer.SetRead() + } + } + s.mutex.Lock() + if s.currentFrame == nil { + s.dequeueNextFrame() + } + } + + if bytesRead > len(p) { + return false, bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p)) + } + if s.readPosInFrame > len(s.currentFrame) { + return false, bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, len(s.currentFrame)) + } + + m := copy(p[bytesRead:], s.currentFrame[s.readPosInFrame:]) + s.readPosInFrame += m + bytesRead += m + + // when a RESET_STREAM was received, the flow controller was already + // informed about the final byteOffset for this stream + if s.resetRemotelyErr == nil { + s.flowController.AddBytesRead(protocol.ByteCount(m)) + } + + if s.readPosInFrame >= len(s.currentFrame) && s.currentFrameIsLast { + s.finRead = true + s.currentFrame = nil + if s.currentFrameDone != nil { + s.currentFrameDone() + } + return true, bytesRead, io.EOF + } + } + return false, bytesRead, nil +} + +func (s *receiveStream) dequeueNextFrame() { + var offset protocol.ByteCount + // We're done with the last frame. Release the buffer. + if s.currentFrameDone != nil { + s.currentFrameDone() + } + offset, s.currentFrame, s.currentFrameDone = s.frameQueue.Pop() + s.currentFrameIsLast = offset+protocol.ByteCount(len(s.currentFrame)) >= s.finalOffset + s.readPosInFrame = 0 +} + +func (s *receiveStream) CancelRead(errorCode StreamErrorCode) { + s.mutex.Lock() + completed := s.cancelReadImpl(errorCode) + s.mutex.Unlock() + + if completed { + s.flowController.Abandon() + s.sender.onStreamCompleted(s.streamID) + } +} + +func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ { + if s.finRead || s.cancelReadErr != nil || s.resetRemotelyErr != nil { + return false + } + s.cancelReadErr = &StreamError{StreamID: s.streamID, ErrorCode: errorCode, Remote: false} + s.signalRead() + s.sender.queueControlFrame(&wire.StopSendingFrame{ + StreamID: s.streamID, + ErrorCode: errorCode, + }) + // We're done with this stream if the final offset was already received. + return s.finalOffset != protocol.MaxByteCount +} + +func (s *receiveStream) handleStreamFrame(frame *wire.StreamFrame) error { + s.mutex.Lock() + completed, err := s.handleStreamFrameImpl(frame) + s.mutex.Unlock() + + if completed { + s.flowController.Abandon() + s.sender.onStreamCompleted(s.streamID) + } + return err +} + +func (s *receiveStream) handleStreamFrameImpl(frame *wire.StreamFrame) (bool /* completed */, error) { + maxOffset := frame.Offset + frame.DataLen() + if err := s.flowController.UpdateHighestReceived(maxOffset, frame.Fin); err != nil { + return false, err + } + var newlyRcvdFinalOffset bool + if frame.Fin { + newlyRcvdFinalOffset = s.finalOffset == protocol.MaxByteCount + s.finalOffset = maxOffset + } + if s.cancelReadErr != nil { + return newlyRcvdFinalOffset, nil + } + if err := s.frameQueue.Push(frame.Data, frame.Offset, frame.PutBack); err != nil { + return false, err + } + s.signalRead() + return false, nil +} + +func (s *receiveStream) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { + s.mutex.Lock() + completed, err := s.handleResetStreamFrameImpl(frame) + s.mutex.Unlock() + + if completed { + s.flowController.Abandon() + s.sender.onStreamCompleted(s.streamID) + } + return err +} + +func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame) (bool /*completed */, error) { + if s.closeForShutdownErr != nil { + return false, nil + } + if err := s.flowController.UpdateHighestReceived(frame.FinalSize, true); err != nil { + return false, err + } + newlyRcvdFinalOffset := s.finalOffset == protocol.MaxByteCount + s.finalOffset = frame.FinalSize + + // ignore duplicate RESET_STREAM frames for this stream (after checking their final offset) + if s.resetRemotelyErr != nil { + return false, nil + } + s.resetRemotelyErr = &StreamError{ + StreamID: s.streamID, + ErrorCode: frame.ErrorCode, + Remote: true, + } + s.signalRead() + return newlyRcvdFinalOffset, nil +} + +func (s *receiveStream) SetReadDeadline(t time.Time) error { + s.mutex.Lock() + s.deadline = t + s.mutex.Unlock() + s.signalRead() + return nil +} + +// CloseForShutdown closes a stream abruptly. +// It makes Read unblock (and return the error) immediately. +// The peer will NOT be informed about this: the stream is closed without sending a FIN or RESET. +func (s *receiveStream) closeForShutdown(err error) { + s.mutex.Lock() + s.closeForShutdownErr = err + s.mutex.Unlock() + s.signalRead() +} + +func (s *receiveStream) getWindowUpdate() protocol.ByteCount { + return s.flowController.GetWindowUpdate() +} + +// signalRead performs a non-blocking send on the readChan +func (s *receiveStream) signalRead() { + select { + case s.readChan <- struct{}{}: + default: + } +} diff --git a/vendor/github.com/quic-go/quic-go/retransmission_queue.go b/vendor/github.com/quic-go/quic-go/retransmission_queue.go new file mode 100644 index 000000000..14ae1e3b8 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/retransmission_queue.go @@ -0,0 +1,180 @@ +package quic + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/ackhandler" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +type retransmissionQueue struct { + initial []wire.Frame + initialCryptoData []*wire.CryptoFrame + + handshake []wire.Frame + handshakeCryptoData []*wire.CryptoFrame + + appData []wire.Frame +} + +func newRetransmissionQueue() *retransmissionQueue { + return &retransmissionQueue{} +} + +// AddPing queues a ping. +// It is used when a probe packet needs to be sent +func (q *retransmissionQueue) AddPing(encLevel protocol.EncryptionLevel) { + //nolint:exhaustive // Cannot send probe packets for 0-RTT. + switch encLevel { + case protocol.EncryptionInitial: + q.addInitial(&wire.PingFrame{}) + case protocol.EncryptionHandshake: + q.addHandshake(&wire.PingFrame{}) + case protocol.Encryption1RTT: + q.addAppData(&wire.PingFrame{}) + default: + panic("unexpected encryption level") + } +} + +func (q *retransmissionQueue) addInitial(f wire.Frame) { + if cf, ok := f.(*wire.CryptoFrame); ok { + q.initialCryptoData = append(q.initialCryptoData, cf) + return + } + q.initial = append(q.initial, f) +} + +func (q *retransmissionQueue) addHandshake(f wire.Frame) { + if cf, ok := f.(*wire.CryptoFrame); ok { + q.handshakeCryptoData = append(q.handshakeCryptoData, cf) + return + } + q.handshake = append(q.handshake, f) +} + +func (q *retransmissionQueue) HasInitialData() bool { + return len(q.initialCryptoData) > 0 || len(q.initial) > 0 +} + +func (q *retransmissionQueue) HasHandshakeData() bool { + return len(q.handshakeCryptoData) > 0 || len(q.handshake) > 0 +} + +func (q *retransmissionQueue) HasAppData() bool { + return len(q.appData) > 0 +} + +func (q *retransmissionQueue) addAppData(f wire.Frame) { + if _, ok := f.(*wire.StreamFrame); ok { + panic("STREAM frames are handled with their respective streams.") + } + q.appData = append(q.appData, f) +} + +func (q *retransmissionQueue) GetInitialFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame { + if len(q.initialCryptoData) > 0 { + f := q.initialCryptoData[0] + newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v) + if newFrame == nil && !needsSplit { // the whole frame fits + q.initialCryptoData = q.initialCryptoData[1:] + return f + } + if newFrame != nil { // frame was split. Leave the original frame in the queue. + return newFrame + } + } + if len(q.initial) == 0 { + return nil + } + f := q.initial[0] + if f.Length(v) > maxLen { + return nil + } + q.initial = q.initial[1:] + return f +} + +func (q *retransmissionQueue) GetHandshakeFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame { + if len(q.handshakeCryptoData) > 0 { + f := q.handshakeCryptoData[0] + newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v) + if newFrame == nil && !needsSplit { // the whole frame fits + q.handshakeCryptoData = q.handshakeCryptoData[1:] + return f + } + if newFrame != nil { // frame was split. Leave the original frame in the queue. + return newFrame + } + } + if len(q.handshake) == 0 { + return nil + } + f := q.handshake[0] + if f.Length(v) > maxLen { + return nil + } + q.handshake = q.handshake[1:] + return f +} + +func (q *retransmissionQueue) GetAppDataFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame { + if len(q.appData) == 0 { + return nil + } + f := q.appData[0] + if f.Length(v) > maxLen { + return nil + } + q.appData = q.appData[1:] + return f +} + +func (q *retransmissionQueue) DropPackets(encLevel protocol.EncryptionLevel) { + //nolint:exhaustive // Can only drop Initial and Handshake packet number space. + switch encLevel { + case protocol.EncryptionInitial: + q.initial = nil + q.initialCryptoData = nil + case protocol.EncryptionHandshake: + q.handshake = nil + q.handshakeCryptoData = nil + default: + panic(fmt.Sprintf("unexpected encryption level: %s", encLevel)) + } +} + +func (q *retransmissionQueue) InitialAckHandler() ackhandler.FrameHandler { + return (*retransmissionQueueInitialAckHandler)(q) +} + +func (q *retransmissionQueue) HandshakeAckHandler() ackhandler.FrameHandler { + return (*retransmissionQueueHandshakeAckHandler)(q) +} + +func (q *retransmissionQueue) AppDataAckHandler() ackhandler.FrameHandler { + return (*retransmissionQueueAppDataAckHandler)(q) +} + +type retransmissionQueueInitialAckHandler retransmissionQueue + +func (q *retransmissionQueueInitialAckHandler) OnAcked(wire.Frame) {} +func (q *retransmissionQueueInitialAckHandler) OnLost(f wire.Frame) { + (*retransmissionQueue)(q).addInitial(f) +} + +type retransmissionQueueHandshakeAckHandler retransmissionQueue + +func (q *retransmissionQueueHandshakeAckHandler) OnAcked(wire.Frame) {} +func (q *retransmissionQueueHandshakeAckHandler) OnLost(f wire.Frame) { + (*retransmissionQueue)(q).addHandshake(f) +} + +type retransmissionQueueAppDataAckHandler retransmissionQueue + +func (q *retransmissionQueueAppDataAckHandler) OnAcked(wire.Frame) {} +func (q *retransmissionQueueAppDataAckHandler) OnLost(f wire.Frame) { + (*retransmissionQueue)(q).addAppData(f) +} diff --git a/vendor/github.com/quic-go/quic-go/send_conn.go b/vendor/github.com/quic-go/quic-go/send_conn.go new file mode 100644 index 000000000..498ed112b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/send_conn.go @@ -0,0 +1,103 @@ +package quic + +import ( + "net" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +// A sendConn allows sending using a simple Write() on a non-connected packet conn. +type sendConn interface { + Write(b []byte, gsoSize uint16, ecn protocol.ECN) error + Close() error + LocalAddr() net.Addr + RemoteAddr() net.Addr + + capabilities() connCapabilities +} + +type sconn struct { + rawConn + + localAddr net.Addr + remoteAddr net.Addr + + logger utils.Logger + + packetInfoOOB []byte + // If GSO enabled, and we receive a GSO error for this remote address, GSO is disabled. + gotGSOError bool + // Used to catch the error sometimes returned by the first sendmsg call on Linux, + // see https://github.com/golang/go/issues/63322. + wroteFirstPacket bool +} + +var _ sendConn = &sconn{} + +func newSendConn(c rawConn, remote net.Addr, info packetInfo, logger utils.Logger) *sconn { + localAddr := c.LocalAddr() + if info.addr.IsValid() { + if udpAddr, ok := localAddr.(*net.UDPAddr); ok { + addrCopy := *udpAddr + addrCopy.IP = info.addr.AsSlice() + localAddr = &addrCopy + } + } + + oob := info.OOB() + // increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating + l := len(oob) + oob = append(oob, make([]byte, 64)...)[:l] + return &sconn{ + rawConn: c, + localAddr: localAddr, + remoteAddr: remote, + packetInfoOOB: oob, + logger: logger, + } +} + +func (c *sconn) Write(p []byte, gsoSize uint16, ecn protocol.ECN) error { + err := c.writePacket(p, c.remoteAddr, c.packetInfoOOB, gsoSize, ecn) + if err != nil && isGSOError(err) { + // disable GSO for future calls + c.gotGSOError = true + if c.logger.Debug() { + c.logger.Debugf("GSO failed when sending to %s", c.remoteAddr) + } + // send out the packets one by one + for len(p) > 0 { + l := len(p) + if l > int(gsoSize) { + l = int(gsoSize) + } + if err := c.writePacket(p[:l], c.remoteAddr, c.packetInfoOOB, 0, ecn); err != nil { + return err + } + p = p[l:] + } + return nil + } + return err +} + +func (c *sconn) writePacket(p []byte, addr net.Addr, oob []byte, gsoSize uint16, ecn protocol.ECN) error { + _, err := c.WritePacket(p, addr, oob, gsoSize, ecn) + if err != nil && !c.wroteFirstPacket && isPermissionError(err) { + _, err = c.WritePacket(p, addr, oob, gsoSize, ecn) + } + c.wroteFirstPacket = true + return err +} + +func (c *sconn) capabilities() connCapabilities { + capabilities := c.rawConn.capabilities() + if capabilities.GSO { + capabilities.GSO = !c.gotGSOError + } + return capabilities +} + +func (c *sconn) RemoteAddr() net.Addr { return c.remoteAddr } +func (c *sconn) LocalAddr() net.Addr { return c.localAddr } diff --git a/vendor/github.com/quic-go/quic-go/send_queue.go b/vendor/github.com/quic-go/quic-go/send_queue.go new file mode 100644 index 000000000..bde023348 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/send_queue.go @@ -0,0 +1,103 @@ +package quic + +import "github.com/quic-go/quic-go/internal/protocol" + +type sender interface { + Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN) + Run() error + WouldBlock() bool + Available() <-chan struct{} + Close() +} + +type queueEntry struct { + buf *packetBuffer + gsoSize uint16 + ecn protocol.ECN +} + +type sendQueue struct { + queue chan queueEntry + closeCalled chan struct{} // runStopped when Close() is called + runStopped chan struct{} // runStopped when the run loop returns + available chan struct{} + conn sendConn +} + +var _ sender = &sendQueue{} + +const sendQueueCapacity = 8 + +func newSendQueue(conn sendConn) sender { + return &sendQueue{ + conn: conn, + runStopped: make(chan struct{}), + closeCalled: make(chan struct{}), + available: make(chan struct{}, 1), + queue: make(chan queueEntry, sendQueueCapacity), + } +} + +// Send sends out a packet. It's guaranteed to not block. +// Callers need to make sure that there's actually space in the send queue by calling WouldBlock. +// Otherwise Send will panic. +func (h *sendQueue) Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN) { + select { + case h.queue <- queueEntry{buf: p, gsoSize: gsoSize, ecn: ecn}: + // clear available channel if we've reached capacity + if len(h.queue) == sendQueueCapacity { + select { + case <-h.available: + default: + } + } + case <-h.runStopped: + default: + panic("sendQueue.Send would have blocked") + } +} + +func (h *sendQueue) WouldBlock() bool { + return len(h.queue) == sendQueueCapacity +} + +func (h *sendQueue) Available() <-chan struct{} { + return h.available +} + +func (h *sendQueue) Run() error { + defer close(h.runStopped) + var shouldClose bool + for { + if shouldClose && len(h.queue) == 0 { + return nil + } + select { + case <-h.closeCalled: + h.closeCalled = nil // prevent this case from being selected again + // make sure that all queued packets are actually sent out + shouldClose = true + case e := <-h.queue: + if err := h.conn.Write(e.buf.Data, e.gsoSize, e.ecn); err != nil { + // This additional check enables: + // 1. Checking for "datagram too large" message from the kernel, as such, + // 2. Path MTU discovery,and + // 3. Eventual detection of loss PingFrame. + if !isSendMsgSizeErr(err) { + return err + } + } + e.buf.Release() + select { + case h.available <- struct{}{}: + default: + } + } + } +} + +func (h *sendQueue) Close() { + close(h.closeCalled) + // wait until the run loop returned + <-h.runStopped +} diff --git a/vendor/github.com/quic-go/quic-go/send_stream.go b/vendor/github.com/quic-go/quic-go/send_stream.go new file mode 100644 index 000000000..e1ce3e677 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/send_stream.go @@ -0,0 +1,494 @@ +package quic + +import ( + "context" + "fmt" + "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/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" +) + +type sendStreamI interface { + SendStream + handleStopSendingFrame(*wire.StopSendingFrame) + hasData() bool + popStreamFrame(maxBytes protocol.ByteCount, v protocol.Version) (frame ackhandler.StreamFrame, ok, hasMore bool) + closeForShutdown(error) + updateSendWindow(protocol.ByteCount) +} + +type sendStream struct { + mutex sync.Mutex + + numOutstandingFrames int64 + retransmissionQueue []*wire.StreamFrame + + ctx context.Context + ctxCancel context.CancelCauseFunc + + streamID protocol.StreamID + sender streamSender + + writeOffset protocol.ByteCount + + cancelWriteErr error + closeForShutdownErr error + + finishedWriting bool // set once Close() is called + finSent bool // set when a STREAM_FRAME with FIN bit has been sent + completed bool // set when this stream has been reported to the streamSender as completed + + dataForWriting []byte // during a Write() call, this slice is the part of p that still needs to be sent out + nextFrame *wire.StreamFrame + + writeChan chan struct{} + writeOnce chan struct{} + deadline time.Time + + flowController flowcontrol.StreamFlowController +} + +var ( + _ SendStream = &sendStream{} + _ sendStreamI = &sendStream{} +) + +func newSendStream( + streamID protocol.StreamID, + sender streamSender, + flowController flowcontrol.StreamFlowController, +) *sendStream { + s := &sendStream{ + streamID: streamID, + sender: sender, + flowController: flowController, + writeChan: make(chan struct{}, 1), + writeOnce: make(chan struct{}, 1), // cap: 1, to protect against concurrent use of Write + } + s.ctx, s.ctxCancel = context.WithCancelCause(context.Background()) + return s +} + +func (s *sendStream) StreamID() protocol.StreamID { + return s.streamID // same for receiveStream and sendStream +} + +func (s *sendStream) Write(p []byte) (int, error) { + // Concurrent use of Write is not permitted (and doesn't make any sense), + // but sometimes people do it anyway. + // Make sure that we only execute one call at any given time to avoid hard to debug failures. + s.writeOnce <- struct{}{} + defer func() { <-s.writeOnce }() + + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.finishedWriting { + return 0, fmt.Errorf("write on closed stream %d", s.streamID) + } + if s.cancelWriteErr != nil { + return 0, s.cancelWriteErr + } + if s.closeForShutdownErr != nil { + return 0, s.closeForShutdownErr + } + if !s.deadline.IsZero() && !time.Now().Before(s.deadline) { + return 0, errDeadline + } + if len(p) == 0 { + return 0, nil + } + + s.dataForWriting = p + + var ( + deadlineTimer *utils.Timer + bytesWritten int + notifiedSender bool + ) + for { + var copied bool + var deadline time.Time + // As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame), + // which can then be popped the next time we assemble a packet. + // This allows us to return Write() when all data but x bytes have been sent out. + // When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame, + // allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN). + if s.canBufferStreamFrame() && len(s.dataForWriting) > 0 { + if s.nextFrame == nil { + f := wire.GetStreamFrame() + f.Offset = s.writeOffset + f.StreamID = s.streamID + f.DataLenPresent = true + f.Data = f.Data[:len(s.dataForWriting)] + copy(f.Data, s.dataForWriting) + s.nextFrame = f + } else { + l := len(s.nextFrame.Data) + s.nextFrame.Data = s.nextFrame.Data[:l+len(s.dataForWriting)] + copy(s.nextFrame.Data[l:], s.dataForWriting) + } + s.dataForWriting = nil + bytesWritten = len(p) + copied = true + } else { + bytesWritten = len(p) - len(s.dataForWriting) + deadline = s.deadline + if !deadline.IsZero() { + if !time.Now().Before(deadline) { + s.dataForWriting = nil + return bytesWritten, errDeadline + } + if deadlineTimer == nil { + deadlineTimer = utils.NewTimer() + defer deadlineTimer.Stop() + } + deadlineTimer.Reset(deadline) + } + if s.dataForWriting == nil || s.cancelWriteErr != nil || s.closeForShutdownErr != nil { + break + } + } + + s.mutex.Unlock() + if !notifiedSender { + s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex + notifiedSender = true + } + if copied { + s.mutex.Lock() + break + } + if deadline.IsZero() { + <-s.writeChan + } else { + select { + case <-s.writeChan: + case <-deadlineTimer.Chan(): + deadlineTimer.SetRead() + } + } + s.mutex.Lock() + } + + if bytesWritten == len(p) { + return bytesWritten, nil + } + if s.closeForShutdownErr != nil { + return bytesWritten, s.closeForShutdownErr + } else if s.cancelWriteErr != nil { + return bytesWritten, s.cancelWriteErr + } + return bytesWritten, nil +} + +func (s *sendStream) canBufferStreamFrame() bool { + var l protocol.ByteCount + if s.nextFrame != nil { + l = s.nextFrame.DataLen() + } + return l+protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxPacketBufferSize +} + +// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream +// maxBytes is the maximum length this frame (including frame header) will have. +func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount, v protocol.Version) (af ackhandler.StreamFrame, ok, hasMore bool) { + s.mutex.Lock() + f, hasMoreData := s.popNewOrRetransmittedStreamFrame(maxBytes, v) + if f != nil { + s.numOutstandingFrames++ + } + s.mutex.Unlock() + + if f == nil { + return ackhandler.StreamFrame{}, false, hasMoreData + } + return ackhandler.StreamFrame{ + Frame: f, + Handler: (*sendStreamAckHandler)(s), + }, true, hasMoreData +} + +func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCount, v protocol.Version) (*wire.StreamFrame, bool /* has more data to send */) { + if s.cancelWriteErr != nil || s.closeForShutdownErr != nil { + return nil, false + } + + if len(s.retransmissionQueue) > 0 { + f, hasMoreRetransmissions := s.maybeGetRetransmission(maxBytes, v) + if f != nil || hasMoreRetransmissions { + if f == nil { + return nil, true + } + // We always claim that we have more data to send. + // This might be incorrect, in which case there'll be a spurious call to popStreamFrame in the future. + return f, true + } + } + + if len(s.dataForWriting) == 0 && s.nextFrame == nil { + if s.finishedWriting && !s.finSent { + s.finSent = true + return &wire.StreamFrame{ + StreamID: s.streamID, + Offset: s.writeOffset, + DataLenPresent: true, + Fin: true, + }, false + } + return nil, false + } + + sendWindow := s.flowController.SendWindowSize() + if sendWindow == 0 { + if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked { + s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{ + StreamID: s.streamID, + MaximumStreamData: offset, + }) + return nil, false + } + return nil, true + } + + f, hasMoreData := s.popNewStreamFrame(maxBytes, sendWindow, v) + if dataLen := f.DataLen(); dataLen > 0 { + s.writeOffset += f.DataLen() + s.flowController.AddBytesSent(f.DataLen()) + } + f.Fin = s.finishedWriting && s.dataForWriting == nil && s.nextFrame == nil && !s.finSent + if f.Fin { + s.finSent = true + } + return f, hasMoreData +} + +func (s *sendStream) popNewStreamFrame(maxBytes, sendWindow protocol.ByteCount, v protocol.Version) (*wire.StreamFrame, bool) { + if s.nextFrame != nil { + nextFrame := s.nextFrame + s.nextFrame = nil + + maxDataLen := min(sendWindow, nextFrame.MaxDataLen(maxBytes, v)) + if nextFrame.DataLen() > maxDataLen { + s.nextFrame = wire.GetStreamFrame() + s.nextFrame.StreamID = s.streamID + s.nextFrame.Offset = s.writeOffset + maxDataLen + s.nextFrame.Data = s.nextFrame.Data[:nextFrame.DataLen()-maxDataLen] + s.nextFrame.DataLenPresent = true + copy(s.nextFrame.Data, nextFrame.Data[maxDataLen:]) + nextFrame.Data = nextFrame.Data[:maxDataLen] + } else { + s.signalWrite() + } + return nextFrame, s.nextFrame != nil || s.dataForWriting != nil + } + + f := wire.GetStreamFrame() + f.Fin = false + f.StreamID = s.streamID + f.Offset = s.writeOffset + f.DataLenPresent = true + f.Data = f.Data[:0] + + hasMoreData := s.popNewStreamFrameWithoutBuffer(f, maxBytes, sendWindow, v) + if len(f.Data) == 0 && !f.Fin { + f.PutBack() + return nil, hasMoreData + } + return f, hasMoreData +} + +func (s *sendStream) popNewStreamFrameWithoutBuffer(f *wire.StreamFrame, maxBytes, sendWindow protocol.ByteCount, v protocol.Version) bool { + maxDataLen := f.MaxDataLen(maxBytes, v) + if maxDataLen == 0 { // a STREAM frame must have at least one byte of data + return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting + } + s.getDataForWriting(f, min(maxDataLen, sendWindow)) + + return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting +} + +func (s *sendStream) maybeGetRetransmission(maxBytes protocol.ByteCount, v protocol.Version) (*wire.StreamFrame, bool /* has more retransmissions */) { + f := s.retransmissionQueue[0] + newFrame, needsSplit := f.MaybeSplitOffFrame(maxBytes, v) + if needsSplit { + return newFrame, true + } + s.retransmissionQueue = s.retransmissionQueue[1:] + return f, len(s.retransmissionQueue) > 0 +} + +func (s *sendStream) hasData() bool { + s.mutex.Lock() + hasData := len(s.dataForWriting) > 0 + s.mutex.Unlock() + return hasData +} + +func (s *sendStream) getDataForWriting(f *wire.StreamFrame, maxBytes protocol.ByteCount) { + if protocol.ByteCount(len(s.dataForWriting)) <= maxBytes { + f.Data = f.Data[:len(s.dataForWriting)] + copy(f.Data, s.dataForWriting) + s.dataForWriting = nil + s.signalWrite() + return + } + f.Data = f.Data[:maxBytes] + copy(f.Data, s.dataForWriting) + s.dataForWriting = s.dataForWriting[maxBytes:] + if s.canBufferStreamFrame() { + s.signalWrite() + } +} + +func (s *sendStream) isNewlyCompleted() bool { + completed := (s.finSent || s.cancelWriteErr != nil) && s.numOutstandingFrames == 0 && len(s.retransmissionQueue) == 0 + if completed && !s.completed { + s.completed = true + return true + } + return false +} + +func (s *sendStream) Close() error { + s.mutex.Lock() + if s.closeForShutdownErr != nil { + s.mutex.Unlock() + return nil + } + if s.cancelWriteErr != nil { + s.mutex.Unlock() + return fmt.Errorf("close called for canceled stream %d", s.streamID) + } + s.ctxCancel(nil) + s.finishedWriting = true + s.mutex.Unlock() + + s.sender.onHasStreamData(s.streamID) // need to send the FIN, must be called without holding the mutex + return nil +} + +func (s *sendStream) CancelWrite(errorCode StreamErrorCode) { + s.cancelWriteImpl(errorCode, false) +} + +// must be called after locking the mutex +func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, remote bool) { + s.mutex.Lock() + if s.cancelWriteErr != nil { + s.mutex.Unlock() + return + } + s.cancelWriteErr = &StreamError{StreamID: s.streamID, ErrorCode: errorCode, Remote: remote} + s.ctxCancel(s.cancelWriteErr) + s.numOutstandingFrames = 0 + s.retransmissionQueue = nil + newlyCompleted := s.isNewlyCompleted() + s.mutex.Unlock() + + s.signalWrite() + s.sender.queueControlFrame(&wire.ResetStreamFrame{ + StreamID: s.streamID, + FinalSize: s.writeOffset, + ErrorCode: errorCode, + }) + if newlyCompleted { + s.sender.onStreamCompleted(s.streamID) + } +} + +func (s *sendStream) updateSendWindow(limit protocol.ByteCount) { + updated := s.flowController.UpdateSendWindow(limit) + if !updated { // duplicate or reordered MAX_STREAM_DATA frame + return + } + s.mutex.Lock() + hasStreamData := s.dataForWriting != nil || s.nextFrame != nil + s.mutex.Unlock() + if hasStreamData { + s.sender.onHasStreamData(s.streamID) + } +} + +func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) { + s.cancelWriteImpl(frame.ErrorCode, true) +} + +func (s *sendStream) Context() context.Context { + return s.ctx +} + +func (s *sendStream) SetWriteDeadline(t time.Time) error { + s.mutex.Lock() + s.deadline = t + s.mutex.Unlock() + s.signalWrite() + return nil +} + +// CloseForShutdown closes a stream abruptly. +// It makes Write unblock (and return the error) immediately. +// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. +func (s *sendStream) closeForShutdown(err error) { + s.mutex.Lock() + s.ctxCancel(err) + s.closeForShutdownErr = err + s.mutex.Unlock() + s.signalWrite() +} + +// signalWrite performs a non-blocking send on the writeChan +func (s *sendStream) signalWrite() { + select { + case s.writeChan <- struct{}{}: + default: + } +} + +type sendStreamAckHandler sendStream + +var _ ackhandler.FrameHandler = &sendStreamAckHandler{} + +func (s *sendStreamAckHandler) OnAcked(f wire.Frame) { + sf := f.(*wire.StreamFrame) + sf.PutBack() + s.mutex.Lock() + if s.cancelWriteErr != nil { + s.mutex.Unlock() + return + } + s.numOutstandingFrames-- + if s.numOutstandingFrames < 0 { + panic("numOutStandingFrames negative") + } + newlyCompleted := (*sendStream)(s).isNewlyCompleted() + s.mutex.Unlock() + + if newlyCompleted { + s.sender.onStreamCompleted(s.streamID) + } +} + +func (s *sendStreamAckHandler) OnLost(f wire.Frame) { + sf := f.(*wire.StreamFrame) + s.mutex.Lock() + if s.cancelWriteErr != nil { + s.mutex.Unlock() + return + } + sf.DataLenPresent = true + s.retransmissionQueue = append(s.retransmissionQueue, sf) + s.numOutstandingFrames-- + if s.numOutstandingFrames < 0 { + panic("numOutStandingFrames negative") + } + s.mutex.Unlock() + + s.sender.onHasStreamData(s.streamID) +} diff --git a/vendor/github.com/quic-go/quic-go/server.go b/vendor/github.com/quic-go/quic-go/server.go new file mode 100644 index 000000000..afbd18fd2 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/server.go @@ -0,0 +1,895 @@ +package quic + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/logging" +) + +// ErrServerClosed is returned by the Listener or EarlyListener's Accept method after a call to Close. +var ErrServerClosed = errors.New("quic: server closed") + +// packetHandler handles packets +type packetHandler interface { + handlePacket(receivedPacket) + destroy(error) + closeWithTransportError(qerr.TransportErrorCode) +} + +type packetHandlerManager interface { + Get(protocol.ConnectionID) (packetHandler, bool) + GetByResetToken(protocol.StatelessResetToken) (packetHandler, bool) + AddWithConnID(destConnID, newConnID protocol.ConnectionID, h packetHandler) bool + Close(error) + connRunner +} + +type quicConn interface { + EarlyConnection + earlyConnReady() <-chan struct{} + handlePacket(receivedPacket) + run() error + destroy(error) + closeWithTransportError(TransportErrorCode) +} + +type zeroRTTQueue struct { + packets []receivedPacket + expiration time.Time +} + +type rejectedPacket struct { + receivedPacket + hdr *wire.Header +} + +// A Listener of QUIC +type baseServer struct { + disableVersionNegotiation bool + acceptEarlyConns bool + + tlsConf *tls.Config + config *Config + + conn rawConn + + tokenGenerator *handshake.TokenGenerator + maxTokenAge time.Duration + + connIDGenerator ConnectionIDGenerator + connHandler packetHandlerManager + onClose func() + + receivedPackets chan receivedPacket + + nextZeroRTTCleanup time.Time + zeroRTTQueues map[protocol.ConnectionID]*zeroRTTQueue // only initialized if acceptEarlyConns == true + + // set as a member, so they can be set in the tests + newConn func( + sendConn, + connRunner, + protocol.ConnectionID, /* original dest connection ID */ + *protocol.ConnectionID, /* retry src connection ID */ + protocol.ConnectionID, /* client dest connection ID */ + protocol.ConnectionID, /* destination connection ID */ + protocol.ConnectionID, /* source connection ID */ + ConnectionIDGenerator, + protocol.StatelessResetToken, + *Config, + *tls.Config, + *handshake.TokenGenerator, + bool, /* client address validated by an address validation token */ + *logging.ConnectionTracer, + uint64, + utils.Logger, + protocol.Version, + ) quicConn + + closeMx sync.Mutex + errorChan chan struct{} // is closed when the server is closed + closeErr error + running chan struct{} // closed as soon as run() returns + + versionNegotiationQueue chan receivedPacket + invalidTokenQueue chan rejectedPacket + connectionRefusedQueue chan rejectedPacket + retryQueue chan rejectedPacket + + verifySourceAddress func(net.Addr) bool + + connQueue chan quicConn + + tracer *logging.Tracer + + logger utils.Logger +} + +// A Listener listens for incoming QUIC connections. +// It returns connections once the handshake has completed. +type Listener struct { + baseServer *baseServer +} + +// Accept returns new connections. It should be called in a loop. +func (l *Listener) Accept(ctx context.Context) (Connection, error) { + return l.baseServer.Accept(ctx) +} + +// Close closes the listener. +// Accept will return ErrServerClosed as soon as all connections in the accept queue have been accepted. +// QUIC handshakes that are still in flight will be rejected with a CONNECTION_REFUSED error. +// The effect of closing the listener depends on how it was created: +// * if it was created using Transport.Listen, already established connections will be unaffected +// * if it was created using the Listen convenience method, all established connection will be closed immediately +func (l *Listener) Close() error { + return l.baseServer.Close() +} + +// Addr returns the local network address that the server is listening on. +func (l *Listener) Addr() net.Addr { + return l.baseServer.Addr() +} + +// An EarlyListener listens for incoming QUIC connections, and returns them before the handshake completes. +// For connections that don't use 0-RTT, this allows the server to send 0.5-RTT data. +// This data is encrypted with forward-secure keys, however, the client's identity has not yet been verified. +// For connection using 0-RTT, this allows the server to accept and respond to streams that the client opened in the +// 0-RTT data it sent. Note that at this point during the handshake, the live-ness of the +// client has not yet been confirmed, and the 0-RTT data could have been replayed by an attacker. +type EarlyListener struct { + baseServer *baseServer +} + +// Accept returns a new connections. It should be called in a loop. +func (l *EarlyListener) Accept(ctx context.Context) (EarlyConnection, error) { + return l.baseServer.accept(ctx) +} + +// Close the server. All active connections will be closed. +func (l *EarlyListener) Close() error { + return l.baseServer.Close() +} + +// Addr returns the local network addr that the server is listening on. +func (l *EarlyListener) Addr() net.Addr { + return l.baseServer.Addr() +} + +// ListenAddr creates a QUIC server listening on a given address. +// See Listen for more details. +func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (*Listener, error) { + conn, err := listenUDP(addr) + if err != nil { + return nil, err + } + return (&Transport{ + Conn: conn, + createdConn: true, + isSingleUse: true, + }).Listen(tlsConf, config) +} + +// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes. +func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (*EarlyListener, error) { + conn, err := listenUDP(addr) + if err != nil { + return nil, err + } + return (&Transport{ + Conn: conn, + createdConn: true, + isSingleUse: true, + }).ListenEarly(tlsConf, config) +} + +func listenUDP(addr string) (*net.UDPConn, error) { + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + return net.ListenUDP("udp", udpAddr) +} + +// Listen listens for QUIC connections on a given net.PacketConn. +// 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. +// A single net.PacketConn can only be used for a single call to Listen. +// +// The tls.Config must not be nil and must contain a certificate configuration. +// Furthermore, it must define an application control (using NextProtos). +// The quic.Config may be nil, in that case the default values will be used. +// +// 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 outgoing QUIC connections. +// When closing a listener created with Listen, all established QUIC connections will be closed immediately. +func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*Listener, error) { + tr := &Transport{Conn: conn, isSingleUse: true} + return tr.Listen(tlsConf, config) +} + +// ListenEarly works like Listen, but it returns connections before the handshake completes. +func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*EarlyListener, error) { + tr := &Transport{Conn: conn, isSingleUse: true} + return tr.ListenEarly(tlsConf, config) +} + +func newServer( + conn rawConn, + connHandler packetHandlerManager, + connIDGenerator ConnectionIDGenerator, + tlsConf *tls.Config, + config *Config, + tracer *logging.Tracer, + onClose func(), + tokenGeneratorKey TokenGeneratorKey, + maxTokenAge time.Duration, + verifySourceAddress func(net.Addr) bool, + disableVersionNegotiation bool, + acceptEarly bool, +) *baseServer { + s := &baseServer{ + conn: conn, + tlsConf: tlsConf, + config: config, + tokenGenerator: handshake.NewTokenGenerator(tokenGeneratorKey), + maxTokenAge: maxTokenAge, + verifySourceAddress: verifySourceAddress, + connIDGenerator: connIDGenerator, + connHandler: connHandler, + connQueue: make(chan quicConn, protocol.MaxAcceptQueueSize), + errorChan: make(chan struct{}), + running: make(chan struct{}), + receivedPackets: make(chan receivedPacket, protocol.MaxServerUnprocessedPackets), + versionNegotiationQueue: make(chan receivedPacket, 4), + invalidTokenQueue: make(chan rejectedPacket, 4), + connectionRefusedQueue: make(chan rejectedPacket, 4), + retryQueue: make(chan rejectedPacket, 8), + newConn: newConnection, + tracer: tracer, + logger: utils.DefaultLogger.WithPrefix("server"), + acceptEarlyConns: acceptEarly, + disableVersionNegotiation: disableVersionNegotiation, + onClose: onClose, + } + if acceptEarly { + s.zeroRTTQueues = map[protocol.ConnectionID]*zeroRTTQueue{} + } + go s.run() + go s.runSendQueue() + s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) + return s +} + +func (s *baseServer) run() { + defer close(s.running) + for { + select { + case <-s.errorChan: + return + default: + } + select { + case <-s.errorChan: + return + case p := <-s.receivedPackets: + if bufferStillInUse := s.handlePacketImpl(p); !bufferStillInUse { + p.buffer.Release() + } + } + } +} + +func (s *baseServer) runSendQueue() { + for { + select { + case <-s.running: + return + case p := <-s.versionNegotiationQueue: + s.maybeSendVersionNegotiationPacket(p) + case p := <-s.invalidTokenQueue: + s.maybeSendInvalidToken(p) + case p := <-s.connectionRefusedQueue: + s.sendConnectionRefused(p) + case p := <-s.retryQueue: + s.sendRetry(p) + } + } +} + +// Accept returns connections that already completed the handshake. +// It is only valid if acceptEarlyConns is false. +func (s *baseServer) Accept(ctx context.Context) (Connection, error) { + return s.accept(ctx) +} + +func (s *baseServer) accept(ctx context.Context) (quicConn, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case conn := <-s.connQueue: + return conn, nil + case <-s.errorChan: + return nil, s.closeErr + } +} + +func (s *baseServer) Close() error { + s.close(ErrServerClosed, true) + return nil +} + +func (s *baseServer) close(e error, notifyOnClose bool) { + s.closeMx.Lock() + if s.closeErr != nil { + s.closeMx.Unlock() + return + } + s.closeErr = e + close(s.errorChan) + <-s.running + s.closeMx.Unlock() + + if notifyOnClose { + s.onClose() + } +} + +// Addr returns the server's network address +func (s *baseServer) Addr() net.Addr { + return s.conn.LocalAddr() +} + +func (s *baseServer) handlePacket(p receivedPacket) { + select { + case s.receivedPackets <- p: + default: + s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size()) + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) + } + } +} + +func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer still in use? */ { + if !s.nextZeroRTTCleanup.IsZero() && p.rcvTime.After(s.nextZeroRTTCleanup) { + defer s.cleanupZeroRTTQueues(p.rcvTime) + } + + if wire.IsVersionNegotiationPacket(p.data) { + s.logger.Debugf("Dropping Version Negotiation packet.") + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + // Short header packets should never end up here in the first place + if !wire.IsLongHeaderPacket(p.data[0]) { + panic(fmt.Sprintf("misrouted packet: %#v", p.data)) + } + v, err := wire.ParseVersion(p.data) + // drop the packet if we failed to parse the protocol version + if err != nil { + s.logger.Debugf("Dropping a packet with an unknown version") + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + // send a Version Negotiation Packet if the client is speaking a different protocol version + if !protocol.IsSupportedVersion(s.config.Versions, v) { + if s.disableVersionNegotiation { + return false + } + + if p.Size() < protocol.MinUnknownVersionPacketSize { + s.logger.Debugf("Dropping a packet with an unsupported version number %d that is too small (%d bytes)", v, p.Size()) + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + return s.enqueueVersionNegotiationPacket(p) + } + + if wire.Is0RTTPacket(p.data) { + if !s.acceptEarlyConns { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + return s.handle0RTTPacket(p) + } + + // If we're creating a new connection, the packet will be passed to the connection. + // The header will then be parsed again. + hdr, _, _, err := wire.ParsePacket(p.data) + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) + } + s.logger.Debugf("Error parsing packet: %s", err) + return false + } + if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize { + s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size()) + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + + if hdr.Type != protocol.PacketTypeInitial { + // Drop long header packets. + // There's little point in sending a Stateless Reset, since the client + // might not have received the token yet. + s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data)) + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + + s.logger.Debugf("<- Received Initial packet.") + + if err := s.handleInitialImpl(p, hdr); err != nil { + s.logger.Errorf("Error occurred handling initial packet: %s", err) + } + // Don't put the packet buffer back. + // handleInitialImpl deals with the buffer. + return true +} + +func (s *baseServer) handle0RTTPacket(p receivedPacket) bool { + connID, err := wire.ParseConnectionID(p.data, 0) + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropHeaderParseError) + } + return false + } + + // check again if we might have a connection now + if handler, ok := s.connHandler.Get(connID); ok { + handler.handlePacket(p) + return true + } + + if q, ok := s.zeroRTTQueues[connID]; ok { + if len(q.packets) >= protocol.Max0RTTQueueLen { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + return false + } + q.packets = append(q.packets, p) + return true + } + + if len(s.zeroRTTQueues) >= protocol.Max0RTTQueues { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + return false + } + queue := &zeroRTTQueue{packets: make([]receivedPacket, 1, 8)} + queue.packets[0] = p + expiration := p.rcvTime.Add(protocol.Max0RTTQueueingDuration) + queue.expiration = expiration + if s.nextZeroRTTCleanup.IsZero() || s.nextZeroRTTCleanup.After(expiration) { + s.nextZeroRTTCleanup = expiration + } + s.zeroRTTQueues[connID] = queue + return true +} + +func (s *baseServer) cleanupZeroRTTQueues(now time.Time) { + // Iterate over all queues to find those that are expired. + // This is ok since we're placing a pretty low limit on the number of queues. + var nextCleanup time.Time + for connID, q := range s.zeroRTTQueues { + if q.expiration.After(now) { + if nextCleanup.IsZero() || nextCleanup.After(q.expiration) { + nextCleanup = q.expiration + } + continue + } + for _, p := range q.packets { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + p.buffer.Release() + } + delete(s.zeroRTTQueues, connID) + if s.logger.Debug() { + s.logger.Debugf("Removing 0-RTT queue for %s.", connID) + } + } + s.nextZeroRTTCleanup = nextCleanup +} + +// validateToken returns false if: +// - address is invalid +// - token is expired +// - token is null +func (s *baseServer) validateToken(token *handshake.Token, addr net.Addr) bool { + if token == nil { + return false + } + if !token.ValidateRemoteAddr(addr) { + return false + } + if !token.IsRetryToken && time.Since(token.SentTime) > s.maxTokenAge { + return false + } + if token.IsRetryToken && time.Since(token.SentTime) > s.config.maxRetryTokenAge() { + return false + } + return true +} + +func (s *baseServer) handleInitialImpl(p receivedPacket, hdr *wire.Header) error { + if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) + } + p.buffer.Release() + return errors.New("too short connection ID") + } + + // The server queues packets for a while, and we might already have established a connection by now. + // This results in a second check in the connection map. + // That's ok since it's not the hot path (it's only taken by some Initial and 0-RTT packets). + if handler, ok := s.connHandler.Get(hdr.DestConnectionID); ok { + handler.handlePacket(p) + return nil + } + + var ( + token *handshake.Token + retrySrcConnID *protocol.ConnectionID + clientAddrVerified bool + ) + origDestConnID := hdr.DestConnectionID + if len(hdr.Token) > 0 { + tok, err := s.tokenGenerator.DecodeToken(hdr.Token) + if err == nil { + if tok.IsRetryToken { + origDestConnID = tok.OriginalDestConnectionID + retrySrcConnID = &tok.RetrySrcConnectionID + } + token = tok + } + } + if token != nil { + clientAddrVerified = s.validateToken(token, p.remoteAddr) + if !clientAddrVerified { + // For invalid and expired non-retry tokens, we don't send an INVALID_TOKEN error. + // We just ignore them, and act as if there was no token on this packet at all. + // This also means we might send a Retry later. + if !token.IsRetryToken { + token = nil + } else { + // For Retry tokens, we send an INVALID_ERROR if + // * the token is too old, or + // * the token is invalid, in case of a retry token. + select { + case s.invalidTokenQueue <- rejectedPacket{receivedPacket: p, hdr: hdr}: + default: + // drop packet if we can't send out the INVALID_TOKEN packets fast enough + p.buffer.Release() + } + return nil + } + } + } + + if token == nil && s.verifySourceAddress != nil && s.verifySourceAddress(p.remoteAddr) { + // Retry invalidates all 0-RTT packets sent. + delete(s.zeroRTTQueues, hdr.DestConnectionID) + select { + case s.retryQueue <- rejectedPacket{receivedPacket: p, hdr: hdr}: + default: + // drop packet if we can't send out Retry packets fast enough + p.buffer.Release() + } + return nil + } + + config := s.config + if s.config.GetConfigForClient != nil { + conf, err := s.config.GetConfigForClient(&ClientHelloInfo{ + RemoteAddr: p.remoteAddr, + AddrVerified: clientAddrVerified, + }) + if err != nil { + s.logger.Debugf("Rejecting new connection due to GetConfigForClient callback") + delete(s.zeroRTTQueues, hdr.DestConnectionID) + select { + case s.connectionRefusedQueue <- rejectedPacket{receivedPacket: p, hdr: hdr}: + default: + // drop packet if we can't send out the CONNECTION_REFUSED fast enough + p.buffer.Release() + } + return nil + } + config = populateConfig(conf) + } + + var conn quicConn + tracingID := nextConnTracingID() + var tracer *logging.ConnectionTracer + if config.Tracer != nil { + // Use the same connection ID that is passed to the client's GetLogWriter callback. + connID := hdr.DestConnectionID + if origDestConnID.Len() > 0 { + connID = origDestConnID + } + tracer = config.Tracer(context.WithValue(context.Background(), ConnectionTracingKey, tracingID), protocol.PerspectiveServer, connID) + } + connID, err := s.connIDGenerator.GenerateConnectionID() + if err != nil { + return err + } + s.logger.Debugf("Changing connection ID to %s.", connID) + conn = s.newConn( + newSendConn(s.conn, p.remoteAddr, p.info, s.logger), + s.connHandler, + origDestConnID, + retrySrcConnID, + hdr.DestConnectionID, + hdr.SrcConnectionID, + connID, + s.connIDGenerator, + s.connHandler.GetStatelessResetToken(connID), + config, + s.tlsConf, + s.tokenGenerator, + clientAddrVerified, + tracer, + tracingID, + s.logger, + hdr.Version, + ) + conn.handlePacket(p) + // Adding the connection will fail if the client's chosen Destination Connection ID is already in use. + // This is very unlikely: Even if an attacker chooses a connection ID that's already in use, + // under normal circumstances the packet would just be routed to that connection. + // The only time this collision will occur if we receive the two Initial packets at the same time. + if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, conn); !added { + delete(s.zeroRTTQueues, hdr.DestConnectionID) + conn.closeWithTransportError(qerr.ConnectionRefused) + return nil + } + // Pass queued 0-RTT to the newly established connection. + if q, ok := s.zeroRTTQueues[hdr.DestConnectionID]; ok { + for _, p := range q.packets { + conn.handlePacket(p) + } + delete(s.zeroRTTQueues, hdr.DestConnectionID) + } + + go conn.run() + go func() { + if completed := s.handleNewConn(conn); !completed { + return + } + + select { + case s.connQueue <- conn: + default: + conn.closeWithTransportError(ConnectionRefused) + } + }() + return nil +} + +func (s *baseServer) handleNewConn(conn quicConn) bool { + if s.acceptEarlyConns { + // wait until the early connection is ready, the handshake fails, or the server is closed + select { + case <-s.errorChan: + conn.closeWithTransportError(ConnectionRefused) + return false + case <-conn.Context().Done(): + return false + case <-conn.earlyConnReady(): + return true + } + } + // wait until the handshake completes, fails, or the server is closed + select { + case <-s.errorChan: + conn.closeWithTransportError(ConnectionRefused) + return false + case <-conn.Context().Done(): + return false + case <-conn.HandshakeComplete(): + return true + } +} + +func (s *baseServer) sendRetry(p rejectedPacket) { + if err := s.sendRetryPacket(p); err != nil { + s.logger.Debugf("Error sending Retry packet: %s", err) + } +} + +func (s *baseServer) sendRetryPacket(p rejectedPacket) error { + hdr := p.hdr + // Log the Initial packet now. + // If no Retry is sent, the packet will be logged by the connection. + (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) + srcConnID, err := s.connIDGenerator.GenerateConnectionID() + if err != nil { + return err + } + token, err := s.tokenGenerator.NewRetryToken(p.remoteAddr, hdr.DestConnectionID, srcConnID) + if err != nil { + return err + } + replyHdr := &wire.ExtendedHeader{} + replyHdr.Type = protocol.PacketTypeRetry + replyHdr.Version = hdr.Version + replyHdr.SrcConnectionID = srcConnID + replyHdr.DestConnectionID = hdr.SrcConnectionID + replyHdr.Token = token + if s.logger.Debug() { + s.logger.Debugf("Changing connection ID to %s.", srcConnID) + s.logger.Debugf("-> Sending Retry") + replyHdr.Log(s.logger) + } + + buf := getPacketBuffer() + defer buf.Release() + buf.Data, err = replyHdr.Append(buf.Data, hdr.Version) + if err != nil { + return err + } + // append the Retry integrity tag + tag := handshake.GetRetryIntegrityTag(buf.Data, hdr.DestConnectionID, hdr.Version) + buf.Data = append(buf.Data, tag[:]...) + if s.tracer != nil && s.tracer.SentPacket != nil { + s.tracer.SentPacket(p.remoteAddr, &replyHdr.Header, protocol.ByteCount(len(buf.Data)), nil) + } + _, err = s.conn.WritePacket(buf.Data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported) + return err +} + +func (s *baseServer) maybeSendInvalidToken(p rejectedPacket) { + defer p.buffer.Release() + + // Only send INVALID_TOKEN if we can unprotect the packet. + // This makes sure that we won't send it for packets that were corrupted. + hdr := p.hdr + sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) + data := p.data[:hdr.ParsedLen()+hdr.Length] + extHdr, err := unpackLongHeader(opener, hdr, data, hdr.Version) + // Only send INVALID_TOKEN if we can unprotect the packet. + // This makes sure that we won't send it for packets that were corrupted. + if err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) + } + return + } + hdrLen := extHdr.ParsedLen() + if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) + } + return + } + if s.logger.Debug() { + s.logger.Debugf("Client sent an invalid retry token. Sending INVALID_TOKEN to %s.", p.remoteAddr) + } + if err := s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info); err != nil { + s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err) + } +} + +func (s *baseServer) sendConnectionRefused(p rejectedPacket) { + defer p.buffer.Release() + sealer, _ := handshake.NewInitialAEAD(p.hdr.DestConnectionID, protocol.PerspectiveServer, p.hdr.Version) + if err := s.sendError(p.remoteAddr, p.hdr, sealer, qerr.ConnectionRefused, p.info); err != nil { + s.logger.Debugf("Error sending CONNECTION_REFUSED error: %s", err) + } +} + +// sendError sends the error as a response to the packet received with header hdr +func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info packetInfo) error { + b := getPacketBuffer() + defer b.Release() + + ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)} + + replyHdr := &wire.ExtendedHeader{} + replyHdr.Type = protocol.PacketTypeInitial + replyHdr.Version = hdr.Version + replyHdr.SrcConnectionID = hdr.DestConnectionID + replyHdr.DestConnectionID = hdr.SrcConnectionID + replyHdr.PacketNumberLen = protocol.PacketNumberLen4 + replyHdr.Length = 4 /* packet number len */ + ccf.Length(hdr.Version) + protocol.ByteCount(sealer.Overhead()) + var err error + b.Data, err = replyHdr.Append(b.Data, hdr.Version) + if err != nil { + return err + } + payloadOffset := len(b.Data) + + b.Data, err = ccf.Append(b.Data, hdr.Version) + if err != nil { + return err + } + + _ = sealer.Seal(b.Data[payloadOffset:payloadOffset], b.Data[payloadOffset:], replyHdr.PacketNumber, b.Data[:payloadOffset]) + b.Data = b.Data[0 : len(b.Data)+sealer.Overhead()] + + pnOffset := payloadOffset - int(replyHdr.PacketNumberLen) + sealer.EncryptHeader( + b.Data[pnOffset+4:pnOffset+4+16], + &b.Data[0], + b.Data[pnOffset:payloadOffset], + ) + + replyHdr.Log(s.logger) + wire.LogFrame(s.logger, ccf, true) + if s.tracer != nil && s.tracer.SentPacket != nil { + s.tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(b.Data)), []logging.Frame{ccf}) + } + _, err = s.conn.WritePacket(b.Data, remoteAddr, info.OOB(), 0, protocol.ECNUnsupported) + return err +} + +func (s *baseServer) enqueueVersionNegotiationPacket(p receivedPacket) (bufferInUse bool) { + select { + case s.versionNegotiationQueue <- p: + return true + default: + // it's fine to not send version negotiation packets when we are busy + } + return false +} + +func (s *baseServer) maybeSendVersionNegotiationPacket(p receivedPacket) { + defer p.buffer.Release() + + v, err := wire.ParseVersion(p.data) + if err != nil { + s.logger.Debugf("failed to parse version for sending version negotiation packet: %s", err) + return + } + + _, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data) + if err != nil { // should never happen + s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs") + if s.tracer != nil && s.tracer.DroppedPacket != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return + } + + s.logger.Debugf("Client offered version %s, sending Version Negotiation", v) + + data := wire.ComposeVersionNegotiation(dest, src, s.config.Versions) + if s.tracer != nil && s.tracer.SentVersionNegotiationPacket != nil { + s.tracer.SentVersionNegotiationPacket(p.remoteAddr, src, dest, s.config.Versions) + } + if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported); err != nil { + s.logger.Debugf("Error sending Version Negotiation: %s", err) + } +} diff --git a/vendor/github.com/quic-go/quic-go/stream.go b/vendor/github.com/quic-go/quic-go/stream.go new file mode 100644 index 000000000..ce4374d60 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/stream.go @@ -0,0 +1,146 @@ +package quic + +import ( + "net" + "os" + "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/wire" +) + +type deadlineError struct{} + +func (deadlineError) Error() string { return "deadline exceeded" } +func (deadlineError) Temporary() bool { return true } +func (deadlineError) Timeout() bool { return true } +func (deadlineError) Unwrap() error { return os.ErrDeadlineExceeded } + +var errDeadline net.Error = &deadlineError{} + +// The streamSender is notified by the stream about various events. +type streamSender interface { + queueControlFrame(wire.Frame) + onHasStreamData(protocol.StreamID) + // must be called without holding the mutex that is acquired by closeForShutdown + onStreamCompleted(protocol.StreamID) +} + +// Each of the both stream halves gets its own uniStreamSender. +// This is necessary in order to keep track when both halves have been completed. +type uniStreamSender struct { + streamSender + onStreamCompletedImpl func() +} + +func (s *uniStreamSender) queueControlFrame(f wire.Frame) { + s.streamSender.queueControlFrame(f) +} + +func (s *uniStreamSender) onHasStreamData(id protocol.StreamID) { + s.streamSender.onHasStreamData(id) +} + +func (s *uniStreamSender) onStreamCompleted(protocol.StreamID) { + s.onStreamCompletedImpl() +} + +var _ streamSender = &uniStreamSender{} + +type streamI interface { + Stream + closeForShutdown(error) + // for receiving + handleStreamFrame(*wire.StreamFrame) error + handleResetStreamFrame(*wire.ResetStreamFrame) error + getWindowUpdate() protocol.ByteCount + // for sending + hasData() bool + handleStopSendingFrame(*wire.StopSendingFrame) + popStreamFrame(maxBytes protocol.ByteCount, v protocol.Version) (ackhandler.StreamFrame, bool, bool) + updateSendWindow(protocol.ByteCount) +} + +var ( + _ receiveStreamI = (streamI)(nil) + _ sendStreamI = (streamI)(nil) +) + +// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface +// +// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually. +type stream struct { + receiveStream + sendStream + + completedMutex sync.Mutex + sender streamSender + receiveStreamCompleted bool + sendStreamCompleted bool +} + +var _ Stream = &stream{} + +// newStream creates a new Stream +func newStream(streamID protocol.StreamID, + sender streamSender, + flowController flowcontrol.StreamFlowController, +) *stream { + s := &stream{sender: sender} + senderForSendStream := &uniStreamSender{ + streamSender: sender, + onStreamCompletedImpl: func() { + s.completedMutex.Lock() + s.sendStreamCompleted = true + s.checkIfCompleted() + s.completedMutex.Unlock() + }, + } + s.sendStream = *newSendStream(streamID, senderForSendStream, flowController) + senderForReceiveStream := &uniStreamSender{ + streamSender: sender, + onStreamCompletedImpl: func() { + s.completedMutex.Lock() + s.receiveStreamCompleted = true + s.checkIfCompleted() + s.completedMutex.Unlock() + }, + } + s.receiveStream = *newReceiveStream(streamID, senderForReceiveStream, flowController) + return s +} + +// need to define StreamID() here, since both receiveStream and readStream have a StreamID() +func (s *stream) StreamID() protocol.StreamID { + // the result is same for receiveStream and sendStream + return s.sendStream.StreamID() +} + +func (s *stream) Close() error { + return s.sendStream.Close() +} + +func (s *stream) SetDeadline(t time.Time) error { + _ = s.SetReadDeadline(t) // SetReadDeadline never errors + _ = s.SetWriteDeadline(t) // SetWriteDeadline never errors + return nil +} + +// CloseForShutdown closes a stream abruptly. +// It makes Read and Write unblock (and return the error) immediately. +// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. +func (s *stream) closeForShutdown(err error) { + s.sendStream.closeForShutdown(err) + s.receiveStream.closeForShutdown(err) +} + +// checkIfCompleted is called from the uniStreamSender, when one of the stream halves is completed. +// It makes sure that the onStreamCompleted callback is only called if both receive and send side have completed. +func (s *stream) checkIfCompleted() { + if s.sendStreamCompleted && s.receiveStreamCompleted { + s.sender.onStreamCompleted(s.StreamID()) + } +} diff --git a/vendor/github.com/quic-go/quic-go/streams_map.go b/vendor/github.com/quic-go/quic-go/streams_map.go new file mode 100644 index 000000000..b1a80eb36 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/streams_map.go @@ -0,0 +1,318 @@ +package quic + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + + "github.com/quic-go/quic-go/internal/flowcontrol" + "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 streamError struct { + message string + nums []protocol.StreamNum +} + +func (e streamError) Error() string { + return e.message +} + +func convertStreamError(err error, stype protocol.StreamType, pers protocol.Perspective) error { + strError, ok := err.(streamError) + if !ok { + return err + } + ids := make([]interface{}, len(strError.nums)) + for i, num := range strError.nums { + ids[i] = num.StreamID(stype, pers) + } + return fmt.Errorf(strError.Error(), ids...) +} + +type streamOpenErr struct{ error } + +var _ net.Error = &streamOpenErr{} + +func (e streamOpenErr) Temporary() bool { return e.error == errTooManyOpenStreams } +func (streamOpenErr) Timeout() bool { return false } + +// errTooManyOpenStreams is used internally by the outgoing streams maps. +var errTooManyOpenStreams = errors.New("too many open streams") + +type streamsMap struct { + perspective protocol.Perspective + + maxIncomingBidiStreams uint64 + maxIncomingUniStreams uint64 + + sender streamSender + newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController + + mutex sync.Mutex + outgoingBidiStreams *outgoingStreamsMap[streamI] + outgoingUniStreams *outgoingStreamsMap[sendStreamI] + incomingBidiStreams *incomingStreamsMap[streamI] + incomingUniStreams *incomingStreamsMap[receiveStreamI] + reset bool +} + +var _ streamManager = &streamsMap{} + +func newStreamsMap( + sender streamSender, + newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController, + maxIncomingBidiStreams uint64, + maxIncomingUniStreams uint64, + perspective protocol.Perspective, +) streamManager { + m := &streamsMap{ + perspective: perspective, + newFlowController: newFlowController, + maxIncomingBidiStreams: maxIncomingBidiStreams, + maxIncomingUniStreams: maxIncomingUniStreams, + sender: sender, + } + m.initMaps() + return m +} + +func (m *streamsMap) initMaps() { + m.outgoingBidiStreams = newOutgoingStreamsMap( + protocol.StreamTypeBidi, + func(num protocol.StreamNum) streamI { + id := num.StreamID(protocol.StreamTypeBidi, m.perspective) + return newStream(id, m.sender, m.newFlowController(id)) + }, + m.sender.queueControlFrame, + ) + m.incomingBidiStreams = newIncomingStreamsMap( + protocol.StreamTypeBidi, + func(num protocol.StreamNum) streamI { + id := num.StreamID(protocol.StreamTypeBidi, m.perspective.Opposite()) + return newStream(id, m.sender, m.newFlowController(id)) + }, + m.maxIncomingBidiStreams, + m.sender.queueControlFrame, + ) + m.outgoingUniStreams = newOutgoingStreamsMap( + protocol.StreamTypeUni, + func(num protocol.StreamNum) sendStreamI { + id := num.StreamID(protocol.StreamTypeUni, m.perspective) + return newSendStream(id, m.sender, m.newFlowController(id)) + }, + m.sender.queueControlFrame, + ) + m.incomingUniStreams = newIncomingStreamsMap( + protocol.StreamTypeUni, + func(num protocol.StreamNum) receiveStreamI { + id := num.StreamID(protocol.StreamTypeUni, m.perspective.Opposite()) + return newReceiveStream(id, m.sender, m.newFlowController(id)) + }, + m.maxIncomingUniStreams, + m.sender.queueControlFrame, + ) +} + +func (m *streamsMap) OpenStream() (Stream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.outgoingBidiStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.OpenStream() + return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) +} + +func (m *streamsMap) OpenStreamSync(ctx context.Context) (Stream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.outgoingBidiStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.OpenStreamSync(ctx) + return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) +} + +func (m *streamsMap) OpenUniStream() (SendStream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.outgoingUniStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.OpenStream() + return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) +} + +func (m *streamsMap) OpenUniStreamSync(ctx context.Context) (SendStream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.outgoingUniStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.OpenStreamSync(ctx) + return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) +} + +func (m *streamsMap) AcceptStream(ctx context.Context) (Stream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.incomingBidiStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.AcceptStream(ctx) + return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective.Opposite()) +} + +func (m *streamsMap) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { + m.mutex.Lock() + reset := m.reset + mm := m.incomingUniStreams + m.mutex.Unlock() + if reset { + return nil, Err0RTTRejected + } + str, err := mm.AcceptStream(ctx) + return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective.Opposite()) +} + +func (m *streamsMap) DeleteStream(id protocol.StreamID) error { + num := id.StreamNum() + switch id.Type() { + case protocol.StreamTypeUni: + if id.InitiatedBy() == m.perspective { + return convertStreamError(m.outgoingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective) + } + return convertStreamError(m.incomingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective.Opposite()) + case protocol.StreamTypeBidi: + if id.InitiatedBy() == m.perspective { + return convertStreamError(m.outgoingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective) + } + return convertStreamError(m.incomingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective.Opposite()) + } + panic("") +} + +func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) { + str, err := m.getOrOpenReceiveStream(id) + if err != nil { + return nil, &qerr.TransportError{ + ErrorCode: qerr.StreamStateError, + ErrorMessage: err.Error(), + } + } + return str, nil +} + +func (m *streamsMap) getOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) { + num := id.StreamNum() + switch id.Type() { + case protocol.StreamTypeUni: + if id.InitiatedBy() == m.perspective { + // an outgoing unidirectional stream is a send stream, not a receive stream + return nil, fmt.Errorf("peer attempted to open receive stream %d", id) + } + str, err := m.incomingUniStreams.GetOrOpenStream(num) + return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) + case protocol.StreamTypeBidi: + var str receiveStreamI + var err error + if id.InitiatedBy() == m.perspective { + str, err = m.outgoingBidiStreams.GetStream(num) + } else { + str, err = m.incomingBidiStreams.GetOrOpenStream(num) + } + return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy()) + } + panic("") +} + +func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) { + str, err := m.getOrOpenSendStream(id) + if err != nil { + return nil, &qerr.TransportError{ + ErrorCode: qerr.StreamStateError, + ErrorMessage: err.Error(), + } + } + return str, nil +} + +func (m *streamsMap) getOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) { + num := id.StreamNum() + switch id.Type() { + case protocol.StreamTypeUni: + if id.InitiatedBy() == m.perspective { + str, err := m.outgoingUniStreams.GetStream(num) + return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) + } + // an incoming unidirectional stream is a receive stream, not a send stream + return nil, fmt.Errorf("peer attempted to open send stream %d", id) + case protocol.StreamTypeBidi: + var str sendStreamI + var err error + if id.InitiatedBy() == m.perspective { + str, err = m.outgoingBidiStreams.GetStream(num) + } else { + str, err = m.incomingBidiStreams.GetOrOpenStream(num) + } + return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy()) + } + panic("") +} + +func (m *streamsMap) HandleMaxStreamsFrame(f *wire.MaxStreamsFrame) { + switch f.Type { + case protocol.StreamTypeUni: + m.outgoingUniStreams.SetMaxStream(f.MaxStreamNum) + case protocol.StreamTypeBidi: + m.outgoingBidiStreams.SetMaxStream(f.MaxStreamNum) + } +} + +func (m *streamsMap) UpdateLimits(p *wire.TransportParameters) { + m.outgoingBidiStreams.UpdateSendWindow(p.InitialMaxStreamDataBidiRemote) + m.outgoingBidiStreams.SetMaxStream(p.MaxBidiStreamNum) + m.outgoingUniStreams.UpdateSendWindow(p.InitialMaxStreamDataUni) + m.outgoingUniStreams.SetMaxStream(p.MaxUniStreamNum) +} + +func (m *streamsMap) CloseWithError(err error) { + m.outgoingBidiStreams.CloseWithError(err) + m.outgoingUniStreams.CloseWithError(err) + m.incomingBidiStreams.CloseWithError(err) + m.incomingUniStreams.CloseWithError(err) +} + +// ResetFor0RTT resets is used when 0-RTT is rejected. In that case, the streams maps are +// 1. closed with an Err0RTTRejected, making calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream return that error. +// 2. reset to their initial state, such that we can immediately process new incoming stream data. +// Afterwards, calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream will continue to return the error, +// until UseResetMaps() has been called. +func (m *streamsMap) ResetFor0RTT() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.reset = true + m.CloseWithError(Err0RTTRejected) + m.initMaps() +} + +func (m *streamsMap) UseResetMaps() { + m.mutex.Lock() + m.reset = false + m.mutex.Unlock() +} diff --git a/vendor/github.com/quic-go/quic-go/streams_map_incoming.go b/vendor/github.com/quic-go/quic-go/streams_map_incoming.go new file mode 100644 index 000000000..18ec6f998 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/streams_map_incoming.go @@ -0,0 +1,195 @@ +package quic + +import ( + "context" + "sync" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +type incomingStream interface { + closeForShutdown(error) +} + +// When a stream is deleted before it was accepted, we can't delete it from the map immediately. +// We need to wait until the application accepts it, and delete it then. +type incomingStreamEntry[T incomingStream] struct { + stream T + shouldDelete bool +} + +type incomingStreamsMap[T incomingStream] struct { + mutex sync.RWMutex + newStreamChan chan struct{} + + streamType protocol.StreamType + streams map[protocol.StreamNum]incomingStreamEntry[T] + + nextStreamToAccept protocol.StreamNum // the next stream that will be returned by AcceptStream() + nextStreamToOpen protocol.StreamNum // the highest stream that the peer opened + maxStream protocol.StreamNum // the highest stream that the peer is allowed to open + maxNumStreams uint64 // maximum number of streams + + newStream func(protocol.StreamNum) T + queueMaxStreamID func(*wire.MaxStreamsFrame) + + closeErr error +} + +func newIncomingStreamsMap[T incomingStream]( + streamType protocol.StreamType, + newStream func(protocol.StreamNum) T, + maxStreams uint64, + queueControlFrame func(wire.Frame), +) *incomingStreamsMap[T] { + return &incomingStreamsMap[T]{ + newStreamChan: make(chan struct{}, 1), + streamType: streamType, + streams: make(map[protocol.StreamNum]incomingStreamEntry[T]), + maxStream: protocol.StreamNum(maxStreams), + maxNumStreams: maxStreams, + newStream: newStream, + nextStreamToOpen: 1, + nextStreamToAccept: 1, + queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, + } +} + +func (m *incomingStreamsMap[T]) AcceptStream(ctx context.Context) (T, error) { + // drain the newStreamChan, so we don't check the map twice if the stream doesn't exist + select { + case <-m.newStreamChan: + default: + } + + m.mutex.Lock() + + var num protocol.StreamNum + var entry incomingStreamEntry[T] + for { + num = m.nextStreamToAccept + if m.closeErr != nil { + m.mutex.Unlock() + return *new(T), m.closeErr + } + var ok bool + entry, ok = m.streams[num] + if ok { + break + } + m.mutex.Unlock() + select { + case <-ctx.Done(): + return *new(T), ctx.Err() + case <-m.newStreamChan: + } + m.mutex.Lock() + } + m.nextStreamToAccept++ + // If this stream was completed before being accepted, we can delete it now. + if entry.shouldDelete { + if err := m.deleteStream(num); err != nil { + m.mutex.Unlock() + return *new(T), err + } + } + m.mutex.Unlock() + return entry.stream, nil +} + +func (m *incomingStreamsMap[T]) GetOrOpenStream(num protocol.StreamNum) (T, error) { + m.mutex.RLock() + if num > m.maxStream { + m.mutex.RUnlock() + return *new(T), streamError{ + message: "peer tried to open stream %d (current limit: %d)", + nums: []protocol.StreamNum{num, m.maxStream}, + } + } + // if the num is smaller than the highest we accepted + // * this stream exists in the map, and we can return it, or + // * this stream was already closed, then we can return the nil + if num < m.nextStreamToOpen { + var s T + // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. + if entry, ok := m.streams[num]; ok && !entry.shouldDelete { + s = entry.stream + } + m.mutex.RUnlock() + return s, nil + } + m.mutex.RUnlock() + + m.mutex.Lock() + // no need to check the two error conditions from above again + // * maxStream can only increase, so if the id was valid before, it definitely is valid now + // * highestStream is only modified by this function + for newNum := m.nextStreamToOpen; newNum <= num; newNum++ { + m.streams[newNum] = incomingStreamEntry[T]{stream: m.newStream(newNum)} + select { + case m.newStreamChan <- struct{}{}: + default: + } + } + m.nextStreamToOpen = num + 1 + entry := m.streams[num] + m.mutex.Unlock() + return entry.stream, nil +} + +func (m *incomingStreamsMap[T]) DeleteStream(num protocol.StreamNum) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + return m.deleteStream(num) +} + +func (m *incomingStreamsMap[T]) deleteStream(num protocol.StreamNum) error { + if _, ok := m.streams[num]; !ok { + return streamError{ + message: "tried to delete unknown incoming stream %d", + nums: []protocol.StreamNum{num}, + } + } + + // Don't delete this stream yet, if it was not yet accepted. + // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. + if num >= m.nextStreamToAccept { + entry, ok := m.streams[num] + if ok && entry.shouldDelete { + return streamError{ + message: "tried to delete incoming stream %d multiple times", + nums: []protocol.StreamNum{num}, + } + } + entry.shouldDelete = true + m.streams[num] = entry // can't assign to struct in map, so we need to reassign + return nil + } + + delete(m.streams, num) + // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream + if m.maxNumStreams > uint64(len(m.streams)) { + maxStream := m.nextStreamToOpen + protocol.StreamNum(m.maxNumStreams-uint64(len(m.streams))) - 1 + // Never send a value larger than protocol.MaxStreamCount. + if maxStream <= protocol.MaxStreamCount { + m.maxStream = maxStream + m.queueMaxStreamID(&wire.MaxStreamsFrame{ + Type: m.streamType, + MaxStreamNum: m.maxStream, + }) + } + } + return nil +} + +func (m *incomingStreamsMap[T]) CloseWithError(err error) { + m.mutex.Lock() + m.closeErr = err + for _, entry := range m.streams { + entry.stream.closeForShutdown(err) + } + m.mutex.Unlock() + close(m.newStreamChan) +} diff --git a/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go b/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go new file mode 100644 index 000000000..fd45f4e7c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go @@ -0,0 +1,230 @@ +package quic + +import ( + "context" + "sync" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +type outgoingStream interface { + updateSendWindow(protocol.ByteCount) + closeForShutdown(error) +} + +type outgoingStreamsMap[T outgoingStream] struct { + mutex sync.RWMutex + + streamType protocol.StreamType + streams map[protocol.StreamNum]T + + openQueue map[uint64]chan struct{} + lowestInQueue uint64 + highestInQueue uint64 + + nextStream protocol.StreamNum // stream ID of the stream returned by OpenStream(Sync) + maxStream protocol.StreamNum // the maximum stream ID we're allowed to open + blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream + + newStream func(protocol.StreamNum) T + queueStreamIDBlocked func(*wire.StreamsBlockedFrame) + + closeErr error +} + +func newOutgoingStreamsMap[T outgoingStream]( + streamType protocol.StreamType, + newStream func(protocol.StreamNum) T, + queueControlFrame func(wire.Frame), +) *outgoingStreamsMap[T] { + return &outgoingStreamsMap[T]{ + streamType: streamType, + streams: make(map[protocol.StreamNum]T), + openQueue: make(map[uint64]chan struct{}), + maxStream: protocol.InvalidStreamNum, + nextStream: 1, + newStream: newStream, + queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, + } +} + +func (m *outgoingStreamsMap[T]) OpenStream() (T, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.closeErr != nil { + return *new(T), m.closeErr + } + + // if there are OpenStreamSync calls waiting, return an error here + if len(m.openQueue) > 0 || m.nextStream > m.maxStream { + m.maybeSendBlockedFrame() + return *new(T), streamOpenErr{errTooManyOpenStreams} + } + return m.openStream(), nil +} + +func (m *outgoingStreamsMap[T]) OpenStreamSync(ctx context.Context) (T, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.closeErr != nil { + return *new(T), m.closeErr + } + + if err := ctx.Err(); err != nil { + return *new(T), err + } + + if len(m.openQueue) == 0 && m.nextStream <= m.maxStream { + return m.openStream(), nil + } + + waitChan := make(chan struct{}, 1) + queuePos := m.highestInQueue + m.highestInQueue++ + if len(m.openQueue) == 0 { + m.lowestInQueue = queuePos + } + m.openQueue[queuePos] = waitChan + m.maybeSendBlockedFrame() + + for { + m.mutex.Unlock() + select { + case <-ctx.Done(): + m.mutex.Lock() + delete(m.openQueue, queuePos) + return *new(T), ctx.Err() + case <-waitChan: + } + m.mutex.Lock() + + if m.closeErr != nil { + return *new(T), m.closeErr + } + if m.nextStream > m.maxStream { + // no stream available. Continue waiting + continue + } + str := m.openStream() + delete(m.openQueue, queuePos) + m.lowestInQueue = queuePos + 1 + m.unblockOpenSync() + return str, nil + } +} + +func (m *outgoingStreamsMap[T]) openStream() T { + s := m.newStream(m.nextStream) + m.streams[m.nextStream] = s + m.nextStream++ + return s +} + +// maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset, +// if we haven't sent one for this offset yet +func (m *outgoingStreamsMap[T]) maybeSendBlockedFrame() { + if m.blockedSent { + return + } + + var streamNum protocol.StreamNum + if m.maxStream != protocol.InvalidStreamNum { + streamNum = m.maxStream + } + m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ + Type: m.streamType, + StreamLimit: streamNum, + }) + m.blockedSent = true +} + +func (m *outgoingStreamsMap[T]) GetStream(num protocol.StreamNum) (T, error) { + m.mutex.RLock() + if num >= m.nextStream { + m.mutex.RUnlock() + return *new(T), streamError{ + message: "peer attempted to open stream %d", + nums: []protocol.StreamNum{num}, + } + } + s := m.streams[num] + m.mutex.RUnlock() + return s, nil +} + +func (m *outgoingStreamsMap[T]) DeleteStream(num protocol.StreamNum) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + if _, ok := m.streams[num]; !ok { + return streamError{ + message: "tried to delete unknown outgoing stream %d", + nums: []protocol.StreamNum{num}, + } + } + delete(m.streams, num) + return nil +} + +func (m *outgoingStreamsMap[T]) SetMaxStream(num protocol.StreamNum) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if num <= m.maxStream { + return + } + m.maxStream = num + m.blockedSent = false + if m.maxStream < m.nextStream-1+protocol.StreamNum(len(m.openQueue)) { + m.maybeSendBlockedFrame() + } + m.unblockOpenSync() +} + +// UpdateSendWindow is called when the peer's transport parameters are received. +// Only in the case of a 0-RTT handshake will we have open streams at this point. +// We might need to update the send window, in case the server increased it. +func (m *outgoingStreamsMap[T]) UpdateSendWindow(limit protocol.ByteCount) { + m.mutex.Lock() + for _, str := range m.streams { + str.updateSendWindow(limit) + } + m.mutex.Unlock() +} + +// unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream +func (m *outgoingStreamsMap[T]) unblockOpenSync() { + if len(m.openQueue) == 0 { + return + } + for qp := m.lowestInQueue; qp <= m.highestInQueue; qp++ { + c, ok := m.openQueue[qp] + if !ok { // entry was deleted because the context was canceled + continue + } + // unblockOpenSync is called both from OpenStreamSync and from SetMaxStream. + // It's sufficient to only unblock OpenStreamSync once. + select { + case c <- struct{}{}: + default: + } + return + } +} + +func (m *outgoingStreamsMap[T]) CloseWithError(err error) { + m.mutex.Lock() + m.closeErr = err + for _, str := range m.streams { + str.closeForShutdown(err) + } + for _, c := range m.openQueue { + if c != nil { + close(c) + } + } + m.mutex.Unlock() +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn.go b/vendor/github.com/quic-go/quic-go/sys_conn.go new file mode 100644 index 000000000..71cc46070 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn.go @@ -0,0 +1,117 @@ +package quic + +import ( + "log" + "net" + "os" + "strconv" + "strings" + "syscall" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +// OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header. +// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it. +// In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets. +type OOBCapablePacketConn interface { + net.PacketConn + SyscallConn() (syscall.RawConn, error) + SetReadBuffer(int) error + ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) + WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) +} + +var _ OOBCapablePacketConn = &net.UDPConn{} + +func wrapConn(pc net.PacketConn) (rawConn, error) { + if err := setReceiveBuffer(pc); err != nil { + if !strings.Contains(err.Error(), "use of closed network connection") { + setBufferWarningOnce.Do(func() { + if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { + return + } + log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.", err) + }) + } + } + if err := setSendBuffer(pc); err != nil { + if !strings.Contains(err.Error(), "use of closed network connection") { + setBufferWarningOnce.Do(func() { + if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { + return + } + log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.", err) + }) + } + } + + conn, ok := pc.(interface { + SyscallConn() (syscall.RawConn, error) + }) + var supportsDF bool + if ok { + rawConn, err := conn.SyscallConn() + if err != nil { + return nil, err + } + + if _, ok := pc.LocalAddr().(*net.UDPAddr); ok { + // Only set DF on sockets that we expect to be able to handle that configuration. + var err error + supportsDF, err = setDF(rawConn) + if err != nil { + return nil, err + } + } + } + c, ok := pc.(OOBCapablePacketConn) + if !ok { + utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.") + return &basicConn{PacketConn: pc, supportsDF: supportsDF}, nil + } + return newConn(c, supportsDF) +} + +// The basicConn is the most trivial implementation of a rawConn. +// It reads a single packet from the underlying net.PacketConn. +// It is used when +// * the net.PacketConn is not a OOBCapablePacketConn, and +// * when the OS doesn't support OOB. +type basicConn struct { + net.PacketConn + supportsDF bool +} + +var _ rawConn = &basicConn{} + +func (c *basicConn) ReadPacket() (receivedPacket, error) { + buffer := getPacketBuffer() + // The packet size should not exceed protocol.MaxPacketBufferSize bytes + // If it does, we only read a truncated packet, which will then end up undecryptable + buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize] + n, addr, err := c.PacketConn.ReadFrom(buffer.Data) + if err != nil { + return receivedPacket{}, err + } + return receivedPacket{ + remoteAddr: addr, + rcvTime: time.Now(), + data: buffer.Data[:n], + buffer: buffer, + }, nil +} + +func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte, gsoSize uint16, ecn protocol.ECN) (n int, err error) { + if gsoSize != 0 { + panic("cannot use GSO with a basicConn") + } + if ecn != protocol.ECNUnsupported { + panic("cannot use ECN with a basicConn") + } + return c.PacketConn.WriteTo(b, addr) +} + +func (c *basicConn) capabilities() connCapabilities { return connCapabilities{DF: c.supportsDF} } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go b/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go new file mode 100644 index 000000000..8fe49162c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go @@ -0,0 +1,68 @@ +package quic + +import ( + "errors" + "fmt" + "net" + "syscall" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +//go:generate sh -c "echo '// Code generated by go generate. DO NOT EDIT.\n// Source: sys_conn_buffers.go\n' > sys_conn_buffers_write.go && sed -e 's/SetReadBuffer/SetWriteBuffer/g' -e 's/setReceiveBuffer/setSendBuffer/g' -e 's/inspectReadBuffer/inspectWriteBuffer/g' -e 's/protocol\\.DesiredReceiveBufferSize/protocol\\.DesiredSendBufferSize/g' -e 's/forceSetReceiveBuffer/forceSetSendBuffer/g' -e 's/receive buffer/send buffer/g' sys_conn_buffers.go | sed '/^\\/\\/go:generate/d' >> sys_conn_buffers_write.go" +func setReceiveBuffer(c net.PacketConn) error { + conn, ok := c.(interface{ SetReadBuffer(int) error }) + if !ok { + return errors.New("connection doesn't allow setting of receive buffer size. Not a *net.UDPConn?") + } + + var syscallConn syscall.RawConn + if sc, ok := c.(interface { + SyscallConn() (syscall.RawConn, error) + }); ok { + var err error + syscallConn, err = sc.SyscallConn() + if err != nil { + syscallConn = nil + } + } + // The connection has a SetReadBuffer method, but we couldn't obtain a syscall.RawConn. + // This shouldn't happen for a net.UDPConn, but is possible if the connection just implements the + // net.PacketConn interface and the SetReadBuffer method. + // We have no way of checking if increasing the buffer size actually worked. + if syscallConn == nil { + return conn.SetReadBuffer(protocol.DesiredReceiveBufferSize) + } + + size, err := inspectReadBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + if size >= protocol.DesiredReceiveBufferSize { + utils.DefaultLogger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024) + return nil + } + // Ignore the error. We check if we succeeded by querying the buffer size afterward. + _ = conn.SetReadBuffer(protocol.DesiredReceiveBufferSize) + newSize, err := inspectReadBuffer(syscallConn) + if newSize < protocol.DesiredReceiveBufferSize { + // Try again with RCVBUFFORCE on Linux + _ = forceSetReceiveBuffer(syscallConn, protocol.DesiredReceiveBufferSize) + newSize, err = inspectReadBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + } + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + if newSize == size { + return fmt.Errorf("failed to increase receive buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredReceiveBufferSize/1024, newSize/1024) + } + if newSize < protocol.DesiredReceiveBufferSize { + return fmt.Errorf("failed to sufficiently increase receive buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024, newSize/1024) + } + utils.DefaultLogger.Debugf("Increased receive buffer size to %d kiB", newSize/1024) + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go b/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go new file mode 100644 index 000000000..c01a931b5 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go @@ -0,0 +1,70 @@ +// Code generated by go generate. DO NOT EDIT. +// Source: sys_conn_buffers.go + +package quic + +import ( + "errors" + "fmt" + "net" + "syscall" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +func setSendBuffer(c net.PacketConn) error { + conn, ok := c.(interface{ SetWriteBuffer(int) error }) + if !ok { + return errors.New("connection doesn't allow setting of send buffer size. Not a *net.UDPConn?") + } + + var syscallConn syscall.RawConn + if sc, ok := c.(interface { + SyscallConn() (syscall.RawConn, error) + }); ok { + var err error + syscallConn, err = sc.SyscallConn() + if err != nil { + syscallConn = nil + } + } + // The connection has a SetWriteBuffer method, but we couldn't obtain a syscall.RawConn. + // This shouldn't happen for a net.UDPConn, but is possible if the connection just implements the + // net.PacketConn interface and the SetWriteBuffer method. + // We have no way of checking if increasing the buffer size actually worked. + if syscallConn == nil { + return conn.SetWriteBuffer(protocol.DesiredSendBufferSize) + } + + size, err := inspectWriteBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + if size >= protocol.DesiredSendBufferSize { + utils.DefaultLogger.Debugf("Conn has send buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024) + return nil + } + // Ignore the error. We check if we succeeded by querying the buffer size afterward. + _ = conn.SetWriteBuffer(protocol.DesiredSendBufferSize) + newSize, err := inspectWriteBuffer(syscallConn) + if newSize < protocol.DesiredSendBufferSize { + // Try again with RCVBUFFORCE on Linux + _ = forceSetSendBuffer(syscallConn, protocol.DesiredSendBufferSize) + newSize, err = inspectWriteBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + } + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + if newSize == size { + return fmt.Errorf("failed to increase send buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredSendBufferSize/1024, newSize/1024) + } + if newSize < protocol.DesiredSendBufferSize { + return fmt.Errorf("failed to sufficiently increase send buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024, newSize/1024) + } + utils.DefaultLogger.Debugf("Increased send buffer size to %d kiB", newSize/1024) + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df.go b/vendor/github.com/quic-go/quic-go/sys_conn_df.go new file mode 100644 index 000000000..0db61509d --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df.go @@ -0,0 +1,22 @@ +//go:build !linux && !windows && !darwin + +package quic + +import ( + "syscall" +) + +func setDF(syscall.RawConn) (bool, error) { + // no-op on unsupported platforms + return false, nil +} + +func isSendMsgSizeErr(err error) bool { + // to be implemented for more specific platforms + return false +} + +func isRecvMsgSizeErr(err error) bool { + // to be implemented for more specific platforms + return false +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go new file mode 100644 index 000000000..b51cd8f1a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go @@ -0,0 +1,74 @@ +//go:build darwin + +package quic + +import ( + "errors" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/quic-go/quic-go/internal/utils" +) + +func setDF(rawConn syscall.RawConn) (bool, error) { + // Setting DF bit is only supported from macOS11 + // https://github.com/chromium/chromium/blob/117.0.5881.2/net/socket/udp_socket_posix.cc#L555 + if supportsDF, err := isAtLeastMacOS11(); !supportsDF || err != nil { + return false, err + } + + // Enabling IP_DONTFRAG will force the kernel to return "sendto: message too long" + // and the datagram will not be fragmented + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_DONTFRAG, 1) + errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_DONTFRAG, 1) + }); err != nil { + return false, err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + // On macOS, the syscall for setting DF bit for IPv4 fails on dual-stack listeners. + // Treat the connection as not having DF enabled, even though the DF bit will be set + // when used for IPv6. + // See https://github.com/quic-go/quic-go/issues/3793 for details. + return false, nil + case errDFIPv4 != nil && errDFIPv6 != nil: + return false, errors.New("setting DF failed for both IPv4 and IPv6") + } + return true, nil +} + +func isSendMsgSizeErr(err error) bool { + return errors.Is(err, unix.EMSGSIZE) +} + +func isRecvMsgSizeErr(error) bool { return false } + +func isAtLeastMacOS11() (bool, error) { + uname := &unix.Utsname{} + err := unix.Uname(uname) + if err != nil { + return false, err + } + + release := string(uname.Release[:]) + if idx := strings.Index(release, "."); idx != -1 { + version, err := strconv.Atoi(release[:idx]) + if err != nil { + return false, err + } + // Darwin version 20 is macOS version 11 + // https://en.wikipedia.org/wiki/Darwin_(operating_system)#Darwin_20_onwards + return version >= 20, nil + } + return false, nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go new file mode 100644 index 000000000..f09eaa5df --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go @@ -0,0 +1,42 @@ +//go:build linux + +package quic + +import ( + "errors" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/quic-go/quic-go/internal/utils" +) + +func setDF(rawConn syscall.RawConn) (bool, error) { + // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" + // and the datagram will not be fragmented + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO) + errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO) + }); err != nil { + return false, err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + return false, errors.New("setting DF failed for both IPv4 and IPv6") + } + return true, nil +} + +func isSendMsgSizeErr(err error) bool { + // https://man7.org/linux/man-pages/man7/udp.7.html + return errors.Is(err, unix.EMSGSIZE) +} + +func isRecvMsgSizeErr(error) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go new file mode 100644 index 000000000..e27635ec9 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go @@ -0,0 +1,54 @@ +//go:build windows + +package quic + +import ( + "errors" + "syscall" + + "golang.org/x/sys/windows" + + "github.com/quic-go/quic-go/internal/utils" +) + +const ( + // IP_DONTFRAGMENT controls the Don't Fragment (DF) bit. + // + // It's the same code point for both IPv4 and IPv6 on Windows. + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html + // + //nolint:stylecheck + IP_DONTFRAGMENT = 14 +) + +func setDF(rawConn syscall.RawConn) (bool, error) { + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) + errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IP_DONTFRAGMENT, 1) + }); err != nil { + return false, err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + return false, errors.New("setting DF failed for both IPv4 and IPv6") + } + return true, nil +} + +func isSendMsgSizeErr(err error) bool { + // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + return errors.Is(err, windows.WSAEMSGSIZE) +} + +func isRecvMsgSizeErr(err error) bool { + // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + return errors.Is(err, windows.WSAEMSGSIZE) +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go new file mode 100644 index 000000000..d761072f2 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go @@ -0,0 +1,36 @@ +//go:build darwin + +package quic + +import ( + "encoding/binary" + "net/netip" + "syscall" + + "golang.org/x/sys/unix" +) + +const ( + msgTypeIPTOS = unix.IP_RECVTOS + ipv4PKTINFO = unix.IP_RECVPKTINFO +) + +const ecnIPv4DataLen = 4 + +// ReadBatch only returns a single packet on OSX, +// see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch. +const batchSize = 1 + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) { + // struct in_pktinfo { + // unsigned int ipi_ifindex; /* Interface index */ + // struct in_addr ipi_spec_dst; /* Local address */ + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 12 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true +} + +func isGSOSupported(syscall.RawConn) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go new file mode 100644 index 000000000..a53ca2eae --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go @@ -0,0 +1,31 @@ +//go:build freebsd + +package quic + +import ( + "net/netip" + "syscall" + + "golang.org/x/sys/unix" +) + +const ( + msgTypeIPTOS = unix.IP_RECVTOS + ipv4PKTINFO = 0x7 +) + +const ecnIPv4DataLen = 1 + +const batchSize = 8 + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, _ uint32, ok bool) { + // struct in_pktinfo { + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 4 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body)), 0, true +} + +func isGSOSupported(syscall.RawConn) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go new file mode 100644 index 000000000..5fbf34ade --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go @@ -0,0 +1,110 @@ +//go:build linux + +package quic + +import ( + "encoding/binary" + "errors" + "net/netip" + "os" + "strconv" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + msgTypeIPTOS = unix.IP_TOS + ipv4PKTINFO = unix.IP_PKTINFO +) + +const ecnIPv4DataLen = 1 + +const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed) + +func forceSetReceiveBuffer(c syscall.RawConn, bytes int) error { + var serr error + if err := c.Control(func(fd uintptr) { + serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes) + }); err != nil { + return err + } + return serr +} + +func forceSetSendBuffer(c syscall.RawConn, bytes int) error { + var serr error + if err := c.Control(func(fd uintptr) { + serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes) + }); err != nil { + return err + } + return serr +} + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) { + // struct in_pktinfo { + // unsigned int ipi_ifindex; /* Interface index */ + // struct in_addr ipi_spec_dst; /* Local address */ + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 12 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true +} + +// isGSOSupported tests if the kernel supports GSO. +// Sending with GSO might still fail later on, if the interface doesn't support it (see isGSOError). +func isGSOSupported(conn syscall.RawConn) bool { + disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_GSO")) + if err == nil && disabled { + return false + } + var serr error + if err := conn.Control(func(fd uintptr) { + _, serr = unix.GetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT) + }); err != nil { + return false + } + return serr == nil +} + +func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte { + startLen := len(b) + const dataLen = 2 // payload is a uint16 + b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_UDP + h.Type = unix.UDP_SEGMENT + h.SetLen(unix.CmsgLen(dataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + *(*uint16)(unsafe.Pointer(&b[offset])) = size + return b +} + +func isGSOError(err error) bool { + var serr *os.SyscallError + if errors.As(err, &serr) { + // EIO is returned by udp_send_skb() if the device driver does not have tx checksums enabled, + // which is a hard requirement of UDP_SEGMENT. See: + // https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man7/udp.7?id=806eabd74910447f21005160e90957bde4db0183#n228 + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.2&id=c9c3395d5e3dcc6daee66c6908354d47bf98cb0c#n942 + return serr.Err == unix.EIO + } + return false +} + +// The first sendmsg call on a new UDP socket sometimes errors on Linux. +// It's not clear why this happens. +// See https://github.com/golang/go/issues/63322. +func isPermissionError(err error) bool { + var serr *os.SyscallError + if errors.As(err, &serr) { + return serr.Syscall == "sendmsg" && serr.Err == unix.EPERM + } + return false +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go new file mode 100644 index 000000000..f8d69803b --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go @@ -0,0 +1,10 @@ +//go:build !linux + +package quic + +func forceSetReceiveBuffer(c any, bytes int) error { return nil } +func forceSetSendBuffer(c any, bytes int) error { return nil } + +func appendUDPSegmentSizeMsg([]byte, uint16) []byte { return nil } +func isGSOError(error) bool { return false } +func isPermissionError(err error) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go new file mode 100644 index 000000000..2a1f807ef --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go @@ -0,0 +1,21 @@ +//go:build !darwin && !linux && !freebsd && !windows + +package quic + +import ( + "net" + "net/netip" +) + +func newConn(c net.PacketConn, supportsDF bool) (*basicConn, error) { + return &basicConn{PacketConn: c, supportsDF: supportsDF}, nil +} + +func inspectReadBuffer(any) (int, error) { return 0, nil } +func inspectWriteBuffer(any) (int, error) { return 0, nil } + +type packetInfo struct { + addr netip.Addr +} + +func (i *packetInfo) OOB() []byte { return nil } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go new file mode 100644 index 000000000..64d581c06 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go @@ -0,0 +1,331 @@ +//go:build darwin || linux || freebsd + +package quic + +import ( + "encoding/binary" + "errors" + "log" + "net" + "net/netip" + "os" + "strconv" + "sync" + "syscall" + "time" + "unsafe" + + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +const ( + ecnMask = 0x3 + oobBufferSize = 128 +) + +// Contrary to what the naming suggests, the ipv{4,6}.Message is not dependent on the IP version. +// They're both just aliases for x/net/internal/socket.Message. +// This means we can use this struct to read from a socket that receives both IPv4 and IPv6 messages. +var _ ipv4.Message = ipv6.Message{} + +type batchConn interface { + ReadBatch(ms []ipv4.Message, flags int) (int, error) +} + +func inspectReadBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF) + }); err != nil { + return 0, err + } + return size, serr +} + +func inspectWriteBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF) + }); err != nil { + return 0, err + } + return size, serr +} + +func isECNDisabled() bool { + disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_ECN")) + return err == nil && disabled +} + +type oobConn struct { + OOBCapablePacketConn + batchConn batchConn + + readPos uint8 + // Packets received from the kernel, but not yet returned by ReadPacket(). + messages []ipv4.Message + buffers [batchSize]*packetBuffer + + cap connCapabilities +} + +var _ rawConn = &oobConn{} + +func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) { + rawConn, err := c.SyscallConn() + if err != nil { + return nil, err + } + needsPacketInfo := false + if udpAddr, ok := c.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP.IsUnspecified() { + needsPacketInfo = true + } + // We don't know if this a IPv4-only, IPv6-only or a IPv4-and-IPv6 connection. + // Try enabling receiving of ECN and packet info for both IP versions. + // We expect at least one of those syscalls to succeed. + var errECNIPv4, errECNIPv6, errPIIPv4, errPIIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errECNIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1) + errECNIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1) + + if needsPacketInfo { + errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4PKTINFO, 1) + errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1) + } + }); err != nil { + return nil, err + } + switch { + case errECNIPv4 == nil && errECNIPv6 == nil: + utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4 and IPv6.") + case errECNIPv4 == nil && errECNIPv6 != nil: + utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4.") + case errECNIPv4 != nil && errECNIPv6 == nil: + utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv6.") + case errECNIPv4 != nil && errECNIPv6 != nil: + return nil, errors.New("activating ECN failed for both IPv4 and IPv6") + } + if needsPacketInfo { + switch { + case errPIIPv4 == nil && errPIIPv6 == nil: + utils.DefaultLogger.Debugf("Activating reading of packet info for IPv4 and IPv6.") + case errPIIPv4 == nil && errPIIPv6 != nil: + utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv4.") + case errPIIPv4 != nil && errPIIPv6 == nil: + utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv6.") + case errPIIPv4 != nil && errPIIPv6 != nil: + return nil, errors.New("activating packet info failed for both IPv4 and IPv6") + } + } + + // Allows callers to pass in a connection that already satisfies batchConn interface + // to make use of the optimisation. Otherwise, ipv4.NewPacketConn would unwrap the file descriptor + // via SyscallConn(), and read it that way, which might not be what the caller wants. + var bc batchConn + if ibc, ok := c.(batchConn); ok { + bc = ibc + } else { + bc = ipv4.NewPacketConn(c) + } + + msgs := make([]ipv4.Message, batchSize) + for i := range msgs { + // preallocate the [][]byte + msgs[i].Buffers = make([][]byte, 1) + } + oobConn := &oobConn{ + OOBCapablePacketConn: c, + batchConn: bc, + messages: msgs, + readPos: batchSize, + cap: connCapabilities{ + DF: supportsDF, + GSO: isGSOSupported(rawConn), + ECN: !isECNDisabled(), + }, + } + for i := 0; i < batchSize; i++ { + oobConn.messages[i].OOB = make([]byte, oobBufferSize) + } + return oobConn, nil +} + +var invalidCmsgOnceV4, invalidCmsgOnceV6 sync.Once + +func (c *oobConn) ReadPacket() (receivedPacket, error) { + if len(c.messages) == int(c.readPos) { // all messages read. Read the next batch of messages. + c.messages = c.messages[:batchSize] + // replace buffers data buffers up to the packet that has been consumed during the last ReadBatch call + for i := uint8(0); i < c.readPos; i++ { + buffer := getPacketBuffer() + buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize] + c.buffers[i] = buffer + c.messages[i].Buffers[0] = c.buffers[i].Data + } + c.readPos = 0 + + n, err := c.batchConn.ReadBatch(c.messages, 0) + if n == 0 || err != nil { + return receivedPacket{}, err + } + c.messages = c.messages[:n] + } + + msg := c.messages[c.readPos] + buffer := c.buffers[c.readPos] + c.readPos++ + + data := msg.OOB[:msg.NN] + p := receivedPacket{ + remoteAddr: msg.Addr, + rcvTime: time.Now(), + data: msg.Buffers[0][:msg.N], + buffer: buffer, + } + for len(data) > 0 { + hdr, body, remainder, err := unix.ParseOneSocketControlMessage(data) + if err != nil { + return receivedPacket{}, err + } + if hdr.Level == unix.IPPROTO_IP { + switch hdr.Type { + case msgTypeIPTOS: + p.ecn = protocol.ParseECNHeaderBits(body[0] & ecnMask) + case ipv4PKTINFO: + ip, ifIndex, ok := parseIPv4PktInfo(body) + if ok { + p.info.addr = ip + p.info.ifIndex = ifIndex + } else { + invalidCmsgOnceV4.Do(func() { + log.Printf("Received invalid IPv4 packet info control message: %+x. "+ + "This should never occur, please open a new issue and include details about the architecture.", body) + }) + } + } + } + if hdr.Level == unix.IPPROTO_IPV6 { + switch hdr.Type { + case unix.IPV6_TCLASS: + p.ecn = protocol.ParseECNHeaderBits(body[0] & ecnMask) + case unix.IPV6_PKTINFO: + // struct in6_pktinfo { + // struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + // unsigned int ipi6_ifindex; /* send/recv interface index */ + // }; + if len(body) == 20 { + p.info.addr = netip.AddrFrom16(*(*[16]byte)(body[:16])).Unmap() + p.info.ifIndex = binary.LittleEndian.Uint32(body[16:]) + } else { + invalidCmsgOnceV6.Do(func() { + log.Printf("Received invalid IPv6 packet info control message: %+x. "+ + "This should never occur, please open a new issue and include details about the architecture.", body) + }) + } + } + } + data = remainder + } + return p, nil +} + +// WritePacket writes a new packet. +func (c *oobConn) WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) { + oob := packetInfoOOB + if gsoSize > 0 { + if !c.capabilities().GSO { + panic("GSO disabled") + } + oob = appendUDPSegmentSizeMsg(oob, gsoSize) + } + if ecn != protocol.ECNUnsupported { + if !c.capabilities().ECN { + panic("tried to send a ECN-marked packet although ECN is disabled") + } + if remoteUDPAddr, ok := addr.(*net.UDPAddr); ok { + if remoteUDPAddr.IP.To4() != nil { + oob = appendIPv4ECNMsg(oob, ecn) + } else { + oob = appendIPv6ECNMsg(oob, ecn) + } + } + } + n, _, err := c.OOBCapablePacketConn.WriteMsgUDP(b, oob, addr.(*net.UDPAddr)) + return n, err +} + +func (c *oobConn) capabilities() connCapabilities { + return c.cap +} + +type packetInfo struct { + addr netip.Addr + ifIndex uint32 +} + +func (info *packetInfo) OOB() []byte { + if info == nil { + return nil + } + if info.addr.Is4() { + ip := info.addr.As4() + // struct in_pktinfo { + // unsigned int ipi_ifindex; /* Interface index */ + // struct in_addr ipi_spec_dst; /* Local address */ + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + cm := ipv4.ControlMessage{ + Src: ip[:], + IfIndex: int(info.ifIndex), + } + return cm.Marshal() + } else if info.addr.Is6() { + ip := info.addr.As16() + // struct in6_pktinfo { + // struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + // unsigned int ipi6_ifindex; /* send/recv interface index */ + // }; + cm := ipv6.ControlMessage{ + Src: ip[:], + IfIndex: int(info.ifIndex), + } + return cm.Marshal() + } + return nil +} + +func appendIPv4ECNMsg(b []byte, val protocol.ECN) []byte { + startLen := len(b) + b = append(b, make([]byte, unix.CmsgSpace(ecnIPv4DataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_IP + h.Type = unix.IP_TOS + h.SetLen(unix.CmsgLen(ecnIPv4DataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + b[offset] = val.ToHeaderBits() + return b +} + +func appendIPv6ECNMsg(b []byte, val protocol.ECN) []byte { + startLen := len(b) + const dataLen = 4 + b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_IPV6 + h.Type = unix.IPV6_TCLASS + h.SetLen(unix.CmsgLen(dataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + b[offset] = val.ToHeaderBits() + return b +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go new file mode 100644 index 000000000..b9c1cbc86 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go @@ -0,0 +1,42 @@ +//go:build windows + +package quic + +import ( + "net/netip" + "syscall" + + "golang.org/x/sys/windows" +) + +func newConn(c OOBCapablePacketConn, supportsDF bool) (*basicConn, error) { + return &basicConn{PacketConn: c, supportsDF: supportsDF}, nil +} + +func inspectReadBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF) + }); err != nil { + return 0, err + } + return size, serr +} + +func inspectWriteBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF) + }); err != nil { + return 0, err + } + return size, serr +} + +type packetInfo struct { + addr netip.Addr +} + +func (i *packetInfo) OOB() []byte { return nil } diff --git a/vendor/github.com/quic-go/quic-go/token_store.go b/vendor/github.com/quic-go/quic-go/token_store.go new file mode 100644 index 000000000..a5c1c1852 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/token_store.go @@ -0,0 +1,116 @@ +package quic + +import ( + "sync" + + list "github.com/quic-go/quic-go/internal/utils/linkedlist" +) + +type singleOriginTokenStore struct { + tokens []*ClientToken + len int + p int +} + +func newSingleOriginTokenStore(size int) *singleOriginTokenStore { + return &singleOriginTokenStore{tokens: make([]*ClientToken, size)} +} + +func (s *singleOriginTokenStore) Add(token *ClientToken) { + s.tokens[s.p] = token + s.p = s.index(s.p + 1) + s.len = min(s.len+1, len(s.tokens)) +} + +func (s *singleOriginTokenStore) Pop() *ClientToken { + s.p = s.index(s.p - 1) + token := s.tokens[s.p] + s.tokens[s.p] = nil + s.len = max(s.len-1, 0) + return token +} + +func (s *singleOriginTokenStore) Len() int { + return s.len +} + +func (s *singleOriginTokenStore) index(i int) int { + mod := len(s.tokens) + return (i + mod) % mod +} + +type lruTokenStoreEntry struct { + key string + cache *singleOriginTokenStore +} + +type lruTokenStore struct { + mutex sync.Mutex + + m map[string]*list.Element[*lruTokenStoreEntry] + q *list.List[*lruTokenStoreEntry] + capacity int + singleOriginSize int +} + +var _ TokenStore = &lruTokenStore{} + +// NewLRUTokenStore creates a new LRU cache for tokens received by the client. +// maxOrigins specifies how many origins this cache is saving tokens for. +// tokensPerOrigin specifies the maximum number of tokens per origin. +func NewLRUTokenStore(maxOrigins, tokensPerOrigin int) TokenStore { + return &lruTokenStore{ + m: make(map[string]*list.Element[*lruTokenStoreEntry]), + q: list.New[*lruTokenStoreEntry](), + capacity: maxOrigins, + singleOriginSize: tokensPerOrigin, + } +} + +func (s *lruTokenStore) Put(key string, token *ClientToken) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if el, ok := s.m[key]; ok { + entry := el.Value + entry.cache.Add(token) + s.q.MoveToFront(el) + return + } + + if s.q.Len() < s.capacity { + entry := &lruTokenStoreEntry{ + key: key, + cache: newSingleOriginTokenStore(s.singleOriginSize), + } + entry.cache.Add(token) + s.m[key] = s.q.PushFront(entry) + return + } + + elem := s.q.Back() + entry := elem.Value + delete(s.m, entry.key) + entry.key = key + entry.cache = newSingleOriginTokenStore(s.singleOriginSize) + entry.cache.Add(token) + s.q.MoveToFront(elem) + s.m[key] = elem +} + +func (s *lruTokenStore) Pop(key string) *ClientToken { + s.mutex.Lock() + defer s.mutex.Unlock() + + var token *ClientToken + if el, ok := s.m[key]; ok { + s.q.MoveToFront(el) + cache := el.Value.cache + token = cache.Pop() + if cache.Len() == 0 { + s.q.Remove(el) + delete(s.m, key) + } + } + return token +} diff --git a/vendor/github.com/onsi/gomega/tools b/vendor/github.com/quic-go/quic-go/tools.go similarity index 61% rename from vendor/github.com/onsi/gomega/tools rename to vendor/github.com/quic-go/quic-go/tools.go index e4195cf36..d00ce748d 100644 --- a/vendor/github.com/onsi/gomega/tools +++ b/vendor/github.com/quic-go/quic-go/tools.go @@ -1,8 +1,8 @@ //go:build tools -// +build tools -package main +package quic import ( _ "github.com/onsi/ginkgo/v2/ginkgo" + _ "go.uber.org/mock/mockgen" ) diff --git a/vendor/github.com/quic-go/quic-go/transport.go b/vendor/github.com/quic-go/quic-go/transport.go new file mode 100644 index 000000000..ea219c112 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/transport.go @@ -0,0 +1,542 @@ +package quic + +import ( + "context" + "crypto/rand" + "crypto/tls" + "errors" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" + "github.com/quic-go/quic-go/logging" +) + +var errListenerAlreadySet = errors.New("listener already set") + +// The Transport is the central point to manage incoming and outgoing QUIC connections. +// QUIC demultiplexes connections based on their QUIC Connection IDs, not based on the 4-tuple. +// This means that a single UDP socket can be used for listening for incoming connections, as well as +// for dialing an arbitrary number of outgoing connections. +// A Transport handles a single net.PacketConn, and offers a range of configuration options +// compared to the simple helper functions like Listen and Dial that this package provides. +type Transport struct { + // A single net.PacketConn can only be handled by one Transport. + // Bad things will happen if passed to multiple Transports. + // + // A number of optimizations will be enabled if the connections implements the OOBCapablePacketConn interface, + // as a *net.UDPConn does. + // 1. It enables the Don't Fragment (DF) bit on the IP header. + // This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899). + // 2. It enables reading of the ECN bits from the IP header. + // This allows the remote node to speed up its loss detection and recovery. + // 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket. + // 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux). + // + // After passing the connection to the Transport, it's invalid to call ReadFrom or WriteTo on the connection. + Conn net.PacketConn + + // The length of the connection ID in bytes. + // It can be any value between 1 and 20. + // Due to the increased risk of collisions, it is not recommended to use connection IDs shorter than 4 bytes. + // If unset, a 4 byte connection ID will be used. + ConnectionIDLength int + + // Use for generating new connection IDs. + // This allows the application to control of the connection IDs used, + // which allows routing / load balancing based on connection IDs. + // All Connection IDs returned by the ConnectionIDGenerator MUST + // have the same length. + ConnectionIDGenerator ConnectionIDGenerator + + // The StatelessResetKey is used to generate stateless reset tokens. + // If no key is configured, sending of stateless resets is disabled. + // It is highly recommended to configure a stateless reset key, as stateless resets + // allow the peer to quickly recover from crashes and reboots of this node. + // See section 10.3 of RFC 9000 for details. + StatelessResetKey *StatelessResetKey + + // The TokenGeneratorKey is used to encrypt session resumption tokens. + // If no key is configured, a random key will be generated. + // If multiple servers are authoritative for the same domain, they should use the same key, + // see section 8.1.3 of RFC 9000 for details. + TokenGeneratorKey *TokenGeneratorKey + + // MaxTokenAge is the maximum age of the resumption token presented during the handshake. + // These tokens allow skipping address resumption when resuming a QUIC connection, + // and are especially useful when using 0-RTT. + // If not set, it defaults to 24 hours. + // See section 8.1.3 of RFC 9000 for details. + MaxTokenAge time.Duration + + // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. + // This can be useful if version information is exchanged out-of-band. + // It has no effect for clients. + DisableVersionNegotiationPackets bool + + // VerifySourceAddress decides if a connection attempt originating from unvalidated source + // addresses first needs to go through source address validation using QUIC's Retry mechanism, + // as described in RFC 9000 section 8.1.2. + // Note that the address passed to this callback is unvalidated, and might be spoofed in case + // of an attack. + // Validating the source address adds one additional network roundtrip to the handshake, + // and should therefore only be used if a suspiciously high number of incoming connection is recorded. + // For most use cases, wrapping the Allow function of a rate.Limiter will be a reasonable + // implementation of this callback (negating its return value). + VerifySourceAddress func(net.Addr) bool + + // A Tracer traces events that don't belong to a single QUIC connection. + // Tracer.Close is called when the transport is closed. + Tracer *logging.Tracer + + handlerMap packetHandlerManager + + mutex sync.Mutex + initOnce sync.Once + initErr error + + // Set in init. + // If no ConnectionIDGenerator is set, this is the ConnectionIDLength. + connIDLen int + // Set in init. + // If no ConnectionIDGenerator is set, this is set to a default. + connIDGenerator ConnectionIDGenerator + + server *baseServer + + conn rawConn + + closeQueue chan closePacket + statelessResetQueue chan receivedPacket + + listening chan struct{} // is closed when listen returns + closed bool + createdConn bool + isSingleUse bool // was created for a single server or client, i.e. by calling quic.Listen or quic.Dial + + readingNonQUICPackets atomic.Bool + nonQUICPackets chan receivedPacket + + logger utils.Logger +} + +// Listen starts listening for incoming QUIC connections. +// There can only be a single listener on any net.PacketConn. +// Listen may only be called again after the current Listener was closed. +func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) { + s, err := t.createServer(tlsConf, conf, false) + if err != nil { + return nil, err + } + return &Listener{baseServer: s}, nil +} + +// ListenEarly starts listening for incoming QUIC connections. +// There can only be a single listener on any net.PacketConn. +// Listen may only be called again after the current Listener was closed. +func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListener, error) { + s, err := t.createServer(tlsConf, conf, true) + if err != nil { + return nil, err + } + return &EarlyListener{baseServer: s}, nil +} + +func (t *Transport) createServer(tlsConf *tls.Config, conf *Config, allow0RTT bool) (*baseServer, error) { + if tlsConf == nil { + return nil, errors.New("quic: tls.Config not set") + } + if err := validateConfig(conf); err != nil { + return nil, err + } + + t.mutex.Lock() + defer t.mutex.Unlock() + + if t.server != nil { + return nil, errListenerAlreadySet + } + conf = populateConfig(conf) + if err := t.init(false); err != nil { + return nil, err + } + s := newServer( + t.conn, + t.handlerMap, + t.connIDGenerator, + tlsConf, + conf, + t.Tracer, + t.closeServer, + *t.TokenGeneratorKey, + t.MaxTokenAge, + t.VerifySourceAddress, + t.DisableVersionNegotiationPackets, + allow0RTT, + ) + t.server = s + return s, nil +} + +// Dial dials a new connection to a remote host (not using 0-RTT). +func (t *Transport) Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { + return t.dial(ctx, addr, "", tlsConf, conf, false) +} + +// DialEarly dials a new connection, attempting to use 0-RTT if possible. +func (t *Transport) DialEarly(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { + return t.dial(ctx, addr, "", tlsConf, conf, true) +} + +func (t *Transport) dial(ctx context.Context, addr net.Addr, host string, tlsConf *tls.Config, conf *Config, use0RTT bool) (EarlyConnection, error) { + if err := validateConfig(conf); err != nil { + return nil, err + } + conf = populateConfig(conf) + if err := t.init(t.isSingleUse); err != nil { + return nil, err + } + var onClose func() + if t.isSingleUse { + onClose = func() { t.Close() } + } + tlsConf = tlsConf.Clone() + setTLSConfigServerName(tlsConf, addr, host) + return dial(ctx, newSendConn(t.conn, addr, packetInfo{}, utils.DefaultLogger), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, use0RTT) +} + +func (t *Transport) init(allowZeroLengthConnIDs bool) error { + t.initOnce.Do(func() { + var conn rawConn + if c, ok := t.Conn.(rawConn); ok { + conn = c + } else { + var err error + conn, err = wrapConn(t.Conn) + if err != nil { + t.initErr = err + return + } + } + + t.logger = utils.DefaultLogger // TODO: make this configurable + t.conn = conn + t.handlerMap = newPacketHandlerMap(t.StatelessResetKey, t.enqueueClosePacket, t.logger) + t.listening = make(chan struct{}) + + t.closeQueue = make(chan closePacket, 4) + t.statelessResetQueue = make(chan receivedPacket, 4) + if t.TokenGeneratorKey == nil { + var key TokenGeneratorKey + if _, err := rand.Read(key[:]); err != nil { + t.initErr = err + return + } + t.TokenGeneratorKey = &key + } + + if t.ConnectionIDGenerator != nil { + t.connIDGenerator = t.ConnectionIDGenerator + t.connIDLen = t.ConnectionIDGenerator.ConnectionIDLen() + } else { + connIDLen := t.ConnectionIDLength + if t.ConnectionIDLength == 0 && !allowZeroLengthConnIDs { + connIDLen = protocol.DefaultConnectionIDLength + } + t.connIDLen = connIDLen + t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen} + } + + getMultiplexer().AddConn(t.Conn) + go t.listen(conn) + go t.runSendQueue() + }) + return t.initErr +} + +// WriteTo sends a packet on the underlying connection. +func (t *Transport) WriteTo(b []byte, addr net.Addr) (int, error) { + if err := t.init(false); err != nil { + return 0, err + } + return t.conn.WritePacket(b, addr, nil, 0, protocol.ECNUnsupported) +} + +func (t *Transport) enqueueClosePacket(p closePacket) { + select { + case t.closeQueue <- p: + default: + // Oops, we're backlogged. + // Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway. + } +} + +func (t *Transport) runSendQueue() { + for { + select { + case <-t.listening: + return + case p := <-t.closeQueue: + t.conn.WritePacket(p.payload, p.addr, p.info.OOB(), 0, protocol.ECNUnsupported) + case p := <-t.statelessResetQueue: + t.sendStatelessReset(p) + } + } +} + +// Close closes the underlying connection. +// If any listener was started, it will be closed as well. +// It is invalid to start new listeners or connections after that. +func (t *Transport) Close() error { + t.close(errors.New("closing")) + if t.createdConn { + if err := t.Conn.Close(); err != nil { + return err + } + } else if t.conn != nil { + t.conn.SetReadDeadline(time.Now()) + defer func() { t.conn.SetReadDeadline(time.Time{}) }() + } + if t.listening != nil { + <-t.listening // wait until listening returns + } + return nil +} + +func (t *Transport) closeServer() { + t.mutex.Lock() + t.server = nil + if t.isSingleUse { + t.closed = true + } + t.mutex.Unlock() + if t.createdConn { + t.Conn.Close() + } + if t.isSingleUse { + t.conn.SetReadDeadline(time.Now()) + defer func() { t.conn.SetReadDeadline(time.Time{}) }() + <-t.listening // wait until listening returns + } +} + +func (t *Transport) close(e error) { + t.mutex.Lock() + defer t.mutex.Unlock() + if t.closed { + return + } + + if t.handlerMap != nil { + t.handlerMap.Close(e) + } + if t.server != nil { + t.server.close(e, false) + } + if t.Tracer != nil && t.Tracer.Close != nil { + t.Tracer.Close() + } + t.closed = true +} + +// only print warnings about the UDP receive buffer size once +var setBufferWarningOnce sync.Once + +func (t *Transport) listen(conn rawConn) { + defer close(t.listening) + defer getMultiplexer().RemoveConn(t.Conn) + + for { + p, err := conn.ReadPacket() + //nolint:staticcheck // SA1019 ignore this! + // TODO: This code is used to ignore wsa errors on Windows. + // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution. + // See https://github.com/quic-go/quic-go/issues/1737 for details. + if nerr, ok := err.(net.Error); ok && nerr.Temporary() { + t.mutex.Lock() + closed := t.closed + t.mutex.Unlock() + if closed { + return + } + t.logger.Debugf("Temporary error reading from conn: %w", err) + continue + } + if err != nil { + // Windows returns an error when receiving a UDP datagram that doesn't fit into the provided buffer. + if isRecvMsgSizeErr(err) { + continue + } + t.close(err) + return + } + t.handlePacket(p) + } +} + +func (t *Transport) handlePacket(p receivedPacket) { + if len(p.data) == 0 { + return + } + if !wire.IsPotentialQUICPacket(p.data[0]) && !wire.IsLongHeaderPacket(p.data[0]) { + t.handleNonQUICPacket(p) + return + } + connID, err := wire.ParseConnectionID(p.data, t.connIDLen) + if err != nil { + t.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) + if t.Tracer != nil && t.Tracer.DroppedPacket != nil { + t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) + } + p.buffer.MaybeRelease() + return + } + + // If there's a connection associated with the connection ID, pass the packet there. + if handler, ok := t.handlerMap.Get(connID); ok { + handler.handlePacket(p) + return + } + // RFC 9000 section 10.3.1 requires that the stateless reset detection logic is run for both + // packets that cannot be associated with any connections, and for packets that can't be decrypted. + // We deviate from the RFC and ignore the latter: If a packet's connection ID is associated with an + // existing connection, it is dropped there if if it can't be decrypted. + // Stateless resets use random connection IDs, and at reasonable connection ID lengths collisions are + // exceedingly rare. In the unlikely event that a stateless reset is misrouted to an existing connection, + // it is to be expected that the next stateless reset will be correctly detected. + if isStatelessReset := t.maybeHandleStatelessReset(p.data); isStatelessReset { + return + } + if !wire.IsLongHeaderPacket(p.data[0]) { + t.maybeSendStatelessReset(p) + return + } + + t.mutex.Lock() + defer t.mutex.Unlock() + if t.server == nil { // no server set + t.logger.Debugf("received a packet with an unexpected connection ID %s", connID) + return + } + t.server.handlePacket(p) +} + +func (t *Transport) maybeSendStatelessReset(p receivedPacket) { + if t.StatelessResetKey == nil { + p.buffer.Release() + return + } + + // Don't send a stateless reset in response to very small packets. + // This includes packets that could be stateless resets. + if len(p.data) <= protocol.MinStatelessResetSize { + p.buffer.Release() + return + } + + select { + case t.statelessResetQueue <- p: + default: + // it's fine to not send a stateless reset when we're busy + p.buffer.Release() + } +} + +func (t *Transport) sendStatelessReset(p receivedPacket) { + defer p.buffer.Release() + + connID, err := wire.ParseConnectionID(p.data, t.connIDLen) + if err != nil { + t.logger.Errorf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) + return + } + token := t.handlerMap.GetStatelessResetToken(connID) + t.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) + data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize) + rand.Read(data) + data[0] = (data[0] & 0x7f) | 0x40 + data = append(data, token[:]...) + if _, err := t.conn.WritePacket(data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported); err != nil { + t.logger.Debugf("Error sending Stateless Reset to %s: %s", p.remoteAddr, err) + } +} + +func (t *Transport) maybeHandleStatelessReset(data []byte) bool { + // stateless resets are always short header packets + if wire.IsLongHeaderPacket(data[0]) { + return false + } + if len(data) < 17 /* type byte + 16 bytes for the reset token */ { + return false + } + + token := *(*protocol.StatelessResetToken)(data[len(data)-16:]) + if conn, ok := t.handlerMap.GetByResetToken(token); ok { + t.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token) + go conn.destroy(&StatelessResetError{Token: token}) + return true + } + return false +} + +func (t *Transport) handleNonQUICPacket(p receivedPacket) { + // Strictly speaking, this is racy, + // but we only care about receiving packets at some point after ReadNonQUICPacket has been called. + if !t.readingNonQUICPackets.Load() { + return + } + select { + case t.nonQUICPackets <- p: + default: + if t.Tracer != nil && t.Tracer.DroppedPacket != nil { + t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) + } + } +} + +const maxQueuedNonQUICPackets = 32 + +// ReadNonQUICPacket reads non-QUIC packets received on the underlying connection. +// The detection logic is very simple: Any packet that has the first and second bit of the packet set to 0. +// Note that this is stricter than the detection logic defined in RFC 9443. +func (t *Transport) ReadNonQUICPacket(ctx context.Context, b []byte) (int, net.Addr, error) { + if err := t.init(false); err != nil { + return 0, nil, err + } + if !t.readingNonQUICPackets.Load() { + t.nonQUICPackets = make(chan receivedPacket, maxQueuedNonQUICPackets) + t.readingNonQUICPackets.Store(true) + } + select { + case <-ctx.Done(): + return 0, nil, ctx.Err() + case p := <-t.nonQUICPackets: + n := copy(b, p.data) + return n, p.remoteAddr, nil + case <-t.listening: + return 0, nil, errors.New("closed") + } +} + +func setTLSConfigServerName(tlsConf *tls.Config, addr net.Addr, host string) { + // If no ServerName is set, infer the ServerName from the host we're connecting to. + if tlsConf.ServerName != "" { + return + } + if host == "" { + if udpAddr, ok := addr.(*net.UDPAddr); ok { + tlsConf.ServerName = udpAddr.IP.String() + return + } + } + h, _, err := net.SplitHostPort(host) + if err != nil { // This happens if the host doesn't contain a port number. + tlsConf.ServerName = host + return + } + tlsConf.ServerName = h +} diff --git a/vendor/github.com/quic-go/quic-go/window_update_queue.go b/vendor/github.com/quic-go/quic-go/window_update_queue.go new file mode 100644 index 000000000..9ed121430 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/window_update_queue.go @@ -0,0 +1,71 @@ +package quic + +import ( + "sync" + + "github.com/quic-go/quic-go/internal/flowcontrol" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +type windowUpdateQueue struct { + mutex sync.Mutex + + queue map[protocol.StreamID]struct{} // used as a set + queuedConn bool // connection-level window update + + streamGetter streamGetter + connFlowController flowcontrol.ConnectionFlowController + callback func(wire.Frame) +} + +func newWindowUpdateQueue( + streamGetter streamGetter, + connFC flowcontrol.ConnectionFlowController, + cb func(wire.Frame), +) *windowUpdateQueue { + return &windowUpdateQueue{ + queue: make(map[protocol.StreamID]struct{}), + streamGetter: streamGetter, + connFlowController: connFC, + callback: cb, + } +} + +func (q *windowUpdateQueue) AddStream(id protocol.StreamID) { + q.mutex.Lock() + q.queue[id] = struct{}{} + q.mutex.Unlock() +} + +func (q *windowUpdateQueue) AddConnection() { + q.mutex.Lock() + q.queuedConn = true + q.mutex.Unlock() +} + +func (q *windowUpdateQueue) QueueAll() { + q.mutex.Lock() + // queue a connection-level window update + if q.queuedConn { + q.callback(&wire.MaxDataFrame{MaximumData: q.connFlowController.GetWindowUpdate()}) + q.queuedConn = false + } + // queue all stream-level window updates + for id := range q.queue { + delete(q.queue, id) + str, err := q.streamGetter.GetOrOpenReceiveStream(id) + if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update + continue + } + offset := str.getWindowUpdate() + if offset == 0 { // can happen if we received a final offset, right after queueing the window update + continue + } + q.callback(&wire.MaxStreamDataFrame{ + StreamID: id, + MaximumStreamData: offset, + }) + } + q.mutex.Unlock() +} diff --git a/vendor/github.com/DataDog/datadog-go/LICENSE.txt b/vendor/github.com/secure-systems-lab/go-securesystemslib/LICENSE similarity index 94% rename from vendor/github.com/DataDog/datadog-go/LICENSE.txt rename to vendor/github.com/secure-systems-lab/go-securesystemslib/LICENSE index 97cd06d7f..e51324f9b 100644 --- a/vendor/github.com/DataDog/datadog-go/LICENSE.txt +++ b/vendor/github.com/secure-systems-lab/go-securesystemslib/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2015 Datadog, Inc +The MIT License (MIT) + +Copyright (c) 2021 NYU Secure Systems Lab Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/secure-systems-lab/go-securesystemslib/cjson/canonicaljson.go b/vendor/github.com/secure-systems-lab/go-securesystemslib/cjson/canonicaljson.go new file mode 100644 index 000000000..abc860a49 --- /dev/null +++ b/vendor/github.com/secure-systems-lab/go-securesystemslib/cjson/canonicaljson.go @@ -0,0 +1,151 @@ +package cjson + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "reflect" + "sort" + "strings" +) + +/* +encodeCanonicalString is a helper function to canonicalize the passed string +according to the OLPC canonical JSON specification for strings (see +http://wiki.laptop.org/go/Canonical_JSON). String canonicalization consists of +escaping backslashes ("\") and double quotes (") and wrapping the resulting +string in double quotes ("). +*/ +func encodeCanonicalString(s string) string { + // Escape backslashes + s = strings.ReplaceAll(s, "\\", "\\\\") + // Escape double quotes + s = strings.ReplaceAll(s, "\"", "\\\"") + // Wrap with double quotes + return fmt.Sprintf("\"%s\"", s) +} + +/* +encodeCanonical is a helper function to recursively canonicalize the passed +object according to the OLPC canonical JSON specification (see +http://wiki.laptop.org/go/Canonical_JSON) and write it to the passed +*bytes.Buffer. If canonicalization fails it returns an error. +*/ +func encodeCanonical(obj interface{}, result *strings.Builder) (err error) { + switch objAsserted := obj.(type) { + case string: + result.WriteString(encodeCanonicalString(objAsserted)) + + case bool: + if objAsserted { + result.WriteString("true") + } else { + result.WriteString("false") + } + + // The wrapping `EncodeCanonical` function decodes the passed json data with + // `decoder.UseNumber` so that any numeric value is stored as `json.Number` + // (instead of the default `float64`). This allows us to assert that it is a + // non-floating point number, which are the only numbers allowed by the used + // canonicalization specification. + case json.Number: + if _, err := objAsserted.Int64(); err != nil { + panic(fmt.Sprintf("Can't canonicalize floating point number '%s'", + objAsserted)) + } + result.WriteString(objAsserted.String()) + + case nil: + result.WriteString("null") + + // Canonicalize slice + case []interface{}: + result.WriteString("[") + for i, val := range objAsserted { + if err := encodeCanonical(val, result); err != nil { + return err + } + if i < (len(objAsserted) - 1) { + result.WriteString(",") + } + } + result.WriteString("]") + + case map[string]interface{}: + result.WriteString("{") + + // Make a list of keys + var mapKeys []string + for key := range objAsserted { + mapKeys = append(mapKeys, key) + } + // Sort keys + sort.Strings(mapKeys) + + // Canonicalize map + for i, key := range mapKeys { + if err := encodeCanonical(key, result); err != nil { + return err + } + + result.WriteString(":") + if err := encodeCanonical(objAsserted[key], result); err != nil { + return err + } + if i < (len(mapKeys) - 1) { + result.WriteString(",") + } + i++ + } + result.WriteString("}") + + default: + // We recover in a deferred function defined above + panic(fmt.Sprintf("Can't canonicalize '%s' of type '%s'", + objAsserted, reflect.TypeOf(objAsserted))) + } + return nil +} + +/* +EncodeCanonical JSON canonicalizes the passed object and returns it as a byte +slice. It uses the OLPC canonical JSON specification (see +http://wiki.laptop.org/go/Canonical_JSON). If canonicalization fails the byte +slice is nil and the second return value contains the error. +*/ +func EncodeCanonical(obj interface{}) (out []byte, err error) { + // We use panic if an error occurs and recover in a deferred function, + // which is always called before returning. + // There we set the error that is returned eventually. + defer func() { + if r := recover(); r != nil { + err = errors.New(r.(string)) + } + }() + + // FIXME: Terrible hack to turn the passed struct into a map, converting + // the struct's variable names to the json key names defined in the struct + data, err := json.Marshal(obj) + if err != nil { + return nil, err + } + var jsonMap interface{} + + dec := json.NewDecoder(bytes.NewReader(data)) + dec.UseNumber() + if err := dec.Decode(&jsonMap); err != nil { + return nil, err + } + + // Create a buffer and write the canonicalized JSON bytes to it + var result strings.Builder + // Allocate output result buffer with the input size. + result.Grow(len(data)) + // Recursively encode the jsonmap + if err := encodeCanonical(jsonMap, &result); err != nil { + return nil, err + } + + return []byte(result.String()), nil +} diff --git a/vendor/github.com/spf13/cobra/.golangci.yml b/vendor/github.com/spf13/cobra/.golangci.yml index 439d3e1de..2578d94b5 100644 --- a/vendor/github.com/spf13/cobra/.golangci.yml +++ b/vendor/github.com/spf13/cobra/.golangci.yml @@ -1,4 +1,4 @@ -# Copyright 2013-2022 The Cobra Authors +# Copyright 2013-2023 The Cobra Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/Makefile b/vendor/github.com/spf13/cobra/Makefile index c433a01bc..0da8d7aa0 100644 --- a/vendor/github.com/spf13/cobra/Makefile +++ b/vendor/github.com/spf13/cobra/Makefile @@ -5,10 +5,6 @@ ifeq (, $(shell which golangci-lint)) $(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh") endif -ifeq (, $(shell which richgo)) -$(warning "could not find richgo in $(PATH), run: go install github.com/kyoh86/richgo@latest") -endif - .PHONY: fmt lint test install_deps clean default: all @@ -25,6 +21,10 @@ lint: test: install_deps $(info ******************** running tests ********************) + go test -v ./... + +richtest: install_deps + $(info ******************** running tests with kyoh86/richgo ********************) richgo test -v ./... install_deps: diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index 7cc726beb..592c0b8ab 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -1,4 +1,4 @@ -![cobra logo](https://cloud.githubusercontent.com/assets/173412/10886352/ad566232-814f-11e5-9cd0-aa101788c117.png) +![cobra logo](assets/CobraMain.png) Cobra is a library for creating powerful modern CLI applications. @@ -6,7 +6,7 @@ Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/), [Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. -[![](https://img.shields.io/github/workflow/status/spf13/cobra/Test?longCache=tru&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) +[![](https://img.shields.io/github/actions/workflow/status/spf13/cobra/test.yml?branch=main&longCache=true&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra) [![Slack](https://img.shields.io/badge/Slack-cobra-brightgreen)](https://gophers.slack.com/archives/CD3LP1199) diff --git a/vendor/github.com/spf13/cobra/active_help.go b/vendor/github.com/spf13/cobra/active_help.go index 95e03aecb..2d0239437 100644 --- a/vendor/github.com/spf13/cobra/active_help.go +++ b/vendor/github.com/spf13/cobra/active_help.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/args.go b/vendor/github.com/spf13/cobra/args.go index 2c1f99e78..e79ec33a8 100644 --- a/vendor/github.com/spf13/cobra/args.go +++ b/vendor/github.com/spf13/cobra/args.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( type PositionalArgs func(cmd *Command, args []string) error -// Legacy arg validation has the following behaviour: +// legacyArgs validation has the following behaviour: // - root commands with no subcommands can take arbitrary arguments // - root commands with subcommands will do subcommand validity checking // - subcommands will always accept arbitrary arguments diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index 3acdb2797..10c78847d 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -532,7 +532,7 @@ func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) { } } -// Setup annotations for go completions for registered flags +// prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags func prepareCustomAnnotationsForFlags(cmd *Command) { flagCompletionMutex.RLock() defer flagCompletionMutex.RUnlock() diff --git a/vendor/github.com/spf13/cobra/bash_completionsV2.go b/vendor/github.com/spf13/cobra/bash_completionsV2.go index bb4b71892..19b09560c 100644 --- a/vendor/github.com/spf13/cobra/bash_completionsV2.go +++ b/vendor/github.com/spf13/cobra/bash_completionsV2.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) { __%[1]s_debug() { - if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then + if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } @@ -65,7 +65,7 @@ __%[1]s_get_completion_results() { lastChar=${lastParam:$((${#lastParam}-1)):1} __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" - if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then + if [[ -z ${cur} && ${lastChar} != = ]]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "Adding extra empty parameter" @@ -75,7 +75,7 @@ __%[1]s_get_completion_results() { # When completing a flag with an = (e.g., %[1]s -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur - if [[ "${cur}" == -*=* ]]; then + if [[ ${cur} == -*=* ]]; then cur="${cur#*=}" fi @@ -87,7 +87,7 @@ __%[1]s_get_completion_results() { directive=${out##*:} # Remove the directive out=${out%%:*} - if [ "${directive}" = "${out}" ]; then + if [[ ${directive} == "${out}" ]]; then # There is not directive specified directive=0 fi @@ -101,22 +101,36 @@ __%[1]s_process_completion_results() { local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveKeepOrder=%[8]d - if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then + if (((directive & shellCompDirectiveError) != 0)); then # Error code. No completion. __%[1]s_debug "Received error from custom completion go code" return else - if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then + if (((directive & shellCompDirectiveNoSpace) != 0)); then + if [[ $(type -t compopt) == builtin ]]; then __%[1]s_debug "Activating no space" compopt -o nospace else __%[1]s_debug "No space directive not supported in this version of bash" fi fi - if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then + if (((directive & shellCompDirectiveKeepOrder) != 0)); then + if [[ $(type -t compopt) == builtin ]]; then + # no sort isn't supported for bash less than < 4.4 + if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then + __%[1]s_debug "No sort directive not supported in this version of bash" + else + __%[1]s_debug "Activating keep order" + compopt -o nosort + fi + else + __%[1]s_debug "No sort directive not supported in this version of bash" + fi + fi + if (((directive & shellCompDirectiveNoFileComp) != 0)); then + if [[ $(type -t compopt) == builtin ]]; then __%[1]s_debug "Activating no file completion" compopt +o default else @@ -130,7 +144,7 @@ __%[1]s_process_completion_results() { local activeHelp=() __%[1]s_extract_activeHelp - if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then + if (((directive & shellCompDirectiveFilterFileExt) != 0)); then # File extension filtering local fullFilter filter filteringCmd @@ -143,13 +157,12 @@ __%[1]s_process_completion_results() { filteringCmd="_filedir $fullFilter" __%[1]s_debug "File filtering command: $filteringCmd" $filteringCmd - elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then + elif (((directive & shellCompDirectiveFilterDirs) != 0)); then # File completion for directories only - # Use printf to strip any trailing newline local subdir - subdir=$(printf "%%s" "${completions[0]}") - if [ -n "$subdir" ]; then + subdir=${completions[0]} + if [[ -n $subdir ]]; then __%[1]s_debug "Listing directories in $subdir" pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return else @@ -164,7 +177,7 @@ __%[1]s_process_completion_results() { __%[1]s_handle_special_char "$cur" = # Print the activeHelp statements before we finish - if [ ${#activeHelp[*]} -ne 0 ]; then + if ((${#activeHelp[*]} != 0)); then printf "\n"; printf "%%s\n" "${activeHelp[@]}" printf "\n" @@ -184,21 +197,21 @@ __%[1]s_process_completion_results() { # Separate activeHelp lines from real completions. # Fills the $activeHelp and $completions arrays. __%[1]s_extract_activeHelp() { - local activeHelpMarker="%[8]s" + local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do - if [ "${comp:0:endIndex}" = "$activeHelpMarker" ]; then + if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then comp=${comp:endIndex} __%[1]s_debug "ActiveHelp found: $comp" - if [ -n "$comp" ]; then + if [[ -n $comp ]]; then activeHelp+=("$comp") fi else # Not an activeHelp line but a normal completion completions+=("$comp") fi - done < <(printf "%%s\n" "${out}") + done <<<"${out}" } __%[1]s_handle_completion_types() { @@ -254,7 +267,7 @@ __%[1]s_handle_standard_completion_case() { done < <(printf "%%s\n" "${completions[@]}") # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then + if ((${#COMPREPLY[*]} == 1)); then __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" comp="${COMPREPLY[0]%%%%$tab*}" __%[1]s_debug "Removed description from single completion, which is now: ${comp}" @@ -271,8 +284,8 @@ __%[1]s_handle_special_char() if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then local word=${comp%%"${comp##*${char}}"} local idx=${#COMPREPLY[*]} - while [[ $((--idx)) -ge 0 ]]; do - COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"} + while ((--idx >= 0)); do + COMPREPLY[idx]=${COMPREPLY[idx]#"$word"} done fi } @@ -298,7 +311,7 @@ __%[1]s_format_comp_descriptions() # Make sure we can fit a description of at least 8 characters # if we are to align the descriptions. - if [[ $maxdesclength -gt 8 ]]; then + if ((maxdesclength > 8)); then # Add the proper number of spaces to align the descriptions for ((i = ${#comp} ; i < longest ; i++)); do comp+=" " @@ -310,8 +323,8 @@ __%[1]s_format_comp_descriptions() # If there is enough space for any description text, # truncate the descriptions that are too long for the shell width - if [ $maxdesclength -gt 0 ]; then - if [ ${#desc} -gt $maxdesclength ]; then + if ((maxdesclength > 0)); then + if ((${#desc} > maxdesclength)); then desc=${desc:0:$(( maxdesclength - 1 ))} desc+="…" fi @@ -332,9 +345,9 @@ __start_%[1]s() # Call _init_completion from the bash-completion package # to prepare the arguments properly if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -n "=:" || return + _init_completion -n =: || return else - __%[1]s_init_completion -n "=:" || return + __%[1]s_init_completion -n =: || return fi __%[1]s_debug @@ -361,7 +374,7 @@ fi # ex: ts=4 sw=4 et filetype=sh `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } diff --git a/vendor/github.com/spf13/cobra/cobra.go b/vendor/github.com/spf13/cobra/cobra.go index fe44bc8a0..b07b44a0c 100644 --- a/vendor/github.com/spf13/cobra/cobra.go +++ b/vendor/github.com/spf13/cobra/cobra.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -167,8 +167,8 @@ func appendIfNotPresent(s, stringToAppend string) string { // rpad adds padding to the right of a string. func rpad(s string, padding int) string { - template := fmt.Sprintf("%%-%ds", padding) - return fmt.Sprintf(template, s) + formattedString := fmt.Sprintf("%%-%ds", padding) + return fmt.Sprintf(formattedString, s) } // tmpl executes the given template text on data, writing the result to w. diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 9d5e9cf5e..01f7c6f1c 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist -// Structure to manage groups for commands +// Group Structure to manage groups for commands type Group struct { ID string Title string @@ -47,7 +47,7 @@ type Group struct { // definition to ensure usability. type Command struct { // Use is the one-line usage message. - // Recommended syntax is as follow: + // Recommended syntax is as follows: // [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required. // ... indicates that you can specify multiple values for the previous argument. // | indicates mutually exclusive information. You can use the argument to the left of the separator or the @@ -321,7 +321,7 @@ func (c *Command) SetHelpCommand(cmd *Command) { c.helpCommand = cmd } -// SetHelpCommandGroup sets the group id of the help command. +// SetHelpCommandGroupID sets the group id of the help command. func (c *Command) SetHelpCommandGroupID(groupID string) { if c.helpCommand != nil { c.helpCommand.GroupID = groupID @@ -330,7 +330,7 @@ func (c *Command) SetHelpCommandGroupID(groupID string) { c.helpCommandGroupID = groupID } -// SetCompletionCommandGroup sets the group id of the completion command. +// SetCompletionCommandGroupID sets the group id of the completion command. func (c *Command) SetCompletionCommandGroupID(groupID string) { // completionCommandGroupID is used if no completion command is defined by the user c.Root().completionCommandGroupID = groupID @@ -655,20 +655,44 @@ Loop: // argsMinusFirstX removes only the first x from args. Otherwise, commands that look like // openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]). -func argsMinusFirstX(args []string, x string) []string { - for i, y := range args { - if x == y { - ret := []string{} - ret = append(ret, args[:i]...) - ret = append(ret, args[i+1:]...) - return ret +// Special care needs to be taken not to remove a flag value. +func (c *Command) argsMinusFirstX(args []string, x string) []string { + if len(args) == 0 { + return args + } + c.mergePersistentFlags() + flags := c.Flags() + +Loop: + for pos := 0; pos < len(args); pos++ { + s := args[pos] + switch { + case s == "--": + // -- means we have reached the end of the parseable args. Break out of the loop now. + break Loop + case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags): + fallthrough + case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags): + // This is a flag without a default value, and an equal sign is not used. Increment pos in order to skip + // over the next arg, because that is the value of this flag. + pos++ + continue + case !strings.HasPrefix(s, "-"): + // This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so, + // return the args, excluding the one at this position. + if s == x { + ret := []string{} + ret = append(ret, args[:pos]...) + ret = append(ret, args[pos+1:]...) + return ret + } } } return args } func isFlagArg(arg string) bool { - return ((len(arg) >= 3 && arg[1] == '-') || + return ((len(arg) >= 3 && arg[0:2] == "--") || (len(arg) >= 2 && arg[0] == '-' && arg[1] != '-')) } @@ -686,7 +710,7 @@ func (c *Command) Find(args []string) (*Command, []string, error) { cmd := c.findNext(nextSubCmd) if cmd != nil { - return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd)) + return innerfind(cmd, c.argsMinusFirstX(innerArgs, nextSubCmd)) } return c, innerArgs } @@ -998,6 +1022,10 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // initialize completion at the last point to allow for user overriding c.InitDefaultCompletionCmd() + // Now that all commands have been created, let's make sure all groups + // are properly created also + c.checkCommandGroups() + args := c.args // Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155 @@ -1092,6 +1120,19 @@ func (c *Command) ValidateRequiredFlags() error { return nil } +// checkCommandGroups checks if a command has been added to a group that does not exists. +// If so, we panic because it indicates a coding error that should be corrected. +func (c *Command) checkCommandGroups() { + for _, sub := range c.commands { + // if Group is not defined let the developer know right away + if sub.GroupID != "" && !c.ContainsGroup(sub.GroupID) { + panic(fmt.Sprintf("group id '%s' is not defined for subcommand '%s'", sub.GroupID, sub.CommandPath())) + } + + sub.checkCommandGroups() + } +} + // InitDefaultHelpFlag adds default help flag to c. // It is called automatically by executing the c or by calling help and usage. // If c already has help flag, it will do nothing. @@ -1218,10 +1259,6 @@ func (c *Command) AddCommand(cmds ...*Command) { panic("Command can't be a child of itself") } cmds[i].parent = c - // if Group is not defined let the developer know right away - if x.GroupID != "" && !c.ContainsGroup(x.GroupID) { - panic(fmt.Sprintf("Group id '%s' is not defined for subcommand '%s'", x.GroupID, cmds[i].CommandPath())) - } // update max lengths usageLen := len(x.Use) if usageLen > c.commandsMaxUseLen { @@ -1259,7 +1296,7 @@ func (c *Command) AllChildCommandsHaveGroup() bool { return true } -// ContainGroups return if groupID exists in the list of command groups. +// ContainsGroup return if groupID exists in the list of command groups. func (c *Command) ContainsGroup(groupID string) bool { for _, x := range c.commandgroups { if x.ID == groupID { diff --git a/vendor/github.com/spf13/cobra/command_notwin.go b/vendor/github.com/spf13/cobra/command_notwin.go index 2b77f8f01..307f0c127 100644 --- a/vendor/github.com/spf13/cobra/command_notwin.go +++ b/vendor/github.com/spf13/cobra/command_notwin.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/command_win.go b/vendor/github.com/spf13/cobra/command_win.go index 520f23abf..adbef395c 100644 --- a/vendor/github.com/spf13/cobra/command_win.go +++ b/vendor/github.com/spf13/cobra/command_win.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/completions.go b/vendor/github.com/spf13/cobra/completions.go index e8a0206db..ee38c4d0b 100644 --- a/vendor/github.com/spf13/cobra/completions.go +++ b/vendor/github.com/spf13/cobra/completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -77,6 +77,10 @@ const ( // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs + // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order + // in which the completions are provided + ShellCompDirectiveKeepOrder + // =========================================================================== // All directives using iota should be above this one. @@ -159,6 +163,9 @@ func (d ShellCompDirective) string() string { if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } + if d&ShellCompDirectiveKeepOrder != 0 { + directives = append(directives, "ShellCompDirectiveKeepOrder") + } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } @@ -169,7 +176,7 @@ func (d ShellCompDirective) string() string { return strings.Join(directives, ", ") } -// Adds a special hidden command that can be used to request custom completions. +// initCompleteCmd adds a special hidden command that can be used to request custom completions. func (c *Command) initCompleteCmd(args []string) { completeCmd := &Command{ Use: fmt.Sprintf("%s [command-line]", ShellCompRequestCmd), @@ -727,7 +734,7 @@ to enable it. You can execute the following once: To load completions in your current shell session: - source <(%[1]s completion zsh); compdef _%[1]s %[1]s + source <(%[1]s completion zsh) To load completions for every new session, execute once: diff --git a/vendor/github.com/spf13/cobra/fish_completions.go b/vendor/github.com/spf13/cobra/fish_completions.go index 97112a17b..12ca0d2b1 100644 --- a/vendor/github.com/spf13/cobra/fish_completions.go +++ b/vendor/github.com/spf13/cobra/fish_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ function __%[1]s_perform_completion __%[1]s_debug "last arg: $lastArg" # Disable ActiveHelp which is not supported for fish shell - set -l requestComp "%[9]s=0 $args[1] %[3]s $args[2..-1] $lastArg" + set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg" __%[1]s_debug "Calling $requestComp" set -l results (eval $requestComp 2> /dev/null) @@ -89,6 +89,60 @@ function __%[1]s_perform_completion printf "%%s\n" "$directiveLine" end +# this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result +function __%[1]s_perform_completion_once + __%[1]s_debug "Starting __%[1]s_perform_completion_once" + + if test -n "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion" + return 0 + end + + set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion) + if test -z "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "No completions, probably due to a failure" + return 1 + end + + __%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result" + return 0 +end + +# this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run +function __%[1]s_clear_perform_completion_once_result + __%[1]s_debug "" + __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable ==========" + set --erase __%[1]s_perform_completion_once_result + __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result" +end + +function __%[1]s_requires_order_preservation + __%[1]s_debug "" + __%[1]s_debug "========= checking if order preservation is required ==========" + + __%[1]s_perform_completion_once + if test -z "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "Error determining if order preservation is required" + return 1 + end + + set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) + __%[1]s_debug "Directive is: $directive" + + set -l shellCompDirectiveKeepOrder %[9]d + set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2) + __%[1]s_debug "Keeporder is: $keeporder" + + if test $keeporder -ne 0 + __%[1]s_debug "This does require order preservation" + return 0 + end + + __%[1]s_debug "This doesn't require order preservation" + return 1 +end + + # This function does two things: # - Obtain the completions and store them in the global __%[1]s_comp_results # - Return false if file completion should be performed @@ -99,17 +153,17 @@ function __%[1]s_prepare_completions # Start fresh set --erase __%[1]s_comp_results - set -l results (__%[1]s_perform_completion) - __%[1]s_debug "Completion results: $results" + __%[1]s_perform_completion_once + __%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result" - if test -z "$results" + if test -z "$__%[1]s_perform_completion_once_result" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps return 1 end - set -l directive (string sub --start 2 $results[-1]) - set --global __%[1]s_comp_results $results[1..-2] + set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) + set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2] __%[1]s_debug "Completions are: $__%[1]s_comp_results" __%[1]s_debug "Directive is: $directive" @@ -205,13 +259,17 @@ end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e +# this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global +complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result' # The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results # which provides the program's completion choices. -complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' - +# If this doesn't require order preservation, we don't use the -k flag +complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' +# otherwise we use the -k flag +complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } // GenFishCompletion generates fish completion file and writes to the passed writer. diff --git a/vendor/github.com/spf13/cobra/flag_groups.go b/vendor/github.com/spf13/cobra/flag_groups.go index 9c377aaf9..b35fde155 100644 --- a/vendor/github.com/spf13/cobra/flag_groups.go +++ b/vendor/github.com/spf13/cobra/flag_groups.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/powershell_completions.go b/vendor/github.com/spf13/cobra/powershell_completions.go index 004de42e4..177d2755f 100644 --- a/vendor/github.com/spf13/cobra/powershell_completions.go +++ b/vendor/github.com/spf13/cobra/powershell_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -77,6 +77,7 @@ filter __%[1]s_escapeStringWithSpecialChars { $ShellCompDirectiveNoFileComp=%[6]d $ShellCompDirectiveFilterFileExt=%[7]d $ShellCompDirectiveFilterDirs=%[8]d + $ShellCompDirectiveKeepOrder=%[9]d # Prepare the command to request completions for the program. # Split the command at the first space to separate the program and arguments. @@ -106,13 +107,22 @@ filter __%[1]s_escapeStringWithSpecialChars { # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "Adding extra empty parameter" -`+" # We need to use `\"`\" to pass an empty argument a \"\" or '' does not work!!!"+` -`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+` + # PowerShell 7.2+ changed the way how the arguments are passed to executables, + # so for pre-7.2 or when Legacy argument passing is enabled we need to use +`+" # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+` + if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or + ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or + (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and + $PSNativeCommandArgumentPassing -eq 'Legacy')) { +`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+` + } else { + $RequestComp="$RequestComp" + ' ""' + } } __%[1]s_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell - $env:%[9]s=0 + $env:%[10]s=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element @@ -137,7 +147,7 @@ filter __%[1]s_escapeStringWithSpecialChars { } $Longest = 0 - $Values = $Out | ForEach-Object { + [Array]$Values = $Out | ForEach-Object { #Split the output in name and description `+" $Name, $Description = $_.Split(\"`t\",2)"+` __%[1]s_debug "Name: $Name Description: $Description" @@ -182,6 +192,11 @@ filter __%[1]s_escapeStringWithSpecialChars { } } + # we sort the values in ascending order by name if keep order isn't passed + if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { + $Values = $Values | Sort-Object -Property Name + } + if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { __%[1]s_debug "ShellCompDirectiveNoFileComp is called" @@ -267,7 +282,7 @@ filter __%[1]s_escapeStringWithSpecialChars { Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock `, name, nameForVar, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error { diff --git a/vendor/github.com/spf13/cobra/projects_using_cobra.md b/vendor/github.com/spf13/cobra/projects_using_cobra.md index 6865f88e7..8a291eb20 100644 --- a/vendor/github.com/spf13/cobra/projects_using_cobra.md +++ b/vendor/github.com/spf13/cobra/projects_using_cobra.md @@ -1,11 +1,13 @@ ## Projects using Cobra - [Allero](https://github.com/allero-io/allero) +- [Arewefastyet](https://benchmark.vitess.io) - [Arduino CLI](https://github.com/arduino/arduino-cli) - [Bleve](https://blevesearch.com/) - [Cilium](https://cilium.io/) - [CloudQuery](https://github.com/cloudquery/cloudquery) - [CockroachDB](https://www.cockroachlabs.com/) +- [Constellation](https://github.com/edgelesssys/constellation) - [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) - [Datree](https://github.com/datreeio/datree) - [Delve](https://github.com/derekparker/delve) @@ -25,7 +27,7 @@ - [Istio](https://istio.io) - [Kool](https://github.com/kool-dev/kool) - [Kubernetes](https://kubernetes.io/) -- [Kubescape](https://github.com/armosec/kubescape) +- [Kubescape](https://github.com/kubescape/kubescape) - [KubeVirt](https://github.com/kubevirt/kubevirt) - [Linkerd](https://linkerd.io/) - [Mattermost-server](https://github.com/mattermost/mattermost-server) @@ -51,10 +53,12 @@ - [Random](https://github.com/erdaltsksn/random) - [Rclone](https://rclone.org/) - [Scaleway CLI](https://github.com/scaleway/scaleway-cli) +- [Sia](https://github.com/SiaFoundation/siad) - [Skaffold](https://skaffold.dev/) - [Tendermint](https://github.com/tendermint/tendermint) - [Twitch CLI](https://github.com/twitchdev/twitch-cli) - [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli) +- [Vitess](https://vitess.io) - VMware's [Tanzu Community Edition](https://github.com/vmware-tanzu/community-edition) & [Tanzu Framework](https://github.com/vmware-tanzu/tanzu-framework) - [Werf](https://werf.io/) - [ZITADEL](https://github.com/zitadel/zitadel) diff --git a/vendor/github.com/spf13/cobra/shell_completions.go b/vendor/github.com/spf13/cobra/shell_completions.go index 126e83c30..b035742d3 100644 --- a/vendor/github.com/spf13/cobra/shell_completions.go +++ b/vendor/github.com/spf13/cobra/shell_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/spf13/cobra/shell_completions.md b/vendor/github.com/spf13/cobra/shell_completions.md index 553ee5df8..065c0621d 100644 --- a/vendor/github.com/spf13/cobra/shell_completions.md +++ b/vendor/github.com/spf13/cobra/shell_completions.md @@ -71,7 +71,7 @@ PowerShell: `,cmd.Root().Name()), DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { switch args[0] { case "bash": @@ -162,16 +162,7 @@ cmd := &cobra.Command{ } ``` -The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by -the completion algorithm if entered manually, e.g. in: - -```bash -$ kubectl get rc [tab][tab] -backend frontend database -``` - -Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of -replication controllers following `rc`. +The aliases are shown to the user on tab completion only if no completions were found within sub-commands or `ValidArgs`. ### Dynamic completion of nouns @@ -237,6 +228,10 @@ ShellCompDirectiveFilterFileExt // return []string{"themes"}, ShellCompDirectiveFilterDirs // ShellCompDirectiveFilterDirs + +// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order +// in which the completions are provided +ShellCompDirectiveKeepOrder ``` ***Note***: When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function. @@ -385,6 +380,19 @@ or ```go ValidArgs: []string{"bash\tCompletions for bash", "zsh\tCompletions for zsh"} ``` + +If you don't want to show descriptions in the completions, you can add `--no-descriptions` to the default `completion` command to disable them, like: + +```bash +$ source <(helm completion bash) +$ helm completion [tab][tab] +bash (generate autocompletion script for bash) powershell (generate autocompletion script for powershell) +fish (generate autocompletion script for fish) zsh (generate autocompletion script for zsh) + +$ source <(helm completion bash --no-descriptions) +$ helm completion [tab][tab] +bash fish powershell zsh +``` ## Bash completions ### Dependencies diff --git a/vendor/github.com/spf13/cobra/user_guide.md b/vendor/github.com/spf13/cobra/user_guide.md index 977306aa8..85201d840 100644 --- a/vendor/github.com/spf13/cobra/user_guide.md +++ b/vendor/github.com/spf13/cobra/user_guide.md @@ -188,6 +188,37 @@ var versionCmd = &cobra.Command{ } ``` +### Organizing subcommands + +A command may have subcommands which in turn may have other subcommands. This is achieved by using +`AddCommand`. In some cases, especially in larger applications, each subcommand may be defined in +its own go package. + +The suggested approach is for the parent command to use `AddCommand` to add its most immediate +subcommands. For example, consider the following directory structure: + +```text +├── cmd +│   ├── root.go +│   └── sub1 +│   ├── sub1.go +│   └── sub2 +│   ├── leafA.go +│   ├── leafB.go +│   └── sub2.go +└── main.go +``` + +In this case: + +* The `init` function of `root.go` adds the command defined in `sub1.go` to the root command. +* The `init` function of `sub1.go` adds the command defined in `sub2.go` to the sub1 command. +* The `init` function of `sub2.go` adds the commands defined in `leafA.go` and `leafB.go` to the + sub2 command. + +This approach ensures the subcommands are always included at compile time while avoiding cyclic +references. + ### Returning and handling errors If you wish to return an error to the caller of a command, `RunE` can be used. @@ -313,8 +344,8 @@ rootCmd.MarkFlagsRequiredTogether("username", "password") You can also prevent different flags from being provided together if they represent mutually exclusive options such as specifying an output format as either `--json` or `--yaml` but never both: ```go -rootCmd.Flags().BoolVar(&u, "json", false, "Output in JSON") -rootCmd.Flags().BoolVar(&pw, "yaml", false, "Output in YAML") +rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") +rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") ``` @@ -349,7 +380,7 @@ shown below: ```go var cmd = &cobra.Command{ Short: "hello", - Args: MatchAll(ExactArgs(2), OnlyValidArgs), + Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello, World!") }, @@ -492,10 +523,11 @@ around it. In fact, you can provide your own if you want. ### Grouping commands in help -Cobra supports grouping of available commands. Groups must be explicitly defined by `AddGroup` and set by -the `GroupId` element of a subcommand. The groups will appear in the same order as they are defined. -If you use the generated `help` or `completion` commands, you can set the group ids by `SetHelpCommandGroupId` -and `SetCompletionCommandGroupId`, respectively. +Cobra supports grouping of available commands in the help output. To group commands, each group must be explicitly +defined using `AddGroup()` on the parent command. Then a subcommand can be added to a group using the `GroupID` element +of that subcommand. The groups will appear in the help output in the same order as they are defined using different +calls to `AddGroup()`. If you use the generated `help` or `completion` commands, you can set their group ids using +`SetHelpCommandGroupId()` and `SetCompletionCommandGroupId()` on the root command, respectively. ### Defining your own help diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go index 84cec76fd..1856e4c7f 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions.go +++ b/vendor/github.com/spf13/cobra/zsh_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -90,6 +90,7 @@ func genZshComp(buf io.StringWriter, name string, includeDesc bool) { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s +compdef _%[1]s %[1]s # zsh completion for %-36[1]s -*- shell-script -*- @@ -108,8 +109,9 @@ _%[1]s() local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveKeepOrder=%[8]d - local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace + local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder local -a completions __%[1]s_debug "\n========= starting completion logic ==========" @@ -177,7 +179,7 @@ _%[1]s() return fi - local activeHelpMarker="%[8]s" + local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} local startIndex=$((${#activeHelpMarker}+1)) local hasActiveHelp=0 @@ -227,6 +229,11 @@ _%[1]s() noSpace="-S ''" fi + if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then + __%[1]s_debug "Activating keep order." + keepOrder="-V" + fi + if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd @@ -262,7 +269,7 @@ _%[1]s() return $result else __%[1]s_debug "Calling _describe" - if eval _describe "completions" completions $flagPrefix $noSpace; then + if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then __%[1]s_debug "_describe found some completions" # Return the success of having called _describe @@ -296,6 +303,6 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 95d8e59da..b774da88d 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 7880b8f94..84dbd6c79 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 339515b8b..b1d94aec5 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -30,9 +30,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -43,9 +43,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -98,7 +98,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -109,7 +109,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -119,7 +119,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -134,8 +134,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -146,8 +146,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -155,10 +155,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a return EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -169,7 +203,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -179,7 +213,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -193,10 +227,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,8 +259,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -237,8 +271,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -266,10 +300,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -280,7 +314,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -288,10 +322,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -301,7 +385,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -311,7 +395,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -353,7 +437,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -363,7 +447,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -391,9 +475,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -403,10 +487,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -416,10 +500,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -429,9 +513,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -442,7 +526,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -455,7 +539,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -468,7 +552,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -481,7 +565,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -493,7 +577,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -505,7 +589,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -517,7 +601,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -529,7 +613,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -541,7 +625,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -553,7 +637,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -565,7 +649,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -577,7 +661,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -589,7 +673,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -599,7 +683,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -609,7 +693,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -651,7 +735,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -693,9 +777,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -705,9 +789,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -717,9 +801,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -729,9 +813,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -741,9 +825,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -753,9 +837,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -765,9 +849,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -777,9 +861,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -805,7 +889,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -815,7 +899,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -826,7 +910,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -837,7 +921,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -847,9 +931,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -859,10 +943,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -872,10 +956,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -885,9 +969,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -897,8 +981,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -908,8 +992,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -920,7 +1004,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) b // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -931,7 +1015,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -941,7 +1025,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -951,7 +1035,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -979,10 +1063,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -992,10 +1076,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1024,9 +1108,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1037,9 +1121,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1050,9 +1134,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1063,9 +1147,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1075,7 +1159,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1088,7 +1172,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1098,7 +1182,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1108,7 +1192,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1139,7 +1223,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1149,7 +1233,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1159,7 +1243,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1169,7 +1253,7 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1179,8 +1263,8 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1190,8 +1274,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1201,7 +1285,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1214,7 +1298,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1228,7 +1312,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1239,7 +1323,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1265,7 +1349,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1277,7 +1361,7 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1289,7 +1373,7 @@ func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1300,7 +1384,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg str // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1311,7 +1395,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,7 +1405,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1331,8 +1415,8 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1342,8 +1426,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1353,8 +1437,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) b // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1364,8 +1448,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1375,7 +1459,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1388,7 +1472,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1402,7 +1486,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1413,7 +1497,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1423,7 +1507,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1433,7 +1517,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1443,7 +1527,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1453,7 +1537,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1463,7 +1547,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1473,7 +1557,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 759448783..00df62a05 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index fa1245b18..a55d1bba9 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -8,7 +8,6 @@ import ( "fmt" "math" "os" - "path/filepath" "reflect" "regexp" "runtime" @@ -76,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool { return bytes.Equal(exp, act) } +// copyExportedFields iterates downward through nested data structures and creates a copy +// that only contains the exported struct fields. +func copyExportedFields(expected interface{}) interface{} { + if isNil(expected) { + return expected + } + + expectedType := reflect.TypeOf(expected) + expectedKind := expectedType.Kind() + expectedValue := reflect.ValueOf(expected) + + switch expectedKind { + case reflect.Struct: + result := reflect.New(expectedType).Elem() + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.IsExported() + if isExported { + fieldValue := expectedValue.Field(i) + if isNil(fieldValue) || isNil(fieldValue.Interface()) { + continue + } + newValue := copyExportedFields(fieldValue.Interface()) + result.Field(i).Set(reflect.ValueOf(newValue)) + } + } + return result.Interface() + + case reflect.Ptr: + result := reflect.New(expectedType.Elem()) + unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) + result.Elem().Set(reflect.ValueOf(unexportedRemoved)) + return result.Interface() + + case reflect.Array, reflect.Slice: + result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + for i := 0; i < expectedValue.Len(); i++ { + index := expectedValue.Index(i) + if isNil(index) { + continue + } + unexportedRemoved := copyExportedFields(index.Interface()) + result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + case reflect.Map: + result := reflect.MakeMap(expectedType) + for _, k := range expectedValue.MapKeys() { + index := expectedValue.MapIndex(k) + unexportedRemoved := copyExportedFields(index.Interface()) + result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + default: + return expected + } +} + +// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are +// considered equal. This comparison of only exported fields is applied recursively to nested data +// structures. +// +// This function does no assertion of any kind. +func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { + expectedCleaned := copyExportedFields(expected) + actualCleaned := copyExportedFields(actual) + return ObjectsAreEqualValues(expectedCleaned, actualCleaned) +} + // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { @@ -141,12 +211,11 @@ func CallerInfo() []string { } parts := strings.Split(file, "/") - file = parts[len(parts)-1] if len(parts) > 1 { + filename := parts[len(parts)-1] dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - path, _ := filepath.Abs(file) - callers = append(callers, fmt.Sprintf("%s:%d", path, line)) + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } } @@ -273,7 +342,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -296,7 +365,7 @@ func labeledOutput(content ...labeledContent) string { // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -328,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -369,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error { // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -389,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -457,7 +526,7 @@ func truncatingFormat(data interface{}) string { // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -475,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + if aType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + } + + if bType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + } + + expected = copyExportedFields(expected) + actual = copyExportedFields(actual) + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true +} + // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true @@ -530,7 +643,7 @@ func isNil(object interface{}) bool { []reflect.Kind{ reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice}, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, kind) if isNilableKind && value.IsNil() { @@ -542,7 +655,7 @@ func isNil(object interface{}) bool { // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true @@ -585,7 +698,7 @@ func isEmpty(object interface{}) bool { // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -602,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { @@ -633,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) { // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { @@ -666,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { @@ -681,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -704,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -763,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -786,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -796,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true @@ -809,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -818,49 +931,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + if !av.IsValid() { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) } } return true } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) } } @@ -870,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -879,34 +987,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { + if !av.IsValid() { + return true + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return true } } @@ -914,8 +1016,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) @@ -1060,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1076,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1117,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1132,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1148,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1207,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1380,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { @@ -1397,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1415,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1438,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1472,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1490,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1603,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,7 +1829,7 @@ type tHelper interface { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1756,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index c9dccc4d6..4953981d3 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,39 +1,40 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // -// Example Usage +// # Example Usage // // The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// assert.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } // // if you assert many times, use the format below: // -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// func TestSomething(t *testing.T) { -// assert := assert.New(t) +// func TestSomething(t *testing.T) { +// assert := assert.New(t) // -// var a string = "Hello" -// var b string = "Hello" +// var a string = "Hello" +// var b string = "Hello" // -// assert.Equal(a, b, "The two words should be the same.") -// } +// assert.Equal(a, b, "The two words should be the same.") +// } // -// Assertions +// # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 4ed341dd2..d8038c28a 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index 169de3922..968434724 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,24 +1,25 @@ // Package require implements the same assertions as the `assert` package but // stops test execution when a test fails. // -// Example Usage +// # Example Usage // // The following is a complete example using require in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// require.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// require.Equal(t, a, b, "The two words should be the same.") // -// Assertions +// } +// +// # Assertions // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 880853f5a..63f852147 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -37,9 +37,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -53,9 +53,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -123,7 +123,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -137,7 +137,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -150,7 +150,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -168,8 +168,8 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,8 +183,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -195,10 +195,50 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args t.FailNow() } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -212,7 +252,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -225,7 +265,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -242,10 +282,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -283,8 +323,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -298,8 +338,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -336,10 +376,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,7 +393,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +404,66 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t t.FailNow() } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -380,7 +476,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -393,7 +489,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -450,7 +546,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -463,7 +559,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -500,9 +596,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -515,10 +611,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -531,10 +627,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -547,9 +643,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -563,7 +659,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -579,7 +675,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -595,7 +691,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -611,7 +707,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -626,7 +722,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -641,7 +737,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -656,7 +752,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -671,7 +767,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -686,7 +782,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -701,7 +797,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -716,7 +812,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -731,7 +827,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -746,7 +842,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -759,7 +855,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -772,7 +868,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -829,7 +925,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -886,9 +982,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -901,9 +997,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -916,9 +1012,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -931,9 +1027,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -946,9 +1042,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -961,9 +1057,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -976,9 +1072,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -991,9 +1087,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1028,7 +1124,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1137,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1055,7 +1151,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,7 +1165,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1082,9 +1178,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,10 +1193,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1113,10 +1209,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1129,9 +1225,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1144,8 +1240,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1158,8 +1254,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1173,7 +1269,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1187,7 +1283,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1200,7 +1296,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1213,7 +1309,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1250,10 +1346,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,10 +1362,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1307,9 +1403,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1323,9 +1419,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1339,9 +1435,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1355,9 +1451,9 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1370,7 +1466,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1386,7 +1482,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1399,7 +1495,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1412,7 +1508,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1452,7 +1548,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1465,7 +1561,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,7 +1574,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1491,7 +1587,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1504,8 +1600,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1518,8 +1614,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1532,7 +1628,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1548,7 +1644,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1565,7 +1661,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1579,7 +1675,7 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1614,7 +1710,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1629,7 +1725,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1644,7 +1740,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1658,7 +1754,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1672,7 +1768,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1685,7 +1781,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1698,8 +1794,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1712,8 +1808,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,8 +1822,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1740,8 +1836,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1754,7 +1850,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1770,7 +1866,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1787,7 +1883,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1801,7 +1897,7 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1814,7 +1910,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1827,7 +1923,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1840,7 +1936,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1853,7 +1949,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1866,7 +1962,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1879,7 +1975,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 960bf6f2c..3b5b09330 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -31,9 +31,9 @@ func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...inte // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -44,9 +44,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -99,7 +99,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -110,7 +110,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -120,7 +120,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -135,8 +135,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -147,8 +147,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -156,10 +156,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -170,7 +204,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -180,7 +214,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -194,10 +228,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -226,8 +260,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -238,8 +272,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -267,10 +301,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -281,7 +315,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -289,10 +323,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -302,7 +386,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -312,7 +396,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -354,7 +438,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -364,7 +448,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -392,9 +476,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -404,10 +488,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -417,10 +501,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -430,9 +514,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -443,7 +527,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -456,7 +540,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -469,7 +553,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -482,7 +566,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -494,7 +578,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -506,7 +590,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -518,7 +602,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -530,7 +614,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -542,7 +626,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -554,7 +638,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -566,7 +650,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -578,7 +662,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -590,7 +674,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -600,7 +684,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -610,7 +694,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -652,7 +736,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -694,9 +778,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -706,9 +790,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -718,9 +802,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -730,9 +814,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -742,9 +826,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -754,9 +838,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -766,9 +850,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -778,9 +862,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -806,7 +890,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -816,7 +900,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -827,7 +911,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -838,7 +922,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -848,9 +932,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -860,10 +944,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -873,10 +957,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -886,9 +970,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -898,8 +982,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -909,8 +993,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -921,7 +1005,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -932,7 +1016,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -942,7 +1026,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -952,7 +1036,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -980,10 +1064,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -993,10 +1077,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1025,9 +1109,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1038,9 +1122,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1051,9 +1135,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1064,9 +1148,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1076,7 +1160,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1089,7 +1173,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1099,7 +1183,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1109,7 +1193,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1140,7 +1224,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1150,7 +1234,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1160,7 +1244,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1170,7 +1254,7 @@ func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{} // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1180,8 +1264,8 @@ func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...inte // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1191,8 +1275,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1202,7 +1286,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1215,7 +1299,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1229,7 +1313,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1240,7 +1324,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1266,7 +1350,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1278,7 +1362,7 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1290,7 +1374,7 @@ func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, m // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1301,7 +1385,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1312,7 +1396,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFun // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1322,7 +1406,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFu // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1332,8 +1416,8 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1343,8 +1427,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1354,8 +1438,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1365,8 +1449,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1376,7 +1460,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1389,7 +1473,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1403,7 +1487,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1414,7 +1498,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1424,7 +1508,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1434,7 +1518,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1444,7 +1528,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1454,7 +1538,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1464,7 +1548,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1474,7 +1558,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/tinylib/msgp/msgp/advise_linux.go b/vendor/github.com/tinylib/msgp/msgp/advise_linux.go index 6c6bb37a5..d2a66857b 100644 --- a/vendor/github.com/tinylib/msgp/msgp/advise_linux.go +++ b/vendor/github.com/tinylib/msgp/msgp/advise_linux.go @@ -1,4 +1,5 @@ -// +build linux,!appengine +//go:build linux && !appengine && !tinygo +// +build linux,!appengine,!tinygo package msgp diff --git a/vendor/github.com/tinylib/msgp/msgp/advise_other.go b/vendor/github.com/tinylib/msgp/msgp/advise_other.go index da65ea541..1b6ed5727 100644 --- a/vendor/github.com/tinylib/msgp/msgp/advise_other.go +++ b/vendor/github.com/tinylib/msgp/msgp/advise_other.go @@ -1,4 +1,5 @@ -// +build !linux appengine +//go:build (!linux && !tinygo) || appengine +// +build !linux,!tinygo appengine package msgp diff --git a/vendor/github.com/tinylib/msgp/msgp/defs.go b/vendor/github.com/tinylib/msgp/msgp/defs.go index c634eef1d..e265aa4f8 100644 --- a/vendor/github.com/tinylib/msgp/msgp/defs.go +++ b/vendor/github.com/tinylib/msgp/msgp/defs.go @@ -5,16 +5,19 @@ // generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces. // // This package defines four "families" of functions: -// - AppendXxxx() appends an object to a []byte in MessagePack encoding. -// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. -// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. -// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. +// - AppendXxxx() appends an object to a []byte in MessagePack encoding. +// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. +// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. +// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. // // Once a type has satisfied the `Encodable` and `Decodable` interfaces, // it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using -// msgp.Encode(io.Writer, msgp.Encodable) +// +// msgp.Encode(io.Writer, msgp.Encodable) +// // and -// msgp.Decode(io.Reader, msgp.Decodable) +// +// msgp.Decode(io.Reader, msgp.Decodable) // // There are also methods for converting MessagePack to JSON without // an explicit de-serialization step. @@ -23,11 +26,13 @@ // the wiki at http://github.com/tinylib/msgp package msgp -const last4 = 0x0f -const first4 = 0xf0 -const last5 = 0x1f -const first3 = 0xe0 -const last7 = 0x7f +const ( + last4 = 0x0f + first4 = 0xf0 + last5 = 0x1f + first3 = 0xe0 + last7 = 0x7f +) func isfixint(b byte) bool { return b>>7 == 0 diff --git a/vendor/github.com/tinylib/msgp/msgp/elsize.go b/vendor/github.com/tinylib/msgp/msgp/elsize.go index 95762e7ee..a05b0b21c 100644 --- a/vendor/github.com/tinylib/msgp/msgp/elsize.go +++ b/vendor/github.com/tinylib/msgp/msgp/elsize.go @@ -1,72 +1,105 @@ package msgp -// size of every object on the wire, -// plus type information. gives us -// constant-time type information -// for traversing composite objects. -// -var sizes = [256]bytespec{ - mnil: {size: 1, extra: constsize, typ: NilType}, - mfalse: {size: 1, extra: constsize, typ: BoolType}, - mtrue: {size: 1, extra: constsize, typ: BoolType}, - mbin8: {size: 2, extra: extra8, typ: BinType}, - mbin16: {size: 3, extra: extra16, typ: BinType}, - mbin32: {size: 5, extra: extra32, typ: BinType}, - mext8: {size: 3, extra: extra8, typ: ExtensionType}, - mext16: {size: 4, extra: extra16, typ: ExtensionType}, - mext32: {size: 6, extra: extra32, typ: ExtensionType}, - mfloat32: {size: 5, extra: constsize, typ: Float32Type}, - mfloat64: {size: 9, extra: constsize, typ: Float64Type}, - muint8: {size: 2, extra: constsize, typ: UintType}, - muint16: {size: 3, extra: constsize, typ: UintType}, - muint32: {size: 5, extra: constsize, typ: UintType}, - muint64: {size: 9, extra: constsize, typ: UintType}, - mint8: {size: 2, extra: constsize, typ: IntType}, - mint16: {size: 3, extra: constsize, typ: IntType}, - mint32: {size: 5, extra: constsize, typ: IntType}, - mint64: {size: 9, extra: constsize, typ: IntType}, - mfixext1: {size: 3, extra: constsize, typ: ExtensionType}, - mfixext2: {size: 4, extra: constsize, typ: ExtensionType}, - mfixext4: {size: 6, extra: constsize, typ: ExtensionType}, - mfixext8: {size: 10, extra: constsize, typ: ExtensionType}, - mfixext16: {size: 18, extra: constsize, typ: ExtensionType}, - mstr8: {size: 2, extra: extra8, typ: StrType}, - mstr16: {size: 3, extra: extra16, typ: StrType}, - mstr32: {size: 5, extra: extra32, typ: StrType}, - marray16: {size: 3, extra: array16v, typ: ArrayType}, - marray32: {size: 5, extra: array32v, typ: ArrayType}, - mmap16: {size: 3, extra: map16v, typ: MapType}, - mmap32: {size: 5, extra: map32v, typ: MapType}, -} - -func init() { - // set up fixed fields +func calcBytespec(v byte) bytespec { + // single byte values + switch v { - // fixint - for i := mfixint; i < 0x80; i++ { - sizes[i] = bytespec{size: 1, extra: constsize, typ: IntType} + case mnil: + return bytespec{size: 1, extra: constsize, typ: NilType} + case mfalse: + return bytespec{size: 1, extra: constsize, typ: BoolType} + case mtrue: + return bytespec{size: 1, extra: constsize, typ: BoolType} + case mbin8: + return bytespec{size: 2, extra: extra8, typ: BinType} + case mbin16: + return bytespec{size: 3, extra: extra16, typ: BinType} + case mbin32: + return bytespec{size: 5, extra: extra32, typ: BinType} + case mext8: + return bytespec{size: 3, extra: extra8, typ: ExtensionType} + case mext16: + return bytespec{size: 4, extra: extra16, typ: ExtensionType} + case mext32: + return bytespec{size: 6, extra: extra32, typ: ExtensionType} + case mfloat32: + return bytespec{size: 5, extra: constsize, typ: Float32Type} + case mfloat64: + return bytespec{size: 9, extra: constsize, typ: Float64Type} + case muint8: + return bytespec{size: 2, extra: constsize, typ: UintType} + case muint16: + return bytespec{size: 3, extra: constsize, typ: UintType} + case muint32: + return bytespec{size: 5, extra: constsize, typ: UintType} + case muint64: + return bytespec{size: 9, extra: constsize, typ: UintType} + case mint8: + return bytespec{size: 2, extra: constsize, typ: IntType} + case mint16: + return bytespec{size: 3, extra: constsize, typ: IntType} + case mint32: + return bytespec{size: 5, extra: constsize, typ: IntType} + case mint64: + return bytespec{size: 9, extra: constsize, typ: IntType} + case mfixext1: + return bytespec{size: 3, extra: constsize, typ: ExtensionType} + case mfixext2: + return bytespec{size: 4, extra: constsize, typ: ExtensionType} + case mfixext4: + return bytespec{size: 6, extra: constsize, typ: ExtensionType} + case mfixext8: + return bytespec{size: 10, extra: constsize, typ: ExtensionType} + case mfixext16: + return bytespec{size: 18, extra: constsize, typ: ExtensionType} + case mstr8: + return bytespec{size: 2, extra: extra8, typ: StrType} + case mstr16: + return bytespec{size: 3, extra: extra16, typ: StrType} + case mstr32: + return bytespec{size: 5, extra: extra32, typ: StrType} + case marray16: + return bytespec{size: 3, extra: array16v, typ: ArrayType} + case marray32: + return bytespec{size: 5, extra: array32v, typ: ArrayType} + case mmap16: + return bytespec{size: 3, extra: map16v, typ: MapType} + case mmap32: + return bytespec{size: 5, extra: map32v, typ: MapType} } - // nfixint - for i := uint16(mnfixint); i < 0x100; i++ { - sizes[uint8(i)] = bytespec{size: 1, extra: constsize, typ: IntType} - } + switch { - // fixstr gets constsize, - // since the prefix yields the size - for i := mfixstr; i < 0xc0; i++ { - sizes[i] = bytespec{size: 1 + rfixstr(i), extra: constsize, typ: StrType} - } + // fixint + case v >= mfixint && v < 0x80: + return bytespec{size: 1, extra: constsize, typ: IntType} + + // fixstr gets constsize, since the prefix yields the size + case v >= mfixstr && v < 0xc0: + return bytespec{size: 1 + rfixstr(v), extra: constsize, typ: StrType} // fixmap - for i := mfixmap; i < 0x90; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(2 * rfixmap(i)), typ: MapType} - } + case v >= mfixmap && v < 0x90: + return bytespec{size: 1, extra: varmode(2 * rfixmap(v)), typ: MapType} // fixarray - for i := mfixarray; i < 0xa0; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(rfixarray(i)), typ: ArrayType} + case v >= mfixarray && v < 0xa0: + return bytespec{size: 1, extra: varmode(rfixarray(v)), typ: ArrayType} + + // nfixint + case v >= mnfixint && uint16(v) < 0x100: + return bytespec{size: 1, extra: constsize, typ: IntType} + } + + // 0xC1 is unused per the spec and falls through to here, + // everything else is covered above + + return bytespec{} +} + +func getType(v byte) Type { + return getBytespec(v).typ } // a valid bytespsec has @@ -85,15 +118,11 @@ type varmode int8 const ( constsize varmode = 0 // constant size (size bytes + uint8(varmode) objects) - extra8 = -1 // has uint8(p[1]) extra bytes - extra16 = -2 // has be16(p[1:]) extra bytes - extra32 = -3 // has be32(p[1:]) extra bytes - map16v = -4 // use map16 - map32v = -5 // use map32 - array16v = -6 // use array16 - array32v = -7 // use array32 + extra8 varmode = -1 // has uint8(p[1]) extra bytes + extra16 varmode = -2 // has be16(p[1:]) extra bytes + extra32 varmode = -3 // has be32(p[1:]) extra bytes + map16v varmode = -4 // use map16 + map32v varmode = -5 // use map32 + array16v varmode = -6 // use array16 + array32v varmode = -7 // use array32 ) - -func getType(v byte) Type { - return sizes[v].typ -} diff --git a/vendor/github.com/tinylib/msgp/msgp/elsize_default.go b/vendor/github.com/tinylib/msgp/msgp/elsize_default.go new file mode 100644 index 000000000..e7e8b547a --- /dev/null +++ b/vendor/github.com/tinylib/msgp/msgp/elsize_default.go @@ -0,0 +1,21 @@ +//go:build !tinygo +// +build !tinygo + +package msgp + +// size of every object on the wire, +// plus type information. gives us +// constant-time type information +// for traversing composite objects. +var sizes [256]bytespec + +func init() { + for i := 0; i < 256; i++ { + sizes[i] = calcBytespec(byte(i)) + } +} + +// getBytespec gets inlined to a simple array index +func getBytespec(v byte) bytespec { + return sizes[v] +} diff --git a/vendor/github.com/tinylib/msgp/msgp/elsize_tinygo.go b/vendor/github.com/tinylib/msgp/msgp/elsize_tinygo.go new file mode 100644 index 000000000..041f4ad69 --- /dev/null +++ b/vendor/github.com/tinylib/msgp/msgp/elsize_tinygo.go @@ -0,0 +1,13 @@ +//go:build tinygo +// +build tinygo + +package msgp + +// for tinygo, getBytespec just calls calcBytespec +// a simple/slow function with a switch statement - +// doesn't require any heap alloc, moves the space +// requirements into code instad of ram + +func getBytespec(v byte) bytespec { + return calcBytespec(v) +} diff --git a/vendor/github.com/tinylib/msgp/msgp/errors.go b/vendor/github.com/tinylib/msgp/msgp/errors.go index cc78a980c..4f19359a2 100644 --- a/vendor/github.com/tinylib/msgp/msgp/errors.go +++ b/vendor/github.com/tinylib/msgp/msgp/errors.go @@ -1,8 +1,8 @@ package msgp import ( - "fmt" "reflect" + "strconv" ) const resumableDefault = false @@ -69,7 +69,6 @@ func Resumable(e error) bool { // // ErrShortBytes is not wrapped with any context due to backward compatibility // issues with the public API. -// func WrapError(err error, ctx ...interface{}) error { switch e := err.(type) { case errShort: @@ -81,18 +80,6 @@ func WrapError(err error, ctx ...interface{}) error { } } -// ctxString converts the incoming interface{} slice into a single string. -func ctxString(ctx []interface{}) string { - out := "" - for idx, cv := range ctx { - if idx > 0 { - out += "/" - } - out += fmt.Sprintf("%v", cv) - } - return out -} - func addCtx(ctx, add string) string { if ctx != "" { return add + "/" + ctx @@ -110,7 +97,7 @@ type errWrapped struct { func (e errWrapped) Error() string { if e.ctx != "" { - return fmt.Sprintf("%s at %s", e.cause, e.ctx) + return e.cause.Error() + " at " + e.ctx } else { return e.cause.Error() } @@ -123,6 +110,9 @@ func (e errWrapped) Resumable() bool { return resumableDefault } +// Unwrap returns the cause. +func (e errWrapped) Unwrap() error { return e.cause } + type errShort struct{} func (e errShort) Error() string { return "msgp: too few bytes left to read object" } @@ -155,7 +145,7 @@ type ArrayError struct { // Error implements the error interface func (a ArrayError) Error() string { - out := fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got) + out := "msgp: wanted array of size " + strconv.Itoa(int(a.Wanted)) + "; got " + strconv.Itoa(int(a.Got)) if a.ctx != "" { out += " at " + a.ctx } @@ -178,7 +168,7 @@ type IntOverflow struct { // Error implements the error interface func (i IntOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize) + str := "msgp: " + strconv.FormatInt(i.Value, 10) + " overflows int" + strconv.Itoa(i.FailedBitsize) if i.ctx != "" { str += " at " + i.ctx } @@ -201,7 +191,7 @@ type UintOverflow struct { // Error implements the error interface func (u UintOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize) + str := "msgp: " + strconv.FormatUint(u.Value, 10) + " overflows uint" + strconv.Itoa(u.FailedBitsize) if u.ctx != "" { str += " at " + u.ctx } @@ -223,7 +213,7 @@ type UintBelowZero struct { // Error implements the error interface func (u UintBelowZero) Error() string { - str := fmt.Sprintf("msgp: attempted to cast int %d to unsigned", u.Value) + str := "msgp: attempted to cast int " + strconv.FormatInt(u.Value, 10) + " to unsigned" if u.ctx != "" { str += " at " + u.ctx } @@ -250,7 +240,7 @@ type TypeError struct { // Error implements the error interface func (t TypeError) Error() string { - out := fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method) + out := "msgp: attempted to decode type " + quoteStr(t.Encoded.String()) + " with method for " + quoteStr(t.Method.String()) if t.ctx != "" { out += " at " + t.ctx } @@ -266,7 +256,7 @@ func (t TypeError) withContext(ctx string) error { t.ctx = addCtx(t.ctx, ctx); r // TypeError depending on whether or not // the prefix is recognized func badPrefix(want Type, lead byte) error { - t := sizes[lead].typ + t := getType(lead) if t == InvalidType { return InvalidPrefixError(lead) } @@ -280,7 +270,7 @@ type InvalidPrefixError byte // Error implements the error interface func (i InvalidPrefixError) Error() string { - return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i)) + return "msgp: unrecognized type prefix 0x" + strconv.FormatInt(int64(i), 16) } // Resumable returns 'false' for InvalidPrefixErrors @@ -297,7 +287,7 @@ type ErrUnsupportedType struct { // Error implements error func (e *ErrUnsupportedType) Error() string { - out := fmt.Sprintf("msgp: type %q not supported", e.T) + out := "msgp: type " + quoteStr(e.T.String()) + " not supported" if e.ctx != "" { out += " at " + e.ctx } @@ -312,3 +302,58 @@ func (e *ErrUnsupportedType) withContext(ctx string) error { o.ctx = addCtx(o.ctx, ctx) return &o } + +// simpleQuoteStr is a simplified version of strconv.Quote for TinyGo, +// which takes up a lot less code space by escaping all non-ASCII +// (UTF-8) bytes with \x. Saves about 4k of code size +// (unicode tables, needed for IsPrint(), are big). +// It lives in errors.go just so we can test it in errors_test.go +func simpleQuoteStr(s string) string { + const ( + lowerhex = "0123456789abcdef" + ) + + sb := make([]byte, 0, len(s)+2) + + sb = append(sb, `"`...) + +l: // loop through string bytes (not UTF-8 characters) + for i := 0; i < len(s); i++ { + b := s[i] + // specific escape chars + switch b { + case '\\': + sb = append(sb, `\\`...) + case '"': + sb = append(sb, `\"`...) + case '\a': + sb = append(sb, `\a`...) + case '\b': + sb = append(sb, `\b`...) + case '\f': + sb = append(sb, `\f`...) + case '\n': + sb = append(sb, `\n`...) + case '\r': + sb = append(sb, `\r`...) + case '\t': + sb = append(sb, `\t`...) + case '\v': + sb = append(sb, `\v`...) + default: + // no escaping needed (printable ASCII) + if b >= 0x20 && b <= 0x7E { + sb = append(sb, b) + continue l + } + // anything else is \x + sb = append(sb, `\x`...) + sb = append(sb, lowerhex[byte(b)>>4]) + sb = append(sb, lowerhex[byte(b)&0xF]) + continue l + } + } + + sb = append(sb, `"`...) + return string(sb) +} diff --git a/vendor/github.com/tinylib/msgp/msgp/errors_default.go b/vendor/github.com/tinylib/msgp/msgp/errors_default.go new file mode 100644 index 000000000..e45c00a8b --- /dev/null +++ b/vendor/github.com/tinylib/msgp/msgp/errors_default.go @@ -0,0 +1,25 @@ +//go:build !tinygo +// +build !tinygo + +package msgp + +import ( + "fmt" + "strconv" +) + +// ctxString converts the incoming interface{} slice into a single string. +func ctxString(ctx []interface{}) string { + out := "" + for idx, cv := range ctx { + if idx > 0 { + out += "/" + } + out += fmt.Sprintf("%v", cv) + } + return out +} + +func quoteStr(s string) string { + return strconv.Quote(s) +} diff --git a/vendor/github.com/tinylib/msgp/msgp/errors_tinygo.go b/vendor/github.com/tinylib/msgp/msgp/errors_tinygo.go new file mode 100644 index 000000000..8691cd387 --- /dev/null +++ b/vendor/github.com/tinylib/msgp/msgp/errors_tinygo.go @@ -0,0 +1,42 @@ +//go:build tinygo +// +build tinygo + +package msgp + +import ( + "reflect" +) + +// ctxString converts the incoming interface{} slice into a single string, +// without using fmt under tinygo +func ctxString(ctx []interface{}) string { + out := "" + for idx, cv := range ctx { + if idx > 0 { + out += "/" + } + out += ifToStr(cv) + } + return out +} + +type stringer interface { + String() string +} + +func ifToStr(i interface{}) string { + switch v := i.(type) { + case stringer: + return v.String() + case error: + return v.Error() + case string: + return v + default: + return reflect.ValueOf(i).String() + } +} + +func quoteStr(s string) string { + return simpleQuoteStr(s) +} diff --git a/vendor/github.com/tinylib/msgp/msgp/extension.go b/vendor/github.com/tinylib/msgp/msgp/extension.go index b2e110851..b5ef3a4e3 100644 --- a/vendor/github.com/tinylib/msgp/msgp/extension.go +++ b/vendor/github.com/tinylib/msgp/msgp/extension.go @@ -1,8 +1,9 @@ package msgp import ( - "fmt" + "errors" "math" + "strconv" ) const ( @@ -30,7 +31,7 @@ var extensionReg = make(map[int8]func() Extension) // // For example, if you wanted to register a user-defined struct: // -// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) +// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) // // RegisterExtension will panic if you call it multiple times // with the same 'typ' argument, or if you use a reserved @@ -38,10 +39,10 @@ var extensionReg = make(map[int8]func() Extension) func RegisterExtension(typ int8, f func() Extension) { switch typ { case Complex64Extension, Complex128Extension, TimeExtension: - panic(fmt.Sprint("msgp: forbidden extension type:", typ)) + panic(errors.New("msgp: forbidden extension type: " + strconv.Itoa(int(typ)))) } if _, ok := extensionReg[typ]; ok { - panic(fmt.Sprint("msgp: RegisterExtension() called with typ", typ, "more than once")) + panic(errors.New("msgp: RegisterExtension() called with typ " + strconv.Itoa(int(typ)) + " more than once")) } extensionReg[typ] = f } @@ -56,7 +57,7 @@ type ExtensionTypeError struct { // Error implements the error interface func (e ExtensionTypeError) Error() string { - return fmt.Sprintf("msgp: error decoding extension: wanted type %d; got type %d", e.Want, e.Got) + return "msgp: error decoding extension: wanted type " + strconv.Itoa(int(e.Want)) + "; got type " + strconv.Itoa(int(e.Got)) } // Resumable returns 'true' for ExtensionTypeErrors @@ -230,7 +231,7 @@ func (m *Reader) peekExtensionType() (int8, error) { if err != nil { return 0, err } - spec := sizes[p[0]] + spec := getBytespec(p[0]) if spec.typ != ExtensionType { return 0, badPrefix(ExtensionType, p[0]) } @@ -248,7 +249,7 @@ func (m *Reader) peekExtensionType() (int8, error) { // peekExtension peeks at the extension encoding type // (must guarantee at least 1 byte in 'b') func peekExtension(b []byte) (int8, error) { - spec := sizes[b[0]] + spec := getBytespec(b[0]) size := spec.size if spec.typ != ExtensionType { return 0, badPrefix(ExtensionType, b[0]) diff --git a/vendor/github.com/tinylib/msgp/msgp/file.go b/vendor/github.com/tinylib/msgp/msgp/file.go index 8e7370ebc..0f2c37520 100644 --- a/vendor/github.com/tinylib/msgp/msgp/file.go +++ b/vendor/github.com/tinylib/msgp/msgp/file.go @@ -1,5 +1,7 @@ +//go:build (linux || darwin || dragonfly || freebsd || netbsd || openbsd) && !appengine && !tinygo // +build linux darwin dragonfly freebsd netbsd openbsd // +build !appengine +// +build !tinygo package msgp @@ -20,7 +22,6 @@ import ( // is only efficient for large files; small // files are best read and written using // the ordinary streaming interfaces. -// func ReadFile(dst Unmarshaler, file *os.File) error { stat, err := file.Stat() if err != nil { diff --git a/vendor/github.com/tinylib/msgp/msgp/file_port.go b/vendor/github.com/tinylib/msgp/msgp/file_port.go index 6e654dbdc..2bbb3ad13 100644 --- a/vendor/github.com/tinylib/msgp/msgp/file_port.go +++ b/vendor/github.com/tinylib/msgp/msgp/file_port.go @@ -1,4 +1,5 @@ -// +build windows appengine +//go:build windows || appengine || tinygo +// +build windows appengine tinygo package msgp diff --git a/vendor/github.com/tinylib/msgp/msgp/json.go b/vendor/github.com/tinylib/msgp/msgp/json.go index 77601e52c..0e11e603c 100644 --- a/vendor/github.com/tinylib/msgp/msgp/json.go +++ b/vendor/github.com/tinylib/msgp/msgp/json.go @@ -206,7 +206,7 @@ func rwFloat32(dst jsWriter, src *Reader) (int, error) { if err != nil { return 0, err } - src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 64) + src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 32) return dst.Write(src.scratch) } @@ -215,7 +215,7 @@ func rwFloat64(dst jsWriter, src *Reader) (int, error) { if err != nil { return 0, err } - src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 32) + src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 64) return dst.Write(src.scratch) } diff --git a/vendor/github.com/tinylib/msgp/msgp/json_bytes.go b/vendor/github.com/tinylib/msgp/msgp/json_bytes.go index 438caf539..e6162d0a6 100644 --- a/vendor/github.com/tinylib/msgp/msgp/json_bytes.go +++ b/vendor/github.com/tinylib/msgp/msgp/json_bytes.go @@ -12,7 +12,6 @@ import ( var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error) func init() { - // NOTE(pmh): this is best expressed as a jump table, // but gc doesn't do that yet. revisit post-go1.5. unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){ @@ -223,27 +222,6 @@ func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) return msg, scratch, err } -func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) { - var f float64 - var err error - var sz int - if f64 { - sz = 64 - f, msg, err = ReadFloat64Bytes(msg) - } else { - sz = 32 - var v float32 - v, msg, err = ReadFloat32Bytes(msg) - f = float64(v) - } - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz) - _, err = w.Write(scratch) - return msg, scratch, err -} - func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { var f float32 var err error diff --git a/vendor/github.com/tinylib/msgp/msgp/number.go b/vendor/github.com/tinylib/msgp/msgp/number.go index ad07ef995..edfe328b4 100644 --- a/vendor/github.com/tinylib/msgp/msgp/number.go +++ b/vendor/github.com/tinylib/msgp/msgp/number.go @@ -29,7 +29,6 @@ type Number struct { // AsInt sets the number to an int64. func (n *Number) AsInt(i int64) { - // we always store int(0) // as {0, InvalidType} in // order to preserve diff --git a/vendor/github.com/tinylib/msgp/msgp/purego.go b/vendor/github.com/tinylib/msgp/msgp/purego.go index c828f7eca..2cd35c3e1 100644 --- a/vendor/github.com/tinylib/msgp/msgp/purego.go +++ b/vendor/github.com/tinylib/msgp/msgp/purego.go @@ -1,3 +1,4 @@ +//go:build purego || appengine // +build purego appengine package msgp diff --git a/vendor/github.com/tinylib/msgp/msgp/read.go b/vendor/github.com/tinylib/msgp/msgp/read.go index aa668c573..e6d72f17d 100644 --- a/vendor/github.com/tinylib/msgp/msgp/read.go +++ b/vendor/github.com/tinylib/msgp/msgp/read.go @@ -36,6 +36,7 @@ const ( IntType UintType NilType + DurationType ExtensionType // pseudo-types provided @@ -126,6 +127,11 @@ func NewReaderSize(r io.Reader, sz int) *Reader { return &Reader{R: fwd.NewReaderSize(r, sz)} } +// NewReaderBuf returns a *Reader with a provided buffer. +func NewReaderBuf(r io.Reader, buf []byte) *Reader { + return &Reader{R: fwd.NewReaderBuf(r, buf)} +} + // Reader wraps an io.Reader and provides // methods to read MessagePack-encoded values // from it. Readers are buffered. @@ -257,7 +263,7 @@ func getNextSize(r *fwd.Reader) (uintptr, uintptr, error) { return 0, 0, err } lead := b[0] - spec := &sizes[lead] + spec := getBytespec(lead) size, mode := spec.size, spec.extra if size == 0 { return 0, 0, InvalidPrefixError(lead) @@ -389,7 +395,7 @@ func (m *Reader) ReadMapKey(scratch []byte) ([]byte, error) { return out, nil } -// MapKeyPtr returns a []byte pointing to the contents +// ReadMapKeyPtr returns a []byte pointing to the contents // of a valid map key. The key cannot be empty, and it // must be shorter than the total buffer size of the // *Reader. Additionally, the returned slice is only @@ -554,6 +560,12 @@ func (m *Reader) ReadBool() (b bool, err error) { return } +// ReadDuration reads a time.Duration from the reader +func (m *Reader) ReadDuration() (d time.Duration, err error) { + i, err := m.ReadInt64() + return time.Duration(i), err +} + // ReadInt64 reads an int64 from the reader func (m *Reader) ReadInt64() (i int64, err error) { var p []byte @@ -1297,6 +1309,10 @@ func (m *Reader) ReadIntf() (i interface{}, err error) { i, err = m.ReadTime() return + case DurationType: + i, err = m.ReadDuration() + return + case ExtensionType: var t int8 t, err = m.peekExtensionType() diff --git a/vendor/github.com/tinylib/msgp/msgp/read_bytes.go b/vendor/github.com/tinylib/msgp/msgp/read_bytes.go index e41997578..a204ac4b9 100644 --- a/vendor/github.com/tinylib/msgp/msgp/read_bytes.go +++ b/vendor/github.com/tinylib/msgp/msgp/read_bytes.go @@ -12,12 +12,12 @@ var big = binary.BigEndian // NextType returns the type of the next // object in the slice. If the length // of the input is zero, it returns -// InvalidType. +// [InvalidType]. func NextType(b []byte) Type { if len(b) == 0 { return InvalidType } - spec := sizes[b[0]] + spec := getBytespec(b[0]) t := spec.typ if t == ExtensionType && len(b) > int(spec.size) { var tp int8 @@ -55,7 +55,7 @@ func IsNil(b []byte) bool { // data without interpreting its contents. type Raw []byte -// MarshalMsg implements msgp.Marshaler. +// MarshalMsg implements [Marshaler]. // It appends the raw contents of 'raw' // to the provided byte slice. If 'raw' // is 0 bytes, 'nil' will be appended instead. @@ -69,7 +69,7 @@ func (r Raw) MarshalMsg(b []byte) ([]byte, error) { return o, nil } -// UnmarshalMsg implements msgp.Unmarshaler. +// UnmarshalMsg implements [Unmarshaler]. // It sets the contents of *Raw to be the next // object in the provided byte slice. func (r *Raw) UnmarshalMsg(b []byte) ([]byte, error) { @@ -91,7 +91,7 @@ func (r *Raw) UnmarshalMsg(b []byte) ([]byte, error) { return out, nil } -// EncodeMsg implements msgp.Encodable. +// EncodeMsg implements [Encodable]. // It writes the raw bytes to the writer. // If r is empty, it writes 'nil' instead. func (r Raw) EncodeMsg(w *Writer) error { @@ -102,7 +102,7 @@ func (r Raw) EncodeMsg(w *Writer) error { return err } -// DecodeMsg implements msgp.Decodable. +// DecodeMsg implements [Decodable]. // It sets the value of *Raw to be the // next object on the wire. func (r *Raw) DecodeMsg(f *Reader) error { @@ -114,7 +114,7 @@ func (r *Raw) DecodeMsg(f *Reader) error { return err } -// Msgsize implements msgp.Sizer +// Msgsize implements [Sizer]. func (r Raw) Msgsize() int { l := len(r) if l == 0 { @@ -144,7 +144,7 @@ func appendNext(f *Reader, d *[]byte) error { return nil } -// MarshalJSON implements json.Marshaler +// MarshalJSON implements [json.Marshaler]. func (r *Raw) MarshalJSON() ([]byte, error) { var buf bytes.Buffer _, err := UnmarshalAsJSON(&buf, []byte(*r)) @@ -153,9 +153,11 @@ func (r *Raw) MarshalJSON() ([]byte, error) { // ReadMapHeaderBytes reads a map header size // from 'b' and returns the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a map) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a map) func ReadMapHeaderBytes(b []byte) (sz uint32, o []byte, err error) { l := len(b) if l < 1 { @@ -197,9 +199,11 @@ func ReadMapHeaderBytes(b []byte) (sz uint32, o []byte, err error) { // ReadMapKeyZC attempts to read a map key // from 'b' and returns the key bytes and the remaining bytes +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a str or bin) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a str or bin) func ReadMapKeyZC(b []byte) ([]byte, []byte, error) { o, x, err := ReadStringZC(b) if err != nil { @@ -214,9 +218,11 @@ func ReadMapKeyZC(b []byte) ([]byte, []byte, error) { // ReadArrayHeaderBytes attempts to read // the array header size off of 'b' and return // the size and remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not an array) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not an array) func ReadArrayHeaderBytes(b []byte) (sz uint32, o []byte, err error) { if len(b) < 1 { return 0, nil, ErrShortBytes @@ -253,12 +259,56 @@ func ReadArrayHeaderBytes(b []byte) (sz uint32, o []byte, err error) { } } +// ReadBytesHeader reads the 'bin' header size +// off of 'b' and returns the size and remaining bytes. +// +// Possible errors: +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a bin object) +func ReadBytesHeader(b []byte) (sz uint32, o []byte, err error) { + if len(b) < 1 { + return 0, nil, ErrShortBytes + } + switch b[0] { + case mbin8: + if len(b) < 2 { + err = ErrShortBytes + return + } + sz = uint32(b[1]) + o = b[2:] + return + case mbin16: + if len(b) < 3 { + err = ErrShortBytes + return + } + sz = uint32(big.Uint16(b[1:])) + o = b[3:] + return + case mbin32: + if len(b) < 5 { + err = ErrShortBytes + return + } + sz = big.Uint32(b[1:]) + o = b[5:] + return + default: + err = badPrefix(BinType, b[0]) + return + } +} + // ReadNilBytes tries to read a "nil" byte // off of 'b' and return the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'nil') -// - InvalidPrefixError +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a 'nil') +// - [InvalidPrefixError] func ReadNilBytes(b []byte) ([]byte, error) { if len(b) < 1 { return nil, ErrShortBytes @@ -271,9 +321,11 @@ func ReadNilBytes(b []byte) ([]byte, error) { // ReadFloat64Bytes tries to read a float64 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float64) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a float64) func ReadFloat64Bytes(b []byte) (f float64, o []byte, err error) { if len(b) < 9 { if len(b) >= 5 && b[0] == mfloat32 { @@ -304,9 +356,11 @@ func ReadFloat64Bytes(b []byte) (f float64, o []byte, err error) { // ReadFloat32Bytes tries to read a float64 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float32) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a float32) func ReadFloat32Bytes(b []byte) (f float32, o []byte, err error) { if len(b) < 5 { err = ErrShortBytes @@ -325,9 +379,11 @@ func ReadFloat32Bytes(b []byte) (f float32, o []byte, err error) { // ReadBoolBytes tries to read a float64 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a bool) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a bool) func ReadBoolBytes(b []byte) (bool, []byte, error) { if len(b) < 1 { return false, b, ErrShortBytes @@ -342,11 +398,25 @@ func ReadBoolBytes(b []byte) (bool, []byte, error) { } } +// ReadDurationBytes tries to read a time.Duration +// from 'b' and return the value and the remaining bytes. +// +// Possible errors: +// +// - [ErrShortBytes] (too few bytes) +// - TypeError (not a int) +func ReadDurationBytes(b []byte) (d time.Duration, o []byte, err error) { + i, o, err := ReadInt64Bytes(b) + return time.Duration(i), o, err +} + // ReadInt64Bytes tries to read an int64 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError (not a int) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a int) func ReadInt64Bytes(b []byte) (i int64, o []byte, err error) { l := len(b) if l < 1 { @@ -451,10 +521,12 @@ func ReadInt64Bytes(b []byte) (i int64, o []byte, err error) { // ReadInt32Bytes tries to read an int32 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int32) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a int) +// - [IntOverflow] (value doesn't fit in int32) func ReadInt32Bytes(b []byte) (int32, []byte, error) { i, o, err := ReadInt64Bytes(b) if i > math.MaxInt32 || i < math.MinInt32 { @@ -465,10 +537,12 @@ func ReadInt32Bytes(b []byte) (int32, []byte, error) { // ReadInt16Bytes tries to read an int16 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int16) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a int) +// - [IntOverflow] (value doesn't fit in int16) func ReadInt16Bytes(b []byte) (int16, []byte, error) { i, o, err := ReadInt64Bytes(b) if i > math.MaxInt16 || i < math.MinInt16 { @@ -479,10 +553,12 @@ func ReadInt16Bytes(b []byte) (int16, []byte, error) { // ReadInt8Bytes tries to read an int16 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int8) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a int) +// - [IntOverflow] (value doesn't fit in int8) func ReadInt8Bytes(b []byte) (int8, []byte, error) { i, o, err := ReadInt64Bytes(b) if i > math.MaxInt8 || i < math.MinInt8 { @@ -493,10 +569,12 @@ func ReadInt8Bytes(b []byte) (int8, []byte, error) { // ReadIntBytes tries to read an int // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int; 32-bit platforms only) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a int) +// - [IntOverflow] (value doesn't fit in int; 32-bit platforms only) func ReadIntBytes(b []byte) (int, []byte, error) { if smallint { i, b, err := ReadInt32Bytes(b) @@ -508,9 +586,11 @@ func ReadIntBytes(b []byte) (int, []byte, error) { // ReadUint64Bytes tries to read a uint64 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a uint) func ReadUint64Bytes(b []byte) (u uint64, o []byte, err error) { l := len(b) if l < 1 { @@ -629,10 +709,12 @@ func ReadUint64Bytes(b []byte) (u uint64, o []byte, err error) { // ReadUint32Bytes tries to read a uint32 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint32) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a uint) +// - [UintOverflow] (value too large for uint32) func ReadUint32Bytes(b []byte) (uint32, []byte, error) { v, o, err := ReadUint64Bytes(b) if v > math.MaxUint32 { @@ -643,10 +725,12 @@ func ReadUint32Bytes(b []byte) (uint32, []byte, error) { // ReadUint16Bytes tries to read a uint16 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint16) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a uint) +// - [UintOverflow] (value too large for uint16) func ReadUint16Bytes(b []byte) (uint16, []byte, error) { v, o, err := ReadUint64Bytes(b) if v > math.MaxUint16 { @@ -657,10 +741,12 @@ func ReadUint16Bytes(b []byte) (uint16, []byte, error) { // ReadUint8Bytes tries to read a uint8 // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint8) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a uint) +// - [UintOverflow] (value too large for uint8) func ReadUint8Bytes(b []byte) (uint8, []byte, error) { v, o, err := ReadUint64Bytes(b) if v > math.MaxUint8 { @@ -671,10 +757,12 @@ func ReadUint8Bytes(b []byte) (uint8, []byte, error) { // ReadUintBytes tries to read a uint // from 'b' and return the value and the remaining bytes. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint; 32-bit platforms only) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a uint) +// - [UintOverflow] (value too large for uint; 32-bit platforms only) func ReadUintBytes(b []byte) (uint, []byte, error) { if smallint { u, b, err := ReadUint32Bytes(b) @@ -692,9 +780,11 @@ func ReadByteBytes(b []byte) (byte, []byte, error) { // ReadBytesBytes reads a 'bin' object // from 'b' and returns its vaue and // the remaining bytes in 'b'. +// // Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'bin' object) +// +// - [ErrShortBytes] (too few bytes) +// - [TypeError] (not a 'bin' object) func ReadBytesBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { return readBytesBytes(b, scratch, false) } @@ -763,9 +853,11 @@ func readBytesBytes(b []byte, scratch []byte, zc bool) (v []byte, o []byte, err // ReadBytesZC extracts the messagepack-encoded // binary field without copying. The returned []byte // points to the same memory as the input slice. +// // Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'bin') +// +// - [ErrShortBytes] (b not long enough) +// - [TypeError] (object not 'bin') func ReadBytesZC(b []byte) (v []byte, o []byte, err error) { return readBytesBytes(b, nil, true) } @@ -823,9 +915,11 @@ func ReadExactBytes(b []byte, into []byte) (o []byte, err error) { // ReadStringZC reads a messagepack string field // without copying. The returned []byte points // to the same memory as the input slice. +// // Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'str') +// +// - [ErrShortBytes] (b not long enough) +// - [TypeError] (object not 'str') func ReadStringZC(b []byte) (v []byte, o []byte, err error) { l := len(b) if l < 1 { @@ -883,10 +977,12 @@ func ReadStringZC(b []byte) (v []byte, o []byte, err error) { // ReadStringBytes reads a 'str' object // from 'b' and returns its value and the // remaining bytes in 'b'. +// // Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError +// +// - [ErrShortBytes] (b not long enough) +// - [TypeError] (not 'str' type) +// - [InvalidPrefixError] func ReadStringBytes(b []byte) (string, []byte, error) { v, o, err := ReadStringZC(b) return string(v), o, err @@ -896,11 +992,13 @@ func ReadStringBytes(b []byte) (string, []byte, error) { // into a slice of bytes. 'v' is the value of // the 'str' object, which may reside in memory // pointed to by 'scratch.' 'o' is the remaining bytes -// in 'b.'' +// in 'b'. +// // Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError (unknown type marker) +// +// - [ErrShortBytes] (b not long enough) +// - [TypeError] (not 'str' type) +// - [InvalidPrefixError] (unknown type marker) func ReadStringAsBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { var tmp []byte tmp, o, err = ReadStringZC(b) @@ -911,11 +1009,13 @@ func ReadStringAsBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) // ReadComplex128Bytes reads a complex128 // extension object from 'b' and returns the // remaining bytes. +// // Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex128) -// - InvalidPrefixError -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex128) +// +// - [ErrShortBytes] (not enough bytes in 'b') +// - [TypeError] (object not a complex128) +// - [InvalidPrefixError] +// - [ExtensionTypeError] (object an extension of the correct size, but not a complex128) func ReadComplex128Bytes(b []byte) (c complex128, o []byte, err error) { if len(b) < 18 { err = ErrShortBytes @@ -938,10 +1038,12 @@ func ReadComplex128Bytes(b []byte) (c complex128, o []byte, err error) { // ReadComplex64Bytes reads a complex64 // extension object from 'b' and returns the // remaining bytes. +// // Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex64) +// +// - [ErrShortBytes] (not enough bytes in 'b') +// - [TypeError] (object not a complex64) +// - [ExtensionTypeError] (object an extension of the correct size, but not a complex64) func ReadComplex64Bytes(b []byte) (c complex64, o []byte, err error) { if len(b) < 10 { err = ErrShortBytes @@ -964,10 +1066,12 @@ func ReadComplex64Bytes(b []byte) (c complex64, o []byte, err error) { // ReadTimeBytes reads a time.Time // extension object from 'b' and returns the // remaining bytes. +// // Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a time.Time) +// +// - [ErrShortBytes] (not enough bytes in 'b') +// - [TypeError] (object not a complex64) +// - [ExtensionTypeError] (object an extension of the correct size, but not a time.Time) func ReadTimeBytes(b []byte) (t time.Time, o []byte, err error) { if len(b) < 15 { err = ErrShortBytes @@ -1136,9 +1240,11 @@ func ReadIntfBytes(b []byte) (i interface{}, o []byte, err error) { // returns the remaining bytes. If the object // is a map or array, all of its elements // will be skipped. -// Possible Errors: -// - ErrShortBytes (not enough bytes in b) -// - InvalidPrefixError (bad encoding) +// +// Possible errors: +// +// - [ErrShortBytes] (not enough bytes in b) +// - [InvalidPrefixError] (bad encoding) func Skip(b []byte) ([]byte, error) { sz, asz, err := getSize(b) if err != nil { @@ -1165,7 +1271,7 @@ func getSize(b []byte) (uintptr, uintptr, error) { return 0, 0, ErrShortBytes } lead := b[0] - spec := &sizes[lead] // get type information + spec := getBytespec(lead) // get type information size, mode := spec.size, spec.extra if size == 0 { return 0, 0, InvalidPrefixError(lead) diff --git a/vendor/github.com/tinylib/msgp/msgp/size.go b/vendor/github.com/tinylib/msgp/msgp/size.go index ce2f8b16f..e3a613b24 100644 --- a/vendor/github.com/tinylib/msgp/msgp/size.go +++ b/vendor/github.com/tinylib/msgp/msgp/size.go @@ -25,9 +25,10 @@ const ( Complex64Size = 10 Complex128Size = 18 - TimeSize = 15 - BoolSize = 1 - NilSize = 1 + DurationSize = Int64Size + TimeSize = 15 + BoolSize = 1 + NilSize = 1 MapHeaderSize = 5 ArrayHeaderSize = 5 diff --git a/vendor/github.com/tinylib/msgp/msgp/unsafe.go b/vendor/github.com/tinylib/msgp/msgp/unsafe.go index 3978b6ff6..06e8d8437 100644 --- a/vendor/github.com/tinylib/msgp/msgp/unsafe.go +++ b/vendor/github.com/tinylib/msgp/msgp/unsafe.go @@ -1,9 +1,9 @@ +//go:build !purego && !appengine // +build !purego,!appengine package msgp import ( - "reflect" "unsafe" ) @@ -24,18 +24,14 @@ const ( // THIS IS EVIL CODE. // YOU HAVE BEEN WARNED. func UnsafeString(b []byte) string { - sh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: sh.Data, Len: sh.Len})) + return *(*string)(unsafe.Pointer(&b)) } // UnsafeBytes returns the string as a byte slice -// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR. -// THIS IS EVIL CODE. -// YOU HAVE BEEN WARNED. +// +// Deprecated: +// Since this code is no longer used by the code generator, +// UnsafeBytes(s) is precisely equivalent to []byte(s) func UnsafeBytes(s string) []byte { - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Len: len(s), - Cap: len(s), - Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data, - })) + return []byte(s) } diff --git a/vendor/github.com/tinylib/msgp/msgp/write.go b/vendor/github.com/tinylib/msgp/msgp/write.go index fb1947c57..ec2f6f528 100644 --- a/vendor/github.com/tinylib/msgp/msgp/write.go +++ b/vendor/github.com/tinylib/msgp/msgp/write.go @@ -2,7 +2,6 @@ package msgp import ( "errors" - "fmt" "io" "math" "reflect" @@ -10,6 +9,11 @@ import ( "time" ) +const ( + // min buffer size for the writer + minWriterSize = 18 +) + // Sizer is an interface implemented // by types that can estimate their // size when MessagePack encoded. @@ -120,16 +124,27 @@ func NewWriter(w io.Writer) *Writer { // NewWriterSize returns a writer with a custom buffer size. func NewWriterSize(w io.Writer, sz int) *Writer { - // we must be able to require() 18 + // we must be able to require() 'minWriterSize' // contiguous bytes, so that is the // practical minimum buffer size - if sz < 18 { - sz = 18 + if sz < minWriterSize { + sz = minWriterSize } + buf := make([]byte, sz) + return NewWriterBuf(w, buf) +} +// NewWriterBuf returns a writer with a provided buffer. +// 'buf' is not used when the capacity is smaller than 18, +// custom buffer is allocated instead. +func NewWriterBuf(w io.Writer, buf []byte) *Writer { + if cap(buf) < minWriterSize { + buf = make([]byte, minWriterSize) + } + buf = buf[:cap(buf)] return &Writer{ w: w, - buf: make([]byte, sz), + buf: buf, } } @@ -341,6 +356,11 @@ func (mw *Writer) WriteFloat32(f float32) error { return mw.prefix32(mfloat32, math.Float32bits(f)) } +// WriteDuration writes a time.Duration to the writer +func (mw *Writer) WriteDuration(d time.Duration) error { + return mw.WriteInt64(int64(d)) +} + // WriteInt64 writes an int64 to the writer func (mw *Writer) WriteInt64(i int64) error { if i >= 0 { @@ -606,12 +626,12 @@ func (mw *Writer) WriteTime(t time.Time) error { // WriteIntf writes the concrete type of 'v'. // WriteIntf will error if 'v' is not one of the following: -// - A bool, float, string, []byte, int, uint, or complex -// - A map of supported types (with string keys) -// - An array or slice of supported types -// - A pointer to a supported type -// - A type that satisfies the msgp.Encodable interface -// - A type that satisfies the msgp.Extension interface +// - A bool, float, string, []byte, int, uint, or complex +// - A map of supported types (with string keys) +// - An array or slice of supported types +// - A pointer to a supported type +// - A type that satisfies the msgp.Encodable interface +// - A type that satisfies the msgp.Extension interface func (mw *Writer) WriteIntf(v interface{}) error { if v == nil { return mw.WriteNil() @@ -667,11 +687,13 @@ func (mw *Writer) WriteIntf(v interface{}) error { return mw.WriteMapStrIntf(v) case time.Time: return mw.WriteTime(v) + case time.Duration: + return mw.WriteDuration(v) } val := reflect.ValueOf(v) if !isSupported(val.Kind()) || !val.IsValid() { - return fmt.Errorf("msgp: type %s not supported", val) + return errors.New("msgp: type " + val.String() + " not supported") } switch val.Kind() { @@ -731,60 +753,6 @@ func (mw *Writer) writeSlice(v reflect.Value) (err error) { return } -func (mw *Writer) writeStruct(v reflect.Value) error { - if enc, ok := v.Interface().(Encodable); ok { - return enc.EncodeMsg(mw) - } - return fmt.Errorf("msgp: unsupported type: %s", v.Type()) -} - -func (mw *Writer) writeVal(v reflect.Value) error { - if !isSupported(v.Kind()) { - return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type()) - } - - // shortcut for nil values - if v.IsNil() { - return mw.WriteNil() - } - switch v.Kind() { - case reflect.Bool: - return mw.WriteBool(v.Bool()) - - case reflect.Float32, reflect.Float64: - return mw.WriteFloat64(v.Float()) - - case reflect.Complex64, reflect.Complex128: - return mw.WriteComplex128(v.Complex()) - - case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8: - return mw.WriteInt64(v.Int()) - - case reflect.Interface, reflect.Ptr: - if v.IsNil() { - mw.WriteNil() - } - return mw.writeVal(v.Elem()) - - case reflect.Map: - return mw.writeMap(v) - - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8: - return mw.WriteUint64(v.Uint()) - - case reflect.String: - return mw.WriteString(v.String()) - - case reflect.Slice, reflect.Array: - return mw.writeSlice(v) - - case reflect.Struct: - return mw.writeStruct(v) - - } - return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type()) -} - // is the reflect.Kind encodable? func isSupported(k reflect.Kind) bool { switch k { diff --git a/vendor/github.com/tinylib/msgp/msgp/write_bytes.go b/vendor/github.com/tinylib/msgp/msgp/write_bytes.go index eaa03c46e..676a6efe1 100644 --- a/vendor/github.com/tinylib/msgp/msgp/write_bytes.go +++ b/vendor/github.com/tinylib/msgp/msgp/write_bytes.go @@ -73,6 +73,11 @@ func AppendFloat32(b []byte, f float32) []byte { return o } +// AppendDuration appends a time.Duration to the slice +func AppendDuration(b []byte, d time.Duration) []byte { + return AppendInt64(b, int64(d)) +} + // AppendInt64 appends an int64 to the slice func AppendInt64(b []byte, i int64) []byte { if i >= 0 { @@ -193,6 +198,26 @@ func AppendBytes(b []byte, bts []byte) []byte { return o[:n+copy(o[n:], bts)] } +// AppendBytesHeader appends an 'bin' header with +// the given size to the slice. +func AppendBytesHeader(b []byte, sz uint32) []byte { + var o []byte + var n int + switch { + case sz <= math.MaxUint8: + o, n = ensure(b, 2) + prefixu8(o[n:], mbin8, uint8(sz)) + return o + case sz <= math.MaxUint16: + o, n = ensure(b, 3) + prefixu16(o[n:], mbin16, uint16(sz)) + return o + } + o, n = ensure(b, 5) + prefixu32(o[n:], mbin32, sz) + return o +} + // AppendBool appends a bool to the slice func AppendBool(b []byte, t bool) []byte { if t { @@ -315,13 +340,13 @@ func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) { // AppendIntf appends the concrete type of 'i' to the // provided []byte. 'i' must be one of the following: -// - 'nil' -// - A bool, float, string, []byte, int, uint, or complex -// - A map[string]interface{} or map[string]string -// - A []T, where T is another supported type -// - A *T, where T is another supported type -// - A type that satisfieds the msgp.Marshaler interface -// - A type that satisfies the msgp.Extension interface +// - 'nil' +// - A bool, float, string, []byte, int, uint, or complex +// - A map[string]interface{} or map[string]string +// - A []T, where T is another supported type +// - A *T, where T is another supported type +// - A type that satisfieds the msgp.Marshaler interface +// - A type that satisfies the msgp.Extension interface func AppendIntf(b []byte, i interface{}) ([]byte, error) { if i == nil { return AppendNil(b), nil diff --git a/vendor/github.com/vishvananda/netns/.golangci.yml b/vendor/github.com/vishvananda/netns/.golangci.yml new file mode 100644 index 000000000..600bef78e --- /dev/null +++ b/vendor/github.com/vishvananda/netns/.golangci.yml @@ -0,0 +1,2 @@ +run: + timeout: 5m diff --git a/vendor/github.com/vishvananda/netns/README.md b/vendor/github.com/vishvananda/netns/README.md index 6b45cfb89..bdfedbe81 100644 --- a/vendor/github.com/vishvananda/netns/README.md +++ b/vendor/github.com/vishvananda/netns/README.md @@ -23,6 +23,7 @@ import ( "fmt" "net" "runtime" + "github.com/vishvananda/netns" ) diff --git a/vendor/github.com/vishvananda/netns/doc.go b/vendor/github.com/vishvananda/netns/doc.go new file mode 100644 index 000000000..cd4093a4d --- /dev/null +++ b/vendor/github.com/vishvananda/netns/doc.go @@ -0,0 +1,9 @@ +// Package netns allows ultra-simple network namespace handling. NsHandles +// can be retrieved and set. Note that the current namespace is thread +// local so actions that set and reset namespaces should use LockOSThread +// to make sure the namespace doesn't change due to a goroutine switch. +// It is best to close NsHandles when you are done with them. This can be +// accomplished via a `defer ns.Close()` on the handle. Changing namespaces +// requires elevated privileges, so in most cases this code needs to be run +// as root. +package netns diff --git a/vendor/github.com/vishvananda/netns/netns_linux.go b/vendor/github.com/vishvananda/netns/netns_linux.go index c76acd087..2ed7c7e2f 100644 --- a/vendor/github.com/vishvananda/netns/netns_linux.go +++ b/vendor/github.com/vishvananda/netns/netns_linux.go @@ -1,33 +1,31 @@ -// +build linux - package netns import ( "fmt" - "io/ioutil" "os" "path" "path/filepath" "strconv" "strings" - "syscall" "golang.org/x/sys/unix" ) -// Deprecated: use syscall pkg instead (go >= 1.5 needed). +// Deprecated: use golang.org/x/sys/unix pkg instead. const ( - CLONE_NEWUTS = 0x04000000 /* New utsname group? */ - CLONE_NEWIPC = 0x08000000 /* New ipcs */ - CLONE_NEWUSER = 0x10000000 /* New user namespace */ - CLONE_NEWPID = 0x20000000 /* New pid namespace */ - CLONE_NEWNET = 0x40000000 /* New network namespace */ - CLONE_IO = 0x80000000 /* Get io context */ - bindMountPath = "/run/netns" /* Bind mount path for named netns */ + CLONE_NEWUTS = unix.CLONE_NEWUTS /* New utsname group? */ + CLONE_NEWIPC = unix.CLONE_NEWIPC /* New ipcs */ + CLONE_NEWUSER = unix.CLONE_NEWUSER /* New user namespace */ + CLONE_NEWPID = unix.CLONE_NEWPID /* New pid namespace */ + CLONE_NEWNET = unix.CLONE_NEWNET /* New network namespace */ + CLONE_IO = unix.CLONE_IO /* Get io context */ ) -// Setns sets namespace using syscall. Note that this should be a method -// in syscall but it has not been added. +const bindMountPath = "/run/netns" /* Bind mount path for named netns */ + +// Setns sets namespace using golang.org/x/sys/unix.Setns. +// +// Deprecated: Use golang.org/x/sys/unix.Setns instead. func Setns(ns NsHandle, nstype int) (err error) { return unix.Setns(int(ns), nstype) } @@ -35,19 +33,20 @@ func Setns(ns NsHandle, nstype int) (err error) { // Set sets the current network namespace to the namespace represented // by NsHandle. func Set(ns NsHandle) (err error) { - return Setns(ns, CLONE_NEWNET) + return unix.Setns(int(ns), unix.CLONE_NEWNET) } // New creates a new network namespace, sets it as current and returns // a handle to it. func New() (ns NsHandle, err error) { - if err := unix.Unshare(CLONE_NEWNET); err != nil { + if err := unix.Unshare(unix.CLONE_NEWNET); err != nil { return -1, err } return Get() } -// NewNamed creates a new named network namespace and returns a handle to it +// NewNamed creates a new named network namespace, sets it as current, +// and returns a handle to it func NewNamed(name string) (NsHandle, error) { if _, err := os.Stat(bindMountPath); os.IsNotExist(err) { err = os.MkdirAll(bindMountPath, 0755) @@ -65,13 +64,15 @@ func NewNamed(name string) (NsHandle, error) { f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0444) if err != nil { + newNs.Close() return None(), err } f.Close() - nsPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid()) - err = syscall.Mount(nsPath, namedPath, "bind", syscall.MS_BIND, "") + nsPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) + err = unix.Mount(nsPath, namedPath, "bind", unix.MS_BIND, "") if err != nil { + newNs.Close() return None(), err } @@ -82,7 +83,7 @@ func NewNamed(name string) (NsHandle, error) { func DeleteNamed(name string) error { namedPath := path.Join(bindMountPath, name) - err := syscall.Unmount(namedPath, syscall.MNT_DETACH) + err := unix.Unmount(namedPath, unix.MNT_DETACH) if err != nil { return err } @@ -108,7 +109,7 @@ func GetFromPath(path string) (NsHandle, error) { // GetFromName gets a handle to a named network namespace such as one // created by `ip netns add`. func GetFromName(name string) (NsHandle, error) { - return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name)) + return GetFromPath(filepath.Join(bindMountPath, name)) } // GetFromPid gets a handle to the network namespace of a given pid. @@ -133,33 +134,38 @@ func GetFromDocker(id string) (NsHandle, error) { } // borrowed from docker/utils/utils.go -func findCgroupMountpoint(cgroupType string) (string, error) { - output, err := ioutil.ReadFile("/proc/mounts") +func findCgroupMountpoint(cgroupType string) (int, string, error) { + output, err := os.ReadFile("/proc/mounts") if err != nil { - return "", err + return -1, "", err } // /proc/mounts has 6 fields per line, one mount per line, e.g. // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 for _, line := range strings.Split(string(output), "\n") { parts := strings.Split(line, " ") - if len(parts) == 6 && parts[2] == "cgroup" { - for _, opt := range strings.Split(parts[3], ",") { - if opt == cgroupType { - return parts[1], nil + if len(parts) == 6 { + switch parts[2] { + case "cgroup2": + return 2, parts[1], nil + case "cgroup": + for _, opt := range strings.Split(parts[3], ",") { + if opt == cgroupType { + return 1, parts[1], nil + } } } } } - return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) + return -1, "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) } // Returns the relative path to the cgroup docker is running in. // borrowed from docker/utils/utils.go // modified to get the docker pid instead of using /proc/self -func getThisCgroup(cgroupType string) (string, error) { - dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") +func getDockerCgroup(cgroupVer int, cgroupType string) (string, error) { + dockerpid, err := os.ReadFile("/var/run/docker.pid") if err != nil { return "", err } @@ -171,14 +177,15 @@ func getThisCgroup(cgroupType string) (string, error) { if err != nil { return "", err } - output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) + output, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) if err != nil { return "", err } for _, line := range strings.Split(string(output), "\n") { parts := strings.Split(line, ":") // any type used by docker should work - if parts[1] == cgroupType { + if (cgroupVer == 1 && parts[1] == cgroupType) || + (cgroupVer == 2 && parts[1] == "") { return parts[2], nil } } @@ -190,40 +197,56 @@ func getThisCgroup(cgroupType string) (string, error) { // modified to only return the first pid // modified to glob with id // modified to search for newer docker containers +// modified to look for cgroups v2 func getPidForContainer(id string) (int, error) { pid := 0 // memory is chosen randomly, any cgroup used by docker works cgroupType := "memory" - cgroupRoot, err := findCgroupMountpoint(cgroupType) + cgroupVer, cgroupRoot, err := findCgroupMountpoint(cgroupType) if err != nil { return pid, err } - cgroupThis, err := getThisCgroup(cgroupType) + cgroupDocker, err := getDockerCgroup(cgroupVer, cgroupType) if err != nil { return pid, err } id += "*" + var pidFile string + if cgroupVer == 1 { + pidFile = "tasks" + } else if cgroupVer == 2 { + pidFile = "cgroup.procs" + } else { + return -1, fmt.Errorf("Invalid cgroup version '%d'", cgroupVer) + } + attempts := []string{ - filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), + filepath.Join(cgroupRoot, cgroupDocker, id, pidFile), // With more recent lxc versions use, cgroup will be in lxc/ - filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), + filepath.Join(cgroupRoot, cgroupDocker, "lxc", id, pidFile), // With more recent docker, cgroup will be in docker/ - filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), + filepath.Join(cgroupRoot, cgroupDocker, "docker", id, pidFile), // Even more recent docker versions under systemd use docker-.scope/ - filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", "tasks"), + filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", pidFile), // Even more recent docker versions under cgroup/systemd/docker// - filepath.Join(cgroupRoot, "..", "systemd", "docker", id, "tasks"), - // Kubernetes with docker and CNI is even more different - filepath.Join(cgroupRoot, "..", "systemd", "kubepods", "*", "pod*", id, "tasks"), - // Another flavor of containers location in recent kubernetes 1.11+ - filepath.Join(cgroupRoot, cgroupThis, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"), - // When runs inside of a container with recent kubernetes 1.11+ - filepath.Join(cgroupRoot, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"), + filepath.Join(cgroupRoot, "..", "systemd", "docker", id, pidFile), + // Kubernetes with docker and CNI is even more different. Works for BestEffort and Burstable QoS + filepath.Join(cgroupRoot, "..", "systemd", "kubepods", "*", "pod*", id, pidFile), + // Same as above but for Guaranteed QoS + filepath.Join(cgroupRoot, "..", "systemd", "kubepods", "pod*", id, pidFile), + // Another flavor of containers location in recent kubernetes 1.11+. Works for BestEffort and Burstable QoS + filepath.Join(cgroupRoot, cgroupDocker, "kubepods.slice", "*.slice", "*", "docker-"+id+".scope", pidFile), + // Same as above but for Guaranteed QoS + filepath.Join(cgroupRoot, cgroupDocker, "kubepods.slice", "*", "docker-"+id+".scope", pidFile), + // When runs inside of a container with recent kubernetes 1.11+. Works for BestEffort and Burstable QoS + filepath.Join(cgroupRoot, "kubepods.slice", "*.slice", "*", "docker-"+id+".scope", pidFile), + // Same as above but for Guaranteed QoS + filepath.Join(cgroupRoot, "kubepods.slice", "*", "docker-"+id+".scope", pidFile), } var filename string @@ -241,7 +264,7 @@ func getPidForContainer(id string) (int, error) { return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) } - output, err := ioutil.ReadFile(filename) + output, err := os.ReadFile(filename) if err != nil { return pid, err } diff --git a/vendor/github.com/vishvananda/netns/netns_unspecified.go b/vendor/github.com/vishvananda/netns/netns_others.go similarity index 63% rename from vendor/github.com/vishvananda/netns/netns_unspecified.go rename to vendor/github.com/vishvananda/netns/netns_others.go index d06af62b6..048983774 100644 --- a/vendor/github.com/vishvananda/netns/netns_unspecified.go +++ b/vendor/github.com/vishvananda/netns/netns_others.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package netns @@ -10,6 +11,14 @@ var ( ErrNotImplemented = errors.New("not implemented") ) +// Setns sets namespace using golang.org/x/sys/unix.Setns on Linux. It +// is not implemented on other platforms. +// +// Deprecated: Use golang.org/x/sys/unix.Setns instead. +func Setns(ns NsHandle, nstype int) (err error) { + return ErrNotImplemented +} + func Set(ns NsHandle) (err error) { return ErrNotImplemented } @@ -18,6 +27,14 @@ func New() (ns NsHandle, err error) { return -1, ErrNotImplemented } +func NewNamed(name string) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func DeleteNamed(name string) error { + return ErrNotImplemented +} + func Get() (NsHandle, error) { return -1, ErrNotImplemented } diff --git a/vendor/github.com/vishvananda/netns/netns.go b/vendor/github.com/vishvananda/netns/nshandle_linux.go similarity index 75% rename from vendor/github.com/vishvananda/netns/netns.go rename to vendor/github.com/vishvananda/netns/nshandle_linux.go index 116befd54..1baffb66a 100644 --- a/vendor/github.com/vishvananda/netns/netns.go +++ b/vendor/github.com/vishvananda/netns/nshandle_linux.go @@ -1,11 +1,3 @@ -// Package netns allows ultra-simple network namespace handling. NsHandles -// can be retrieved and set. Note that the current namespace is thread -// local so actions that set and reset namespaces should use LockOSThread -// to make sure the namespace doesn't change due to a goroutine switch. -// It is best to close NsHandles when you are done with them. This can be -// accomplished via a `defer ns.Close()` on the handle. Changing namespaces -// requires elevated privileges, so in most cases this code needs to be run -// as root. package netns import ( @@ -38,7 +30,7 @@ func (ns NsHandle) Equal(other NsHandle) bool { // String shows the file descriptor number and its dev and inode. func (ns NsHandle) String() string { if ns == -1 { - return "NS(None)" + return "NS(none)" } var s unix.Stat_t if err := unix.Fstat(int(ns), &s); err != nil { @@ -71,7 +63,7 @@ func (ns *NsHandle) Close() error { if err := unix.Close(int(*ns)); err != nil { return err } - (*ns) = -1 + *ns = -1 return nil } diff --git a/vendor/github.com/vishvananda/netns/nshandle_others.go b/vendor/github.com/vishvananda/netns/nshandle_others.go new file mode 100644 index 000000000..af727bc09 --- /dev/null +++ b/vendor/github.com/vishvananda/netns/nshandle_others.go @@ -0,0 +1,45 @@ +//go:build !linux +// +build !linux + +package netns + +// NsHandle is a handle to a network namespace. It can only be used on Linux, +// but provides stub methods on other platforms. +type NsHandle int + +// Equal determines if two network handles refer to the same network +// namespace. It is only implemented on Linux. +func (ns NsHandle) Equal(_ NsHandle) bool { + return false +} + +// String shows the file descriptor number and its dev and inode. +// It is only implemented on Linux, and returns "NS(none)" on other +// platforms. +func (ns NsHandle) String() string { + return "NS(none)" +} + +// UniqueId returns a string which uniquely identifies the namespace +// associated with the network handle. It is only implemented on Linux, +// and returns "NS(none)" on other platforms. +func (ns NsHandle) UniqueId() string { + return "NS(none)" +} + +// IsOpen returns true if Close() has not been called. It is only implemented +// on Linux and always returns false on other platforms. +func (ns NsHandle) IsOpen() bool { + return false +} + +// Close closes the NsHandle and resets its file descriptor to -1. +// It is only implemented on Linux. +func (ns *NsHandle) Close() error { + return nil +} + +// None gets an empty (closed) NsHandle. +func None() NsHandle { + return NsHandle(-1) +} diff --git a/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.go b/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.go index 34c182442..8120907d9 100644 --- a/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.go +++ b/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.go @@ -6154,264 +6154,264 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 4107 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0x5b, 0x73, 0x1b, 0xc9, - 0x75, 0xe6, 0x00, 0xc4, 0xed, 0xe0, 0x42, 0xb0, 0x79, 0x11, 0x84, 0x95, 0x28, 0x6e, 0x6b, 0xa5, - 0xe5, 0x4a, 0xbb, 0xc4, 0x9a, 0xb6, 0xb3, 0x55, 0x4a, 0xe2, 0x18, 0x22, 0xb1, 0x12, 0x97, 0x14, - 0xc9, 0x1d, 0x42, 0xda, 0x4b, 0xb9, 0xc2, 0x1a, 0x02, 0x2d, 0x72, 0x42, 0x60, 0x06, 0x9e, 0x19, - 0x40, 0xe4, 0xe6, 0xe2, 0x94, 0xcb, 0x71, 0x25, 0xaf, 0x76, 0x55, 0x2a, 0x79, 0x48, 0x5e, 0x52, - 0x29, 0x97, 0x1f, 0xfc, 0x9c, 0xbf, 0x90, 0xa7, 0x5c, 0x2a, 0x7f, 0x20, 0xb5, 0xf1, 0x4b, 0xf2, - 0x23, 0x52, 0xae, 0xbe, 0xcd, 0xf4, 0xdc, 0x40, 0xd9, 0xd8, 0xdd, 0x17, 0x11, 0x7d, 0xfa, 0xf4, - 0xf9, 0x4e, 0x9f, 0xee, 0x3e, 0xe7, 0xf4, 0xe9, 0x11, 0x94, 0x9c, 0x51, 0x6f, 0x73, 0xe4, 0xd8, - 0x9e, 0x8d, 0x2a, 0xc4, 0xeb, 0xf5, 0x5d, 0xe2, 0x4c, 0x88, 0x33, 0x3a, 0x6d, 0x2e, 0x9f, 0xd9, - 0x67, 0x36, 0xeb, 0x68, 0xd1, 0x5f, 0x9c, 0xa7, 0xd9, 0xa0, 0x3c, 0x2d, 0x63, 0x64, 0xb6, 0x86, - 0x93, 0x5e, 0x6f, 0x74, 0xda, 0xba, 0x98, 0x88, 0x9e, 0xa6, 0xdf, 0x63, 0x8c, 0xbd, 0xf3, 0xd1, - 0x29, 0xfb, 0x23, 0xfa, 0x6e, 0x9d, 0xd9, 0xf6, 0xd9, 0x80, 0xf0, 0x5e, 0xcb, 0xb2, 0x3d, 0xc3, - 0x33, 0x6d, 0xcb, 0xe5, 0xbd, 0xf8, 0xaf, 0x34, 0xa8, 0xe9, 0xc4, 0x1d, 0xd9, 0x96, 0x4b, 0x9e, - 0x12, 0xa3, 0x4f, 0x1c, 0x74, 0x1b, 0xa0, 0x37, 0x18, 0xbb, 0x1e, 0x71, 0x4e, 0xcc, 0x7e, 0x43, - 0x5b, 0xd7, 0x36, 0xe6, 0xf5, 0x92, 0xa0, 0xec, 0xf6, 0xd1, 0x1b, 0x50, 0x1a, 0x92, 0xe1, 0x29, - 0xef, 0xcd, 0xb0, 0xde, 0x22, 0x27, 0xec, 0xf6, 0x51, 0x13, 0x8a, 0x0e, 0x99, 0x98, 0xae, 0x69, - 0x5b, 0x8d, 0xec, 0xba, 0xb6, 0x91, 0xd5, 0xfd, 0x36, 0x1d, 0xe8, 0x18, 0x2f, 0xbd, 0x13, 0x8f, - 0x38, 0xc3, 0xc6, 0x3c, 0x1f, 0x48, 0x09, 0x5d, 0xe2, 0x0c, 0xf1, 0x4f, 0x72, 0x50, 0xd1, 0x0d, - 0xeb, 0x8c, 0xe8, 0xe4, 0x87, 0x63, 0xe2, 0x7a, 0xa8, 0x0e, 0xd9, 0x0b, 0x72, 0xc5, 0xe0, 0x2b, - 0x3a, 0xfd, 0xc9, 0xc7, 0x5b, 0x67, 0xe4, 0x84, 0x58, 0x1c, 0xb8, 0x42, 0xc7, 0x5b, 0x67, 0xa4, - 0x63, 0xf5, 0xd1, 0x32, 0xe4, 0x06, 0xe6, 0xd0, 0xf4, 0x04, 0x2a, 0x6f, 0x84, 0xd4, 0x99, 0x8f, - 0xa8, 0xb3, 0x0d, 0xe0, 0xda, 0x8e, 0x77, 0x62, 0x3b, 0x7d, 0xe2, 0x34, 0x72, 0xeb, 0xda, 0x46, - 0x6d, 0xeb, 0xad, 0x4d, 0x75, 0x19, 0x36, 0x55, 0x85, 0x36, 0x8f, 0x6d, 0xc7, 0x3b, 0xa4, 0xbc, - 0x7a, 0xc9, 0x95, 0x3f, 0xd1, 0x87, 0x50, 0x66, 0x42, 0x3c, 0xc3, 0x39, 0x23, 0x5e, 0x23, 0xcf, - 0xa4, 0xdc, 0xbb, 0x46, 0x4a, 0x97, 0x31, 0xeb, 0x0c, 0x9e, 0xff, 0x46, 0x18, 0x2a, 0x2e, 0x71, - 0x4c, 0x63, 0x60, 0x7e, 0x61, 0x9c, 0x0e, 0x48, 0xa3, 0xb0, 0xae, 0x6d, 0x14, 0xf5, 0x10, 0x8d, - 0xce, 0xff, 0x82, 0x5c, 0xb9, 0x27, 0xb6, 0x35, 0xb8, 0x6a, 0x14, 0x19, 0x43, 0x91, 0x12, 0x0e, - 0xad, 0xc1, 0x15, 0x5b, 0x34, 0x7b, 0x6c, 0x79, 0xbc, 0xb7, 0xc4, 0x7a, 0x4b, 0x8c, 0xc2, 0xba, - 0x37, 0xa0, 0x3e, 0x34, 0xad, 0x93, 0xa1, 0xdd, 0x3f, 0xf1, 0x0d, 0x02, 0xcc, 0x20, 0xb5, 0xa1, - 0x69, 0x3d, 0xb3, 0xfb, 0xba, 0x34, 0x0b, 0xe5, 0x34, 0x2e, 0xc3, 0x9c, 0x65, 0xc1, 0x69, 0x5c, - 0xaa, 0x9c, 0x9b, 0xb0, 0x44, 0x65, 0xf6, 0x1c, 0x62, 0x78, 0x24, 0x60, 0xae, 0x30, 0xe6, 0xc5, - 0xa1, 0x69, 0x6d, 0xb3, 0x9e, 0x10, 0xbf, 0x71, 0x19, 0xe3, 0xaf, 0x0a, 0x7e, 0xe3, 0x32, 0xcc, - 0x8f, 0x37, 0xa1, 0xe4, 0xdb, 0x1c, 0x15, 0x61, 0xfe, 0xe0, 0xf0, 0xa0, 0x53, 0x9f, 0x43, 0x00, - 0xf9, 0xf6, 0xf1, 0x76, 0xe7, 0x60, 0xa7, 0xae, 0xa1, 0x32, 0x14, 0x76, 0x3a, 0xbc, 0x91, 0xc1, - 0x8f, 0x01, 0x02, 0xeb, 0xa2, 0x02, 0x64, 0xf7, 0x3a, 0x9f, 0xd5, 0xe7, 0x28, 0xcf, 0x8b, 0x8e, - 0x7e, 0xbc, 0x7b, 0x78, 0x50, 0xd7, 0xe8, 0xe0, 0x6d, 0xbd, 0xd3, 0xee, 0x76, 0xea, 0x19, 0xca, - 0xf1, 0xec, 0x70, 0xa7, 0x9e, 0x45, 0x25, 0xc8, 0xbd, 0x68, 0xef, 0x3f, 0xef, 0xd4, 0xe7, 0xf1, - 0xcf, 0x35, 0xa8, 0x8a, 0xf5, 0xe2, 0x67, 0x02, 0x7d, 0x07, 0xf2, 0xe7, 0xec, 0x5c, 0xb0, 0xad, - 0x58, 0xde, 0xba, 0x15, 0x59, 0xdc, 0xd0, 0xd9, 0xd1, 0x05, 0x2f, 0xc2, 0x90, 0xbd, 0x98, 0xb8, - 0x8d, 0xcc, 0x7a, 0x76, 0xa3, 0xbc, 0x55, 0xdf, 0xe4, 0xe7, 0x75, 0x73, 0x8f, 0x5c, 0xbd, 0x30, - 0x06, 0x63, 0xa2, 0xd3, 0x4e, 0x84, 0x60, 0x7e, 0x68, 0x3b, 0x84, 0xed, 0xd8, 0xa2, 0xce, 0x7e, - 0xd3, 0x6d, 0xcc, 0x16, 0x4d, 0xec, 0x56, 0xde, 0xc0, 0xbf, 0xd4, 0x00, 0x8e, 0xc6, 0x5e, 0xfa, - 0xd1, 0x58, 0x86, 0xdc, 0x84, 0x0a, 0x16, 0xc7, 0x82, 0x37, 0xd8, 0x99, 0x20, 0x86, 0x4b, 0xfc, - 0x33, 0x41, 0x1b, 0xe8, 0x06, 0x14, 0x46, 0x0e, 0x99, 0x9c, 0x5c, 0x4c, 0x18, 0x48, 0x51, 0xcf, - 0xd3, 0xe6, 0xde, 0x04, 0xbd, 0x09, 0x15, 0xf3, 0xcc, 0xb2, 0x1d, 0x72, 0xc2, 0x65, 0xe5, 0x58, - 0x6f, 0x99, 0xd3, 0x98, 0xde, 0x0a, 0x0b, 0x17, 0x9c, 0x57, 0x59, 0xf6, 0x29, 0x09, 0x5b, 0x50, - 0x66, 0xaa, 0xce, 0x64, 0xbe, 0x77, 0x02, 0x1d, 0x33, 0x6c, 0x58, 0xdc, 0x84, 0x42, 0x6b, 0xfc, - 0x03, 0x40, 0x3b, 0x64, 0x40, 0x3c, 0x32, 0x8b, 0xf7, 0x50, 0x6c, 0x92, 0x55, 0x6d, 0x82, 0x7f, - 0xa6, 0xc1, 0x52, 0x48, 0xfc, 0x4c, 0xd3, 0x6a, 0x40, 0xa1, 0xcf, 0x84, 0x71, 0x0d, 0xb2, 0xba, - 0x6c, 0xa2, 0x87, 0x50, 0x14, 0x0a, 0xb8, 0x8d, 0x6c, 0xca, 0xa6, 0x29, 0x70, 0x9d, 0x5c, 0xfc, - 0xcb, 0x0c, 0x94, 0xc4, 0x44, 0x0f, 0x47, 0xa8, 0x0d, 0x55, 0x87, 0x37, 0x4e, 0xd8, 0x7c, 0x84, - 0x46, 0xcd, 0x74, 0x27, 0xf4, 0x74, 0x4e, 0xaf, 0x88, 0x21, 0x8c, 0x8c, 0x7e, 0x1f, 0xca, 0x52, - 0xc4, 0x68, 0xec, 0x09, 0x93, 0x37, 0xc2, 0x02, 0x82, 0xfd, 0xf7, 0x74, 0x4e, 0x07, 0xc1, 0x7e, - 0x34, 0xf6, 0x50, 0x17, 0x96, 0xe5, 0x60, 0x3e, 0x1b, 0xa1, 0x46, 0x96, 0x49, 0x59, 0x0f, 0x4b, - 0x89, 0x2f, 0xd5, 0xd3, 0x39, 0x1d, 0x89, 0xf1, 0x4a, 0xa7, 0xaa, 0x92, 0x77, 0xc9, 0x9d, 0x77, - 0x4c, 0xa5, 0xee, 0xa5, 0x15, 0x57, 0xa9, 0x7b, 0x69, 0x3d, 0x2e, 0x41, 0x41, 0xb4, 0xf0, 0xbf, - 0x64, 0x00, 0xe4, 0x6a, 0x1c, 0x8e, 0xd0, 0x0e, 0xd4, 0x1c, 0xd1, 0x0a, 0x59, 0xeb, 0x8d, 0x44, - 0x6b, 0x89, 0x45, 0x9c, 0xd3, 0xab, 0x72, 0x10, 0x57, 0xee, 0x7b, 0x50, 0xf1, 0xa5, 0x04, 0x06, - 0xbb, 0x99, 0x60, 0x30, 0x5f, 0x42, 0x59, 0x0e, 0xa0, 0x26, 0xfb, 0x04, 0x56, 0xfc, 0xf1, 0x09, - 0x36, 0x7b, 0x73, 0x8a, 0xcd, 0x7c, 0x81, 0x4b, 0x52, 0x82, 0x6a, 0x35, 0x55, 0xb1, 0xc0, 0x6c, - 0x37, 0x13, 0xcc, 0x16, 0x57, 0x8c, 0x1a, 0x0e, 0x68, 0xbc, 0xe4, 0x4d, 0xfc, 0xbf, 0x59, 0x28, - 0x6c, 0xdb, 0xc3, 0x91, 0xe1, 0xd0, 0xd5, 0xc8, 0x3b, 0xc4, 0x1d, 0x0f, 0x3c, 0x66, 0xae, 0xda, - 0xd6, 0xdd, 0xb0, 0x44, 0xc1, 0x26, 0xff, 0xea, 0x8c, 0x55, 0x17, 0x43, 0xe8, 0x60, 0x11, 0x1e, - 0x33, 0xaf, 0x31, 0x58, 0x04, 0x47, 0x31, 0x44, 0x1e, 0xe4, 0x6c, 0x70, 0x90, 0x9b, 0x50, 0x98, - 0x10, 0x27, 0x08, 0xe9, 0x4f, 0xe7, 0x74, 0x49, 0x40, 0xef, 0xc0, 0x42, 0x34, 0xbc, 0xe4, 0x04, - 0x4f, 0xad, 0x17, 0x8e, 0x46, 0x77, 0xa1, 0x12, 0x8a, 0x71, 0x79, 0xc1, 0x57, 0x1e, 0x2a, 0x21, - 0x6e, 0x55, 0xfa, 0x55, 0x1a, 0x8f, 0x2b, 0x4f, 0xe7, 0xa4, 0x67, 0x5d, 0x95, 0x9e, 0xb5, 0x28, - 0x46, 0x09, 0xdf, 0x1a, 0x72, 0x32, 0xdf, 0x0f, 0x3b, 0x19, 0xfc, 0x7d, 0xa8, 0x86, 0x0c, 0x44, - 0xe3, 0x4e, 0xe7, 0xe3, 0xe7, 0xed, 0x7d, 0x1e, 0xa4, 0x9e, 0xb0, 0xb8, 0xa4, 0xd7, 0x35, 0x1a, - 0xeb, 0xf6, 0x3b, 0xc7, 0xc7, 0xf5, 0x0c, 0xaa, 0x42, 0xe9, 0xe0, 0xb0, 0x7b, 0xc2, 0xb9, 0xb2, - 0xf8, 0x89, 0x2f, 0x41, 0x04, 0x39, 0x25, 0xb6, 0xcd, 0x29, 0xb1, 0x4d, 0x93, 0xb1, 0x2d, 0x13, - 0xc4, 0x36, 0x16, 0xe6, 0xf6, 0x3b, 0xed, 0xe3, 0x4e, 0x7d, 0xfe, 0x71, 0x0d, 0x2a, 0xdc, 0xbe, - 0x27, 0x63, 0x8b, 0x86, 0xda, 0x7f, 0xd2, 0x00, 0x82, 0xd3, 0x84, 0x5a, 0x50, 0xe8, 0x71, 0x9c, - 0x86, 0xc6, 0x9c, 0xd1, 0x4a, 0xe2, 0x92, 0xe9, 0x92, 0x0b, 0x7d, 0x0b, 0x0a, 0xee, 0xb8, 0xd7, - 0x23, 0xae, 0x0c, 0x79, 0x37, 0xa2, 0xfe, 0x50, 0x78, 0x2b, 0x5d, 0xf2, 0xd1, 0x21, 0x2f, 0x0d, - 0x73, 0x30, 0x66, 0x01, 0x70, 0xfa, 0x10, 0xc1, 0x87, 0xff, 0x5e, 0x83, 0xb2, 0xb2, 0x79, 0x7f, - 0x47, 0x27, 0x7c, 0x0b, 0x4a, 0x4c, 0x07, 0xd2, 0x17, 0x6e, 0xb8, 0xa8, 0x07, 0x04, 0xf4, 0x7b, - 0x50, 0x92, 0x27, 0x40, 0x7a, 0xe2, 0x46, 0xb2, 0xd8, 0xc3, 0x91, 0x1e, 0xb0, 0xe2, 0x3d, 0x58, - 0x64, 0x56, 0xe9, 0xd1, 0xe4, 0x5a, 0xda, 0x51, 0x4d, 0x3f, 0xb5, 0x48, 0xfa, 0xd9, 0x84, 0xe2, - 0xe8, 0xfc, 0xca, 0x35, 0x7b, 0xc6, 0x40, 0x68, 0xe1, 0xb7, 0xf1, 0x47, 0x80, 0x54, 0x61, 0xb3, - 0x4c, 0x17, 0x57, 0xa1, 0xfc, 0xd4, 0x70, 0xcf, 0x85, 0x4a, 0xf8, 0x21, 0x54, 0x69, 0x73, 0xef, - 0xc5, 0x6b, 0xe8, 0xc8, 0x2e, 0x07, 0x92, 0x7b, 0x26, 0x9b, 0x23, 0x98, 0x3f, 0x37, 0xdc, 0x73, - 0x36, 0xd1, 0xaa, 0xce, 0x7e, 0xa3, 0x77, 0xa0, 0xde, 0xe3, 0x93, 0x3c, 0x89, 0x5c, 0x19, 0x16, - 0x04, 0xdd, 0xcf, 0x04, 0x3f, 0x85, 0x0a, 0x9f, 0xc3, 0x57, 0xad, 0x04, 0x5e, 0x84, 0x85, 0x63, - 0xcb, 0x18, 0xb9, 0xe7, 0xb6, 0x8c, 0x6e, 0x74, 0xd2, 0xf5, 0x80, 0x36, 0x13, 0xe2, 0xdb, 0xb0, - 0xe0, 0x90, 0xa1, 0x61, 0x5a, 0xa6, 0x75, 0x76, 0x72, 0x7a, 0xe5, 0x11, 0x57, 0x5c, 0x98, 0x6a, - 0x3e, 0xf9, 0x31, 0xa5, 0x52, 0xd5, 0x4e, 0x07, 0xf6, 0xa9, 0x70, 0x73, 0xec, 0x37, 0xfe, 0x69, - 0x06, 0x2a, 0x9f, 0x18, 0x5e, 0x4f, 0x2e, 0x1d, 0xda, 0x85, 0x9a, 0xef, 0xdc, 0x18, 0x45, 0xe8, - 0x12, 0x09, 0xb1, 0x6c, 0x8c, 0x4c, 0xa5, 0x65, 0x74, 0xac, 0xf6, 0x54, 0x02, 0x13, 0x65, 0x58, - 0x3d, 0x32, 0xf0, 0x45, 0x65, 0xd2, 0x45, 0x31, 0x46, 0x55, 0x94, 0x4a, 0x40, 0x87, 0x50, 0x1f, - 0x39, 0xf6, 0x99, 0x43, 0x5c, 0xd7, 0x17, 0xc6, 0xc3, 0x18, 0x4e, 0x10, 0x76, 0x24, 0x58, 0x03, - 0x71, 0x0b, 0xa3, 0x30, 0xe9, 0xf1, 0x42, 0x90, 0xcf, 0x70, 0xe7, 0xf4, 0x9f, 0x19, 0x40, 0xf1, - 0x49, 0xfd, 0xb6, 0x29, 0xde, 0x3d, 0xa8, 0xb9, 0x9e, 0xe1, 0xc4, 0x36, 0x5b, 0x95, 0x51, 0x7d, - 0x8f, 0xff, 0x36, 0xf8, 0x0a, 0x9d, 0x58, 0xb6, 0x67, 0xbe, 0xbc, 0x12, 0x59, 0x72, 0x4d, 0x92, - 0x0f, 0x18, 0x15, 0x75, 0xa0, 0xf0, 0xd2, 0x1c, 0x78, 0xc4, 0x71, 0x1b, 0xb9, 0xf5, 0xec, 0x46, - 0x6d, 0xeb, 0xe1, 0x75, 0xcb, 0xb0, 0xf9, 0x21, 0xe3, 0xef, 0x5e, 0x8d, 0x88, 0x2e, 0xc7, 0xaa, - 0x99, 0x67, 0x3e, 0x94, 0x8d, 0xdf, 0x84, 0xe2, 0x2b, 0x2a, 0x82, 0xde, 0xb2, 0x0b, 0x3c, 0x59, - 0x64, 0x6d, 0x7e, 0xc9, 0x7e, 0xe9, 0x18, 0x67, 0x43, 0x62, 0x79, 0xf2, 0x1e, 0x28, 0xdb, 0xf8, - 0x1e, 0x40, 0x00, 0x43, 0x5d, 0xfe, 0xc1, 0xe1, 0xd1, 0xf3, 0x6e, 0x7d, 0x0e, 0x55, 0xa0, 0x78, - 0x70, 0xb8, 0xd3, 0xd9, 0xef, 0xd0, 0xf8, 0x80, 0x5b, 0xd2, 0xa4, 0xa1, 0xb5, 0x54, 0x31, 0xb5, - 0x10, 0x26, 0x5e, 0x85, 0xe5, 0xa4, 0x05, 0xa4, 0xb9, 0x68, 0x55, 0xec, 0xd2, 0x99, 0x8e, 0x8a, - 0x0a, 0x9d, 0x09, 0x4f, 0xb7, 0x01, 0x05, 0xbe, 0x7b, 0xfb, 0x22, 0x39, 0x97, 0x4d, 0x6a, 0x08, - 0xbe, 0x19, 0x49, 0x5f, 0xac, 0x92, 0xdf, 0x4e, 0x74, 0x2f, 0xb9, 0x44, 0xf7, 0x82, 0xee, 0x42, - 0xd5, 0x3f, 0x0d, 0x86, 0x2b, 0x72, 0x81, 0x92, 0x5e, 0x91, 0x1b, 0x9d, 0xd2, 0x42, 0x46, 0x2f, - 0x84, 0x8d, 0x8e, 0xee, 0x41, 0x9e, 0x4c, 0x88, 0xe5, 0xb9, 0x8d, 0x32, 0x8b, 0x18, 0x55, 0x99, - 0xbb, 0x77, 0x28, 0x55, 0x17, 0x9d, 0xf8, 0xbb, 0xb0, 0xc8, 0xee, 0x48, 0x4f, 0x1c, 0xc3, 0x52, - 0x2f, 0x73, 0xdd, 0xee, 0xbe, 0x30, 0x37, 0xfd, 0x89, 0x6a, 0x90, 0xd9, 0xdd, 0x11, 0x46, 0xc8, - 0xec, 0xee, 0xe0, 0x1f, 0x6b, 0x80, 0xd4, 0x71, 0x33, 0xd9, 0x39, 0x22, 0x5c, 0xc2, 0x67, 0x03, - 0xf8, 0x65, 0xc8, 0x11, 0xc7, 0xb1, 0x1d, 0x66, 0xd1, 0x92, 0xce, 0x1b, 0xf8, 0x2d, 0xa1, 0x83, - 0x4e, 0x26, 0xf6, 0x85, 0x7f, 0x06, 0xb9, 0x34, 0xcd, 0x57, 0x75, 0x0f, 0x96, 0x42, 0x5c, 0x33, - 0x45, 0xae, 0x0f, 0x61, 0x81, 0x09, 0xdb, 0x3e, 0x27, 0xbd, 0x8b, 0x91, 0x6d, 0x5a, 0x31, 0x3c, - 0xba, 0x72, 0x81, 0x83, 0xa5, 0xf3, 0xe0, 0x13, 0xab, 0xf8, 0xc4, 0x6e, 0x77, 0x1f, 0x7f, 0x06, - 0xab, 0x11, 0x39, 0x52, 0xfd, 0x3f, 0x82, 0x72, 0xcf, 0x27, 0xba, 0x22, 0xd7, 0xb9, 0x1d, 0x56, - 0x2e, 0x3a, 0x54, 0x1d, 0x81, 0x0f, 0xe1, 0x46, 0x4c, 0xf4, 0x4c, 0x73, 0x7e, 0x1b, 0x56, 0x98, - 0xc0, 0x3d, 0x42, 0x46, 0xed, 0x81, 0x39, 0x49, 0xb5, 0xf4, 0x48, 0x4c, 0x4a, 0x61, 0xfc, 0x7a, - 0xf7, 0x05, 0xfe, 0x03, 0x81, 0xd8, 0x35, 0x87, 0xa4, 0x6b, 0xef, 0xa7, 0xeb, 0x46, 0xa3, 0xd9, - 0x05, 0xb9, 0x72, 0x45, 0x5a, 0xc3, 0x7e, 0xe3, 0x7f, 0xd6, 0x84, 0xa9, 0xd4, 0xe1, 0x5f, 0xf3, - 0x4e, 0x5e, 0x03, 0x38, 0xa3, 0x47, 0x86, 0xf4, 0x69, 0x07, 0xaf, 0xa8, 0x28, 0x14, 0x5f, 0x4f, - 0xea, 0xbf, 0x2b, 0x42, 0xcf, 0x65, 0xb1, 0xcf, 0xd9, 0x3f, 0xbe, 0x97, 0xbb, 0x0d, 0x65, 0x46, - 0x38, 0xf6, 0x0c, 0x6f, 0xec, 0xc6, 0x16, 0xe3, 0x2f, 0xc4, 0xb6, 0x97, 0x83, 0x66, 0x9a, 0xd7, - 0xb7, 0x20, 0xcf, 0x2e, 0x13, 0x32, 0x95, 0xbe, 0x99, 0xb0, 0x1f, 0xb9, 0x1e, 0xba, 0x60, 0xc4, - 0x3f, 0xd5, 0x20, 0xff, 0x8c, 0x95, 0x60, 0x15, 0xd5, 0xe6, 0xe5, 0x5a, 0x58, 0xc6, 0x90, 0x17, - 0x86, 0x4a, 0x3a, 0xfb, 0xcd, 0x52, 0x4f, 0x42, 0x9c, 0xe7, 0xfa, 0x3e, 0x4f, 0x71, 0x4b, 0xba, - 0xdf, 0xa6, 0x36, 0xeb, 0x0d, 0x4c, 0x62, 0x79, 0xac, 0x77, 0x9e, 0xf5, 0x2a, 0x14, 0x9a, 0x3d, - 0x9b, 0xee, 0x3e, 0x31, 0x1c, 0x4b, 0x14, 0x4d, 0x8b, 0x7a, 0x40, 0xc0, 0xfb, 0x50, 0xe7, 0x7a, - 0xb4, 0xfb, 0x7d, 0x25, 0xc1, 0xf4, 0xd1, 0xb4, 0x08, 0x5a, 0x48, 0x5a, 0x26, 0x2a, 0xed, 0x17, - 0x1a, 0x2c, 0x2a, 0xe2, 0x66, 0xb2, 0xea, 0xbb, 0x90, 0xe7, 0x45, 0x6a, 0x91, 0xe9, 0x2c, 0x87, - 0x47, 0x71, 0x18, 0x5d, 0xf0, 0xa0, 0x4d, 0x28, 0xf0, 0x5f, 0xf2, 0x0e, 0x90, 0xcc, 0x2e, 0x99, - 0xf0, 0x3d, 0x58, 0x12, 0x24, 0x32, 0xb4, 0x93, 0x0e, 0x06, 0x5b, 0x0c, 0xfc, 0x67, 0xb0, 0x1c, - 0x66, 0x9b, 0x69, 0x4a, 0x8a, 0x92, 0x99, 0xd7, 0x51, 0xb2, 0x2d, 0x95, 0x7c, 0x3e, 0xea, 0x2b, - 0x79, 0x54, 0x74, 0xc7, 0xa8, 0xeb, 0x95, 0x09, 0xaf, 0x57, 0x30, 0x01, 0x29, 0xe2, 0x1b, 0x9d, - 0xc0, 0x07, 0x72, 0x3b, 0xec, 0x9b, 0xae, 0xef, 0xc3, 0x31, 0x54, 0x06, 0xa6, 0x45, 0x0c, 0x47, - 0x54, 0xce, 0x35, 0x5e, 0x39, 0x57, 0x69, 0xf8, 0x0b, 0x40, 0xea, 0xc0, 0x6f, 0x54, 0xe9, 0xfb, - 0xd2, 0x64, 0x47, 0x8e, 0x3d, 0xb4, 0x53, 0xcd, 0x8e, 0xff, 0x1c, 0x56, 0x22, 0x7c, 0xdf, 0xa8, - 0x9a, 0x4b, 0xb0, 0xb8, 0x43, 0x64, 0x42, 0x23, 0xdd, 0xde, 0x47, 0x80, 0x54, 0xe2, 0x4c, 0x91, - 0xad, 0x05, 0x8b, 0xcf, 0xec, 0x09, 0x75, 0x91, 0x94, 0x1a, 0xf8, 0x06, 0x5e, 0x87, 0xf0, 0x4d, - 0xe1, 0xb7, 0x29, 0xb8, 0x3a, 0x60, 0x26, 0xf0, 0x7f, 0xd7, 0xa0, 0xd2, 0x1e, 0x18, 0xce, 0x50, - 0x02, 0x7f, 0x0f, 0xf2, 0xfc, 0x76, 0x2d, 0x0a, 0x5a, 0xf7, 0xc3, 0x62, 0x54, 0x5e, 0xde, 0x68, - 0xf3, 0xbb, 0xb8, 0x18, 0x45, 0x15, 0x17, 0x6f, 0x5e, 0x3b, 0x91, 0x37, 0xb0, 0x1d, 0xf4, 0x1e, - 0xe4, 0x0c, 0x3a, 0x84, 0x85, 0xa2, 0x5a, 0xb4, 0xae, 0xc1, 0xa4, 0xb1, 0x3b, 0x00, 0xe7, 0xc2, - 0xdf, 0x81, 0xb2, 0x82, 0x80, 0x0a, 0x90, 0x7d, 0xd2, 0x11, 0x09, 0x7b, 0x7b, 0xbb, 0xbb, 0xfb, - 0x82, 0x17, 0x74, 0x6a, 0x00, 0x3b, 0x1d, 0xbf, 0x9d, 0xc1, 0x9f, 0x8a, 0x51, 0xc2, 0xed, 0xab, - 0xfa, 0x68, 0x69, 0xfa, 0x64, 0x5e, 0x4b, 0x9f, 0x4b, 0xa8, 0x8a, 0xe9, 0xcf, 0x1a, 0xc6, 0x98, - 0xbc, 0x94, 0x30, 0xa6, 0x28, 0xaf, 0x0b, 0x46, 0xfc, 0x2b, 0x0d, 0xea, 0x3b, 0xf6, 0x2b, 0xeb, - 0xcc, 0x31, 0xfa, 0xfe, 0x39, 0xf9, 0x30, 0xb2, 0x52, 0x9b, 0x91, 0xe2, 0x68, 0x84, 0x3f, 0x20, - 0x44, 0x56, 0xac, 0x11, 0x94, 0x0d, 0x79, 0x2c, 0x94, 0x4d, 0xfc, 0x01, 0x2c, 0x44, 0x06, 0x51, - 0xdb, 0xbf, 0x68, 0xef, 0xef, 0xee, 0x50, 0x5b, 0xb3, 0xc2, 0x5a, 0xe7, 0xa0, 0xfd, 0x78, 0xbf, - 0x23, 0x1e, 0x90, 0xda, 0x07, 0xdb, 0x9d, 0xfd, 0x7a, 0x06, 0xf7, 0x60, 0x51, 0x81, 0x9f, 0xf5, - 0x65, 0x20, 0x45, 0xbb, 0x05, 0xa8, 0x8a, 0x68, 0x2f, 0x0e, 0xe5, 0xbf, 0x65, 0xa0, 0x26, 0x29, - 0x5f, 0x0f, 0x26, 0x5a, 0x85, 0x7c, 0xff, 0xf4, 0xd8, 0xfc, 0x42, 0xbe, 0x1c, 0x89, 0x16, 0xa5, - 0x0f, 0x38, 0x0e, 0x7f, 0xbe, 0x15, 0x2d, 0x1a, 0xc6, 0x1d, 0xe3, 0xa5, 0xb7, 0x6b, 0xf5, 0xc9, - 0x25, 0x4b, 0x0a, 0xe6, 0xf5, 0x80, 0xc0, 0x2a, 0x4c, 0xe2, 0x99, 0x97, 0xdd, 0xac, 0x94, 0x67, - 0x5f, 0xf4, 0x00, 0xea, 0xf4, 0x77, 0x7b, 0x34, 0x1a, 0x98, 0xa4, 0xcf, 0x05, 0x14, 0x18, 0x4f, - 0x8c, 0x4e, 0xd1, 0xd9, 0x5d, 0xc4, 0x6d, 0x14, 0x59, 0x58, 0x12, 0x2d, 0xb4, 0x0e, 0x65, 0xae, - 0xdf, 0xae, 0xf5, 0xdc, 0x25, 0xec, 0xed, 0x33, 0xab, 0xab, 0xa4, 0x70, 0x9a, 0x01, 0xd1, 0x34, - 0x63, 0x09, 0x16, 0xdb, 0x63, 0xef, 0xbc, 0x63, 0xd1, 0x58, 0x21, 0xad, 0xbc, 0x0c, 0x88, 0x12, - 0x77, 0x4c, 0x57, 0xa5, 0x0a, 0xd6, 0xf0, 0x82, 0x74, 0x60, 0x89, 0x12, 0x89, 0xe5, 0x99, 0x3d, - 0x25, 0xae, 0xca, 0xcc, 0x4b, 0x8b, 0x64, 0x5e, 0x86, 0xeb, 0xbe, 0xb2, 0x9d, 0xbe, 0xb0, 0xb9, - 0xdf, 0xc6, 0xff, 0xa8, 0x71, 0xc8, 0xe7, 0x6e, 0x28, 0x7d, 0xfa, 0x2d, 0xc5, 0xa0, 0xf7, 0xa1, - 0x60, 0x8f, 0xd8, 0x0b, 0xbf, 0x28, 0xc3, 0xac, 0x6e, 0xf2, 0x6f, 0x02, 0x36, 0x85, 0xe0, 0x43, - 0xde, 0xab, 0x4b, 0x36, 0x74, 0x1f, 0x6a, 0xe7, 0x86, 0x7b, 0x4e, 0xfa, 0x47, 0x52, 0x26, 0xbf, - 0xf9, 0x45, 0xa8, 0x78, 0x23, 0xd0, 0xef, 0x09, 0xf1, 0xa6, 0xe8, 0x87, 0x1f, 0xc2, 0x8a, 0xe4, - 0x14, 0xaf, 0x13, 0x53, 0x98, 0x5f, 0xc1, 0x6d, 0xc9, 0xbc, 0x7d, 0x6e, 0x58, 0x67, 0x44, 0x02, - 0xfe, 0xae, 0x16, 0x88, 0xcf, 0x27, 0x9b, 0x38, 0x9f, 0xc7, 0xd0, 0xf0, 0xe7, 0xc3, 0x6e, 0xd6, - 0xf6, 0x40, 0x55, 0x74, 0xec, 0x8a, 0xf3, 0x54, 0xd2, 0xd9, 0x6f, 0x4a, 0x73, 0xec, 0x81, 0x9f, - 0x4a, 0xd3, 0xdf, 0x78, 0x1b, 0x6e, 0x4a, 0x19, 0xe2, 0xce, 0x1b, 0x16, 0x12, 0x53, 0x3c, 0x49, - 0x88, 0x30, 0x2c, 0x1d, 0x3a, 0x7d, 0xe1, 0x55, 0xce, 0xf0, 0x12, 0x30, 0x99, 0x9a, 0x22, 0x73, - 0x85, 0x6f, 0x4a, 0xaa, 0x98, 0x92, 0x2d, 0x49, 0x32, 0x15, 0xa0, 0x92, 0xc5, 0x82, 0x51, 0x72, - 0x6c, 0xc1, 0x62, 0xa2, 0x7f, 0x00, 0x6b, 0xbe, 0x12, 0xd4, 0x6e, 0x47, 0xc4, 0x19, 0x9a, 0xae, - 0xab, 0xd4, 0xbd, 0x93, 0x26, 0x7e, 0x1f, 0xe6, 0x47, 0x44, 0x04, 0xa1, 0xf2, 0x16, 0x92, 0x9b, - 0x52, 0x19, 0xcc, 0xfa, 0x71, 0x1f, 0xee, 0x48, 0xe9, 0xdc, 0xa2, 0x89, 0xe2, 0xa3, 0x4a, 0xc9, - 0x6a, 0x60, 0x26, 0xa5, 0x1a, 0x98, 0x8d, 0xbc, 0xc5, 0x7c, 0xc4, 0x0d, 0x29, 0xcf, 0xfc, 0x4c, - 0xc9, 0xc5, 0x1e, 0xb7, 0xa9, 0xef, 0x2a, 0x66, 0x12, 0xf6, 0xd7, 0xc2, 0x0b, 0x7c, 0x55, 0x1e, - 0x9e, 0xb0, 0x19, 0xca, 0x87, 0x0e, 0xd9, 0xa4, 0x59, 0x33, 0x5d, 0x00, 0x5d, 0xad, 0x85, 0xce, - 0xeb, 0x21, 0x1a, 0x3e, 0x85, 0xe5, 0xb0, 0x5f, 0x9b, 0x49, 0x97, 0x65, 0xc8, 0x79, 0xf6, 0x05, - 0x91, 0xb1, 0x86, 0x37, 0xa4, 0xed, 0x7c, 0x9f, 0x37, 0x93, 0xed, 0x8c, 0x40, 0x18, 0x3b, 0x1d, - 0xb3, 0xea, 0x4b, 0x37, 0x96, 0xbc, 0x03, 0xf1, 0x06, 0x3e, 0x80, 0xd5, 0xa8, 0x67, 0x9b, 0x49, - 0xe5, 0x17, 0xfc, 0x2c, 0x25, 0x39, 0xbf, 0x99, 0xe4, 0x7e, 0x1c, 0xf8, 0x25, 0xc5, 0xb7, 0xcd, - 0x24, 0x52, 0x87, 0x66, 0x92, 0xab, 0xfb, 0x2a, 0x8e, 0x8e, 0xef, 0xf9, 0x66, 0x12, 0xe6, 0x06, - 0xc2, 0x66, 0x5f, 0xfe, 0xc0, 0x5d, 0x65, 0xa7, 0xba, 0x2b, 0x71, 0x48, 0x02, 0x87, 0xfa, 0x35, - 0x6c, 0x3a, 0x81, 0x11, 0xf8, 0xf2, 0x59, 0x31, 0x68, 0x38, 0xf3, 0x31, 0x58, 0x43, 0x6e, 0x6c, - 0x35, 0x02, 0xcc, 0xb4, 0x18, 0x9f, 0x04, 0x6e, 0x3c, 0x16, 0x24, 0x66, 0x12, 0xfc, 0x29, 0xac, - 0xa7, 0xc7, 0x87, 0x59, 0x24, 0x3f, 0x68, 0x41, 0xc9, 0xbf, 0x0c, 0x29, 0xdf, 0x9b, 0x95, 0xa1, - 0x70, 0x70, 0x78, 0x7c, 0xd4, 0xde, 0xee, 0xf0, 0x0f, 0xce, 0xb6, 0x0f, 0x75, 0xfd, 0xf9, 0x51, - 0xb7, 0x9e, 0xd9, 0xfa, 0x75, 0x16, 0x32, 0x7b, 0x2f, 0xd0, 0x67, 0x90, 0xe3, 0x5f, 0x5f, 0x4c, - 0xf9, 0xe4, 0xa6, 0x39, 0xed, 0x03, 0x13, 0x7c, 0xe3, 0xc7, 0xff, 0xf5, 0xeb, 0x9f, 0x67, 0x16, - 0x71, 0xa5, 0x35, 0xf9, 0x76, 0xeb, 0x62, 0xd2, 0x62, 0x61, 0xea, 0x91, 0xf6, 0x00, 0x7d, 0x0c, - 0xd9, 0xa3, 0xb1, 0x87, 0x52, 0x3f, 0xc5, 0x69, 0xa6, 0x7f, 0x73, 0x82, 0x57, 0x98, 0xd0, 0x05, - 0x0c, 0x42, 0xe8, 0x68, 0xec, 0x51, 0x91, 0x3f, 0x84, 0xb2, 0xfa, 0xc5, 0xc8, 0xb5, 0xdf, 0xe7, - 0x34, 0xaf, 0xff, 0x1a, 0x05, 0xdf, 0x66, 0x50, 0x37, 0x30, 0x12, 0x50, 0xfc, 0x9b, 0x16, 0x75, - 0x16, 0xdd, 0x4b, 0x0b, 0xa5, 0x7e, 0xbd, 0xd3, 0x4c, 0xff, 0x40, 0x25, 0x36, 0x0b, 0xef, 0xd2, - 0xa2, 0x22, 0xff, 0x44, 0x7c, 0x9b, 0xd2, 0xf3, 0xd0, 0x9d, 0x84, 0x6f, 0x13, 0xd4, 0x57, 0xf8, - 0xe6, 0x7a, 0x3a, 0x83, 0x00, 0xb9, 0xc5, 0x40, 0x56, 0xf1, 0xa2, 0x00, 0xe9, 0xf9, 0x2c, 0x8f, - 0xb4, 0x07, 0x5b, 0x3d, 0xc8, 0xb1, 0x17, 0x2e, 0xf4, 0xb9, 0xfc, 0xd1, 0x4c, 0x78, 0xea, 0x4b, - 0x59, 0xe8, 0xd0, 0xdb, 0x18, 0x5e, 0x66, 0x40, 0x35, 0x5c, 0xa2, 0x40, 0xec, 0x7d, 0xeb, 0x91, - 0xf6, 0x60, 0x43, 0x7b, 0x5f, 0xdb, 0xfa, 0x55, 0x0e, 0x72, 0xac, 0xb4, 0x8b, 0x2e, 0x00, 0x82, - 0xd7, 0x9e, 0xe8, 0xec, 0x62, 0xef, 0x47, 0xd1, 0xd9, 0xc5, 0x1f, 0x8a, 0x70, 0x93, 0x81, 0x2e, - 0xe3, 0x05, 0x0a, 0xca, 0x2a, 0xc6, 0x2d, 0x56, 0x04, 0xa7, 0x76, 0xfc, 0x1b, 0x4d, 0x54, 0xb6, - 0xf9, 0x59, 0x42, 0x49, 0xd2, 0x42, 0x4f, 0x3e, 0xd1, 0xed, 0x90, 0xf0, 0xdc, 0x83, 0xbf, 0xcb, - 0x00, 0x5b, 0xb8, 0x1e, 0x00, 0x3a, 0x8c, 0xe3, 0x91, 0xf6, 0xe0, 0xf3, 0x06, 0x5e, 0x12, 0x56, - 0x8e, 0xf4, 0xa0, 0x1f, 0x41, 0x2d, 0xfc, 0xa4, 0x81, 0xee, 0x26, 0x60, 0x45, 0x5f, 0x46, 0x9a, - 0x6f, 0x4d, 0x67, 0x12, 0x3a, 0xad, 0x31, 0x9d, 0x04, 0x38, 0x47, 0xbe, 0x20, 0x64, 0x64, 0x50, - 0x26, 0xb1, 0x06, 0xe8, 0x1f, 0x34, 0xf1, 0xe2, 0x14, 0xbc, 0x51, 0xa0, 0x24, 0xe9, 0xb1, 0x17, - 0x90, 0xe6, 0xbd, 0x6b, 0xb8, 0x84, 0x12, 0x7f, 0xc8, 0x94, 0xf8, 0x00, 0x2f, 0x07, 0x4a, 0x78, - 0xe6, 0x90, 0x78, 0xb6, 0xd0, 0xe2, 0xf3, 0x5b, 0xf8, 0x46, 0xc8, 0x38, 0xa1, 0xde, 0x60, 0xb1, - 0xf8, 0x3b, 0x43, 0xe2, 0x62, 0x85, 0xde, 0x2d, 0x12, 0x17, 0x2b, 0xfc, 0x48, 0x91, 0xb4, 0x58, - 0xfc, 0x55, 0x21, 0x69, 0xb1, 0xfc, 0x9e, 0xad, 0xff, 0x9b, 0x87, 0xc2, 0x36, 0xff, 0x26, 0x1c, - 0xd9, 0x50, 0xf2, 0xcb, 0xf4, 0x68, 0x2d, 0xa9, 0xce, 0x18, 0x5c, 0x6b, 0x9a, 0x77, 0x52, 0xfb, - 0x85, 0x42, 0x6f, 0x32, 0x85, 0xde, 0xc0, 0xab, 0x14, 0x59, 0x7c, 0x76, 0xde, 0xe2, 0xc5, 0xac, - 0x96, 0xd1, 0xef, 0x53, 0x43, 0xfc, 0x29, 0x54, 0xd4, 0x3a, 0x3a, 0x7a, 0x33, 0xb1, 0xb6, 0xa9, - 0x96, 0xe2, 0x9b, 0x78, 0x1a, 0x8b, 0x40, 0x7e, 0x8b, 0x21, 0xaf, 0xe1, 0x9b, 0x09, 0xc8, 0x0e, - 0x63, 0x0d, 0x81, 0xf3, 0x1a, 0x78, 0x32, 0x78, 0xa8, 0xc4, 0x9e, 0x0c, 0x1e, 0x2e, 0xa1, 0x4f, - 0x05, 0x1f, 0x33, 0x56, 0x0a, 0xee, 0x02, 0x04, 0x95, 0x6c, 0x94, 0x68, 0x4b, 0xe5, 0x5e, 0x17, - 0x75, 0x0e, 0xf1, 0x22, 0x38, 0xc6, 0x0c, 0x56, 0xec, 0xbb, 0x08, 0xec, 0xc0, 0x74, 0x3d, 0x7e, - 0x30, 0xab, 0xa1, 0xd2, 0x34, 0x4a, 0x9c, 0x4f, 0xb8, 0xbe, 0xdd, 0xbc, 0x3b, 0x95, 0x47, 0xa0, - 0xdf, 0x63, 0xe8, 0x77, 0x70, 0x33, 0x01, 0x7d, 0xc4, 0x79, 0xe9, 0x66, 0xfb, 0xff, 0x3c, 0x94, - 0x9f, 0x19, 0xa6, 0xe5, 0x11, 0xcb, 0xb0, 0x7a, 0x04, 0x9d, 0x42, 0x8e, 0x45, 0xea, 0xa8, 0x23, - 0x56, 0xcb, 0xb6, 0x51, 0x47, 0x1c, 0xaa, 0x69, 0xe2, 0x75, 0x06, 0xdc, 0xc4, 0x2b, 0x14, 0x78, - 0x18, 0x88, 0x6e, 0xb1, 0x52, 0x24, 0x9d, 0xf4, 0x4b, 0xc8, 0x8b, 0xd7, 0xbe, 0x88, 0xa0, 0x50, - 0xf1, 0xa7, 0x79, 0x2b, 0xb9, 0x33, 0x69, 0x2f, 0xab, 0x30, 0x2e, 0xe3, 0xa3, 0x38, 0x13, 0x80, - 0xa0, 0xc6, 0x1e, 0x5d, 0xd1, 0x58, 0x49, 0xbe, 0xb9, 0x9e, 0xce, 0x90, 0x64, 0x53, 0x15, 0xb3, - 0xef, 0xf3, 0x52, 0xdc, 0x3f, 0x86, 0xf9, 0xa7, 0x86, 0x7b, 0x8e, 0x22, 0xb1, 0x57, 0xf9, 0x56, - 0xac, 0xd9, 0x4c, 0xea, 0x12, 0x28, 0x77, 0x18, 0xca, 0x4d, 0xee, 0xca, 0x54, 0x94, 0x73, 0xc3, - 0xa5, 0x41, 0x0d, 0xf5, 0x21, 0xcf, 0x3f, 0x1d, 0x8b, 0xda, 0x2f, 0xf4, 0xf9, 0x59, 0xd4, 0x7e, - 0xe1, 0xaf, 0xcd, 0xae, 0x47, 0x19, 0x41, 0x51, 0x7e, 0xab, 0x85, 0x22, 0x0f, 0xf7, 0x91, 0xef, - 0xba, 0x9a, 0x6b, 0x69, 0xdd, 0x02, 0xeb, 0x2e, 0xc3, 0xba, 0x8d, 0x1b, 0xb1, 0xb5, 0x12, 0x9c, - 0x8f, 0xb4, 0x07, 0xef, 0x6b, 0xe8, 0x47, 0x00, 0xc1, 0xb3, 0x44, 0xec, 0x04, 0x46, 0x5f, 0x38, - 0x62, 0x27, 0x30, 0xf6, 0xa2, 0x81, 0x37, 0x19, 0xee, 0x06, 0xbe, 0x1b, 0xc5, 0xf5, 0x1c, 0xc3, - 0x72, 0x5f, 0x12, 0xe7, 0x3d, 0x5e, 0x65, 0x75, 0xcf, 0xcd, 0x11, 0x9d, 0xb2, 0x03, 0x25, 0xbf, - 0xea, 0x1c, 0xf5, 0xb6, 0xd1, 0x6a, 0x78, 0xd4, 0xdb, 0xc6, 0xca, 0xd5, 0x61, 0xb7, 0x13, 0xda, - 0x2d, 0x92, 0x95, 0x1e, 0xc0, 0x5f, 0xd4, 0x61, 0x9e, 0x66, 0xdd, 0x34, 0x39, 0x09, 0xea, 0x26, - 0xd1, 0xd9, 0xc7, 0xaa, 0xa8, 0xd1, 0xd9, 0xc7, 0x4b, 0x2e, 0xe1, 0xe4, 0x84, 0x5e, 0xb2, 0x5a, - 0xbc, 0x44, 0x41, 0x67, 0x6a, 0x43, 0x59, 0x29, 0xac, 0xa0, 0x04, 0x61, 0xe1, 0xf2, 0x6c, 0x34, - 0xdc, 0x25, 0x54, 0x65, 0xf0, 0x1b, 0x0c, 0x6f, 0x85, 0x87, 0x3b, 0x86, 0xd7, 0xe7, 0x1c, 0x14, - 0x50, 0xcc, 0x4e, 0x9c, 0xfb, 0x84, 0xd9, 0x85, 0xcf, 0xfe, 0x7a, 0x3a, 0x43, 0xea, 0xec, 0x82, - 0x83, 0xff, 0x0a, 0x2a, 0x6a, 0x79, 0x05, 0x25, 0x28, 0x1f, 0x29, 0x29, 0x47, 0xe3, 0x48, 0x52, - 0x75, 0x26, 0xec, 0xd9, 0x18, 0xa4, 0xa1, 0xb0, 0x51, 0xe0, 0x01, 0x14, 0x44, 0xbd, 0x25, 0xc9, - 0xa4, 0xe1, 0xf2, 0x73, 0x92, 0x49, 0x23, 0xc5, 0x9a, 0x70, 0xf6, 0xcc, 0x10, 0xe9, 0x95, 0x52, - 0xc6, 0x6a, 0x81, 0xf6, 0x84, 0x78, 0x69, 0x68, 0x41, 0x25, 0x33, 0x0d, 0x4d, 0xb9, 0xce, 0xa7, - 0xa1, 0x9d, 0x11, 0x4f, 0xf8, 0x03, 0x79, 0x4d, 0x46, 0x29, 0xc2, 0xd4, 0xf8, 0x88, 0xa7, 0xb1, - 0x24, 0x5d, 0x6e, 0x02, 0x40, 0x19, 0x1c, 0x2f, 0x01, 0x82, 0x6a, 0x50, 0x34, 0x63, 0x4d, 0xac, - 0x82, 0x47, 0x33, 0xd6, 0xe4, 0x82, 0x52, 0xd8, 0xf7, 0x05, 0xb8, 0xfc, 0x6e, 0x45, 0x91, 0x7f, - 0xa6, 0x01, 0x8a, 0x17, 0x8e, 0xd0, 0xc3, 0x64, 0xe9, 0x89, 0xb5, 0xf5, 0xe6, 0xbb, 0xaf, 0xc7, - 0x9c, 0x14, 0xce, 0x02, 0x95, 0x7a, 0x8c, 0x7b, 0xf4, 0x8a, 0x2a, 0xf5, 0x97, 0x1a, 0x54, 0x43, - 0x55, 0x27, 0x74, 0x3f, 0x65, 0x4d, 0x23, 0x25, 0xf7, 0xe6, 0xdb, 0xd7, 0xf2, 0x25, 0xa5, 0xf2, - 0xca, 0x0e, 0x90, 0x77, 0x9a, 0x9f, 0x68, 0x50, 0x0b, 0x57, 0xa9, 0x50, 0x8a, 0xec, 0x58, 0xc9, - 0xbe, 0xb9, 0x71, 0x3d, 0xe3, 0xf4, 0xe5, 0x09, 0xae, 0x33, 0x03, 0x28, 0x88, 0xba, 0x56, 0xd2, - 0xc6, 0x0f, 0x17, 0xfb, 0x93, 0x36, 0x7e, 0xa4, 0x28, 0x96, 0xb0, 0xf1, 0x1d, 0x7b, 0x40, 0x94, - 0x63, 0x26, 0x0a, 0x5f, 0x69, 0x68, 0xd3, 0x8f, 0x59, 0xa4, 0x6a, 0x96, 0x86, 0x16, 0x1c, 0x33, - 0x59, 0xf1, 0x42, 0x29, 0xc2, 0xae, 0x39, 0x66, 0xd1, 0x82, 0x59, 0xc2, 0x31, 0x63, 0x80, 0xca, - 0x31, 0x0b, 0x6a, 0x53, 0x49, 0xc7, 0x2c, 0xf6, 0x76, 0x91, 0x74, 0xcc, 0xe2, 0xe5, 0xad, 0x84, - 0x75, 0x64, 0xb8, 0xa1, 0x63, 0xb6, 0x94, 0x50, 0xc6, 0x42, 0xef, 0xa6, 0x18, 0x31, 0xf1, 0x49, - 0xa4, 0xf9, 0xde, 0x6b, 0x72, 0xa7, 0xee, 0x71, 0x6e, 0x7e, 0xb9, 0xc7, 0xff, 0x56, 0x83, 0xe5, - 0xa4, 0x12, 0x18, 0x4a, 0xc1, 0x49, 0x79, 0x4a, 0x69, 0x6e, 0xbe, 0x2e, 0xfb, 0x74, 0x6b, 0xf9, - 0xbb, 0xfe, 0x71, 0xfd, 0x5f, 0xbf, 0x5c, 0xd3, 0xfe, 0xe3, 0xcb, 0x35, 0xed, 0xbf, 0xbf, 0x5c, - 0xd3, 0xfe, 0xee, 0x7f, 0xd6, 0xe6, 0x4e, 0xf3, 0xec, 0x3f, 0x1a, 0x7f, 0xfb, 0x37, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xee, 0x4f, 0x63, 0x90, 0xed, 0x3c, 0x00, 0x00, + // 4110 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0xdd, 0x73, 0x1b, 0xc9, + 0x71, 0xe7, 0x02, 0x24, 0x01, 0x34, 0x3e, 0x08, 0x0e, 0x29, 0x0a, 0xc2, 0x49, 0x14, 0x6f, 0x74, + 0xd2, 0xf1, 0xa4, 0x3b, 0xe2, 0x4c, 0xdb, 0xb9, 0x2a, 0x25, 0x71, 0x0c, 0x91, 0x38, 0x89, 0x47, + 0x8a, 0xe4, 0x2d, 0x21, 0xdd, 0x47, 0xb9, 0xc2, 0x5a, 0x02, 0x23, 0x72, 0x43, 0x60, 0x17, 0xde, + 0x5d, 0x40, 0xe4, 0xe5, 0xc3, 0x2e, 0x97, 0xe3, 0x4a, 0x5e, 0xed, 0xaa, 0x54, 0xf2, 0x90, 0xbc, + 0xa4, 0x52, 0x2e, 0x3f, 0xf8, 0x39, 0xff, 0x42, 0x9e, 0xf2, 0x51, 0xf9, 0x07, 0x52, 0x17, 0xbf, + 0x24, 0x7f, 0x85, 0x6b, 0xbe, 0x76, 0x67, 0xf6, 0x83, 0x92, 0x8d, 0xbb, 0x7b, 0x11, 0x31, 0x3d, + 0x3d, 0xfd, 0xeb, 0xe9, 0x99, 0xe9, 0xee, 0xe9, 0x59, 0x41, 0xc9, 0x1b, 0xf5, 0x36, 0x46, 0x9e, + 0x1b, 0xb8, 0xa8, 0x42, 0x82, 0x5e, 0xdf, 0x27, 0xde, 0x84, 0x78, 0xa3, 0x93, 0xe6, 0xf2, 0xa9, + 0x7b, 0xea, 0xb2, 0x8e, 0x16, 0xfd, 0xc5, 0x79, 0x9a, 0x0d, 0xca, 0xd3, 0xb2, 0x46, 0x76, 0x6b, + 0x38, 0xe9, 0xf5, 0x46, 0x27, 0xad, 0xf3, 0x89, 0xe8, 0x69, 0x86, 0x3d, 0xd6, 0x38, 0x38, 0x1b, + 0x9d, 0xb0, 0x3f, 0xa2, 0xef, 0xe6, 0xa9, 0xeb, 0x9e, 0x0e, 0x08, 0xef, 0x75, 0x1c, 0x37, 0xb0, + 0x02, 0xdb, 0x75, 0x7c, 0xde, 0x8b, 0xff, 0xda, 0x80, 0x9a, 0x49, 0xfc, 0x91, 0xeb, 0xf8, 0xe4, + 0x09, 0xb1, 0xfa, 0xc4, 0x43, 0xb7, 0x00, 0x7a, 0x83, 0xb1, 0x1f, 0x10, 0xef, 0xd8, 0xee, 0x37, + 0x8c, 0x35, 0x63, 0x7d, 0xd6, 0x2c, 0x09, 0xca, 0x4e, 0x1f, 0xbd, 0x01, 0xa5, 0x21, 0x19, 0x9e, + 0xf0, 0xde, 0x1c, 0xeb, 0x2d, 0x72, 0xc2, 0x4e, 0x1f, 0x35, 0xa1, 0xe8, 0x91, 0x89, 0xed, 0xdb, + 0xae, 0xd3, 0xc8, 0xaf, 0x19, 0xeb, 0x79, 0x33, 0x6c, 0xd3, 0x81, 0x9e, 0xf5, 0x22, 0x38, 0x0e, + 0x88, 0x37, 0x6c, 0xcc, 0xf2, 0x81, 0x94, 0xd0, 0x25, 0xde, 0x10, 0xff, 0x74, 0x0e, 0x2a, 0xa6, + 0xe5, 0x9c, 0x12, 0x93, 0xfc, 0x70, 0x4c, 0xfc, 0x00, 0xd5, 0x21, 0x7f, 0x4e, 0x2e, 0x19, 0x7c, + 0xc5, 0xa4, 0x3f, 0xf9, 0x78, 0xe7, 0x94, 0x1c, 0x13, 0x87, 0x03, 0x57, 0xe8, 0x78, 0xe7, 0x94, + 0x74, 0x9c, 0x3e, 0x5a, 0x86, 0xb9, 0x81, 0x3d, 0xb4, 0x03, 0x81, 0xca, 0x1b, 0x9a, 0x3a, 0xb3, + 0x31, 0x75, 0xb6, 0x00, 0x7c, 0xd7, 0x0b, 0x8e, 0x5d, 0xaf, 0x4f, 0xbc, 0xc6, 0xdc, 0x9a, 0xb1, + 0x5e, 0xdb, 0x7c, 0x6b, 0x43, 0x5d, 0x86, 0x0d, 0x55, 0xa1, 0x8d, 0x23, 0xd7, 0x0b, 0x0e, 0x28, + 0xaf, 0x59, 0xf2, 0xe5, 0x4f, 0xf4, 0x21, 0x94, 0x99, 0x90, 0xc0, 0xf2, 0x4e, 0x49, 0xd0, 0x98, + 0x67, 0x52, 0xee, 0xbe, 0x42, 0x4a, 0x97, 0x31, 0x9b, 0x0c, 0x9e, 0xff, 0x46, 0x18, 0x2a, 0x3e, + 0xf1, 0x6c, 0x6b, 0x60, 0x7f, 0x61, 0x9d, 0x0c, 0x48, 0xa3, 0xb0, 0x66, 0xac, 0x17, 0x4d, 0x8d, + 0x46, 0xe7, 0x7f, 0x4e, 0x2e, 0xfd, 0x63, 0xd7, 0x19, 0x5c, 0x36, 0x8a, 0x8c, 0xa1, 0x48, 0x09, + 0x07, 0xce, 0xe0, 0x92, 0x2d, 0x9a, 0x3b, 0x76, 0x02, 0xde, 0x5b, 0x62, 0xbd, 0x25, 0x46, 0x61, + 0xdd, 0xeb, 0x50, 0x1f, 0xda, 0xce, 0xf1, 0xd0, 0xed, 0x1f, 0x87, 0x06, 0x01, 0x66, 0x90, 0xda, + 0xd0, 0x76, 0x9e, 0xba, 0x7d, 0x53, 0x9a, 0x85, 0x72, 0x5a, 0x17, 0x3a, 0x67, 0x59, 0x70, 0x5a, + 0x17, 0x2a, 0xe7, 0x06, 0x2c, 0x51, 0x99, 0x3d, 0x8f, 0x58, 0x01, 0x89, 0x98, 0x2b, 0x8c, 0x79, + 0x71, 0x68, 0x3b, 0x5b, 0xac, 0x47, 0xe3, 0xb7, 0x2e, 0x12, 0xfc, 0x55, 0xc1, 0x6f, 0x5d, 0xe8, + 0xfc, 0x78, 0x03, 0x4a, 0xa1, 0xcd, 0x51, 0x11, 0x66, 0xf7, 0x0f, 0xf6, 0x3b, 0xf5, 0x19, 0x04, + 0x30, 0xdf, 0x3e, 0xda, 0xea, 0xec, 0x6f, 0xd7, 0x0d, 0x54, 0x86, 0xc2, 0x76, 0x87, 0x37, 0x72, + 0xf8, 0x11, 0x40, 0x64, 0x5d, 0x54, 0x80, 0xfc, 0x6e, 0xe7, 0xb3, 0xfa, 0x0c, 0xe5, 0x79, 0xde, + 0x31, 0x8f, 0x76, 0x0e, 0xf6, 0xeb, 0x06, 0x1d, 0xbc, 0x65, 0x76, 0xda, 0xdd, 0x4e, 0x3d, 0x47, + 0x39, 0x9e, 0x1e, 0x6c, 0xd7, 0xf3, 0xa8, 0x04, 0x73, 0xcf, 0xdb, 0x7b, 0xcf, 0x3a, 0xf5, 0x59, + 0xfc, 0x0b, 0x03, 0xaa, 0x62, 0xbd, 0xf8, 0x99, 0x40, 0xdf, 0x81, 0xf9, 0x33, 0x76, 0x2e, 0xd8, + 0x56, 0x2c, 0x6f, 0xde, 0x8c, 0x2d, 0xae, 0x76, 0x76, 0x4c, 0xc1, 0x8b, 0x30, 0xe4, 0xcf, 0x27, + 0x7e, 0x23, 0xb7, 0x96, 0x5f, 0x2f, 0x6f, 0xd6, 0x37, 0xf8, 0x79, 0xdd, 0xd8, 0x25, 0x97, 0xcf, + 0xad, 0xc1, 0x98, 0x98, 0xb4, 0x13, 0x21, 0x98, 0x1d, 0xba, 0x1e, 0x61, 0x3b, 0xb6, 0x68, 0xb2, + 0xdf, 0x74, 0x1b, 0xb3, 0x45, 0x13, 0xbb, 0x95, 0x37, 0xf0, 0xaf, 0x0c, 0x80, 0xc3, 0x71, 0x90, + 0x7d, 0x34, 0x96, 0x61, 0x6e, 0x42, 0x05, 0x8b, 0x63, 0xc1, 0x1b, 0xec, 0x4c, 0x10, 0xcb, 0x27, + 0xe1, 0x99, 0xa0, 0x0d, 0x74, 0x1d, 0x0a, 0x23, 0x8f, 0x4c, 0x8e, 0xcf, 0x27, 0x0c, 0xa4, 0x68, + 0xce, 0xd3, 0xe6, 0xee, 0x04, 0xbd, 0x09, 0x15, 0xfb, 0xd4, 0x71, 0x3d, 0x72, 0xcc, 0x65, 0xcd, + 0xb1, 0xde, 0x32, 0xa7, 0x31, 0xbd, 0x15, 0x16, 0x2e, 0x78, 0x5e, 0x65, 0xd9, 0xa3, 0x24, 0xec, + 0x40, 0x99, 0xa9, 0x3a, 0x95, 0xf9, 0xde, 0x89, 0x74, 0xcc, 0xb1, 0x61, 0x49, 0x13, 0x0a, 0xad, + 0xf1, 0x0f, 0x00, 0x6d, 0x93, 0x01, 0x09, 0xc8, 0x34, 0xde, 0x43, 0xb1, 0x49, 0x5e, 0xb5, 0x09, + 0xfe, 0xb9, 0x01, 0x4b, 0x9a, 0xf8, 0xa9, 0xa6, 0xd5, 0x80, 0x42, 0x9f, 0x09, 0xe3, 0x1a, 0xe4, + 0x4d, 0xd9, 0x44, 0x0f, 0xa0, 0x28, 0x14, 0xf0, 0x1b, 0xf9, 0x8c, 0x4d, 0x53, 0xe0, 0x3a, 0xf9, + 0xf8, 0x57, 0x39, 0x28, 0x89, 0x89, 0x1e, 0x8c, 0x50, 0x1b, 0xaa, 0x1e, 0x6f, 0x1c, 0xb3, 0xf9, + 0x08, 0x8d, 0x9a, 0xd9, 0x4e, 0xe8, 0xc9, 0x8c, 0x59, 0x11, 0x43, 0x18, 0x19, 0xfd, 0x21, 0x94, + 0xa5, 0x88, 0xd1, 0x38, 0x10, 0x26, 0x6f, 0xe8, 0x02, 0xa2, 0xfd, 0xf7, 0x64, 0xc6, 0x04, 0xc1, + 0x7e, 0x38, 0x0e, 0x50, 0x17, 0x96, 0xe5, 0x60, 0x3e, 0x1b, 0xa1, 0x46, 0x9e, 0x49, 0x59, 0xd3, + 0xa5, 0x24, 0x97, 0xea, 0xc9, 0x8c, 0x89, 0xc4, 0x78, 0xa5, 0x53, 0x55, 0x29, 0xb8, 0xe0, 0xce, + 0x3b, 0xa1, 0x52, 0xf7, 0xc2, 0x49, 0xaa, 0xd4, 0xbd, 0x70, 0x1e, 0x95, 0xa0, 0x20, 0x5a, 0xf8, + 0x5f, 0x73, 0x00, 0x72, 0x35, 0x0e, 0x46, 0x68, 0x1b, 0x6a, 0x9e, 0x68, 0x69, 0xd6, 0x7a, 0x23, + 0xd5, 0x5a, 0x62, 0x11, 0x67, 0xcc, 0xaa, 0x1c, 0xc4, 0x95, 0xfb, 0x1e, 0x54, 0x42, 0x29, 0x91, + 0xc1, 0x6e, 0xa4, 0x18, 0x2c, 0x94, 0x50, 0x96, 0x03, 0xa8, 0xc9, 0x3e, 0x81, 0x6b, 0xe1, 0xf8, + 0x14, 0x9b, 0xbd, 0x79, 0x85, 0xcd, 0x42, 0x81, 0x4b, 0x52, 0x82, 0x6a, 0x35, 0x55, 0xb1, 0xc8, + 0x6c, 0x37, 0x52, 0xcc, 0x96, 0x54, 0x8c, 0x1a, 0x0e, 0x68, 0xbc, 0xe4, 0x4d, 0xfc, 0x7f, 0x79, + 0x28, 0x6c, 0xb9, 0xc3, 0x91, 0xe5, 0xd1, 0xd5, 0x98, 0xf7, 0x88, 0x3f, 0x1e, 0x04, 0xcc, 0x5c, + 0xb5, 0xcd, 0x3b, 0xba, 0x44, 0xc1, 0x26, 0xff, 0x9a, 0x8c, 0xd5, 0x14, 0x43, 0xe8, 0x60, 0x11, + 0x1e, 0x73, 0xaf, 0x31, 0x58, 0x04, 0x47, 0x31, 0x44, 0x1e, 0xe4, 0x7c, 0x74, 0x90, 0x9b, 0x50, + 0x98, 0x10, 0x2f, 0x0a, 0xe9, 0x4f, 0x66, 0x4c, 0x49, 0x40, 0xef, 0xc0, 0x42, 0x3c, 0xbc, 0xcc, + 0x09, 0x9e, 0x5a, 0x4f, 0x8f, 0x46, 0x77, 0xa0, 0xa2, 0xc5, 0xb8, 0x79, 0xc1, 0x57, 0x1e, 0x2a, + 0x21, 0x6e, 0x45, 0xfa, 0x55, 0x1a, 0x8f, 0x2b, 0x4f, 0x66, 0xa4, 0x67, 0x5d, 0x91, 0x9e, 0xb5, + 0x28, 0x46, 0x09, 0xdf, 0xaa, 0x39, 0x99, 0xef, 0xeb, 0x4e, 0x06, 0x7f, 0x1f, 0xaa, 0x9a, 0x81, + 0x68, 0xdc, 0xe9, 0x7c, 0xfc, 0xac, 0xbd, 0xc7, 0x83, 0xd4, 0x63, 0x16, 0x97, 0xcc, 0xba, 0x41, + 0x63, 0xdd, 0x5e, 0xe7, 0xe8, 0xa8, 0x9e, 0x43, 0x55, 0x28, 0xed, 0x1f, 0x74, 0x8f, 0x39, 0x57, + 0x1e, 0x3f, 0x0e, 0x25, 0x88, 0x20, 0xa7, 0xc4, 0xb6, 0x19, 0x25, 0xb6, 0x19, 0x32, 0xb6, 0xe5, + 0xa2, 0xd8, 0xc6, 0xc2, 0xdc, 0x5e, 0xa7, 0x7d, 0xd4, 0xa9, 0xcf, 0x3e, 0xaa, 0x41, 0x85, 0xdb, + 0xf7, 0x78, 0xec, 0xd0, 0x50, 0xfb, 0xcf, 0x06, 0x40, 0x74, 0x9a, 0x50, 0x0b, 0x0a, 0x3d, 0x8e, + 0xd3, 0x30, 0x98, 0x33, 0xba, 0x96, 0xba, 0x64, 0xa6, 0xe4, 0x42, 0xdf, 0x82, 0x82, 0x3f, 0xee, + 0xf5, 0x88, 0x2f, 0x43, 0xde, 0xf5, 0xb8, 0x3f, 0x14, 0xde, 0xca, 0x94, 0x7c, 0x74, 0xc8, 0x0b, + 0xcb, 0x1e, 0x8c, 0x59, 0x00, 0xbc, 0x7a, 0x88, 0xe0, 0xc3, 0xff, 0x60, 0x40, 0x59, 0xd9, 0xbc, + 0xbf, 0xa7, 0x13, 0xbe, 0x09, 0x25, 0xa6, 0x03, 0xe9, 0x0b, 0x37, 0x5c, 0x34, 0x23, 0x02, 0xfa, + 0x03, 0x28, 0xc9, 0x13, 0x20, 0x3d, 0x71, 0x23, 0x5d, 0xec, 0xc1, 0xc8, 0x8c, 0x58, 0xf1, 0x2e, + 0x2c, 0x32, 0xab, 0xf4, 0x68, 0x72, 0x2d, 0xed, 0xa8, 0xa6, 0x9f, 0x46, 0x2c, 0xfd, 0x6c, 0x42, + 0x71, 0x74, 0x76, 0xe9, 0xdb, 0x3d, 0x6b, 0x20, 0xb4, 0x08, 0xdb, 0xf8, 0x23, 0x40, 0xaa, 0xb0, + 0x69, 0xa6, 0x8b, 0xab, 0x50, 0x7e, 0x62, 0xf9, 0x67, 0x42, 0x25, 0xfc, 0x00, 0xaa, 0xb4, 0xb9, + 0xfb, 0xfc, 0x35, 0x74, 0x64, 0x97, 0x03, 0xc9, 0x3d, 0x95, 0xcd, 0x11, 0xcc, 0x9e, 0x59, 0xfe, + 0x19, 0x9b, 0x68, 0xd5, 0x64, 0xbf, 0xd1, 0x3b, 0x50, 0xef, 0xf1, 0x49, 0x1e, 0xc7, 0xae, 0x0c, + 0x0b, 0x82, 0x1e, 0x66, 0x82, 0x9f, 0x42, 0x85, 0xcf, 0xe1, 0xab, 0x56, 0x02, 0x2f, 0xc2, 0xc2, + 0x91, 0x63, 0x8d, 0xfc, 0x33, 0x57, 0x46, 0x37, 0x3a, 0xe9, 0x7a, 0x44, 0x9b, 0x0a, 0xf1, 0x6d, + 0x58, 0xf0, 0xc8, 0xd0, 0xb2, 0x1d, 0xdb, 0x39, 0x3d, 0x3e, 0xb9, 0x0c, 0x88, 0x2f, 0x2e, 0x4c, + 0xb5, 0x90, 0xfc, 0x88, 0x52, 0xa9, 0x6a, 0x27, 0x03, 0xf7, 0x44, 0xb8, 0x39, 0xf6, 0x1b, 0xff, + 0x2c, 0x07, 0x95, 0x4f, 0xac, 0xa0, 0x27, 0x97, 0x0e, 0xed, 0x40, 0x2d, 0x74, 0x6e, 0x8c, 0x22, + 0x74, 0x89, 0x85, 0x58, 0x36, 0x46, 0xa6, 0xd2, 0x32, 0x3a, 0x56, 0x7b, 0x2a, 0x81, 0x89, 0xb2, + 0x9c, 0x1e, 0x19, 0x84, 0xa2, 0x72, 0xd9, 0xa2, 0x18, 0xa3, 0x2a, 0x4a, 0x25, 0xa0, 0x03, 0xa8, + 0x8f, 0x3c, 0xf7, 0xd4, 0x23, 0xbe, 0x1f, 0x0a, 0xe3, 0x61, 0x0c, 0xa7, 0x08, 0x3b, 0x14, 0xac, + 0x91, 0xb8, 0x85, 0x91, 0x4e, 0x7a, 0xb4, 0x10, 0xe5, 0x33, 0xdc, 0x39, 0xfd, 0x57, 0x0e, 0x50, + 0x72, 0x52, 0xbf, 0x6b, 0x8a, 0x77, 0x17, 0x6a, 0x7e, 0x60, 0x79, 0x89, 0xcd, 0x56, 0x65, 0xd4, + 0xd0, 0xe3, 0xbf, 0x0d, 0xa1, 0x42, 0xc7, 0x8e, 0x1b, 0xd8, 0x2f, 0x2e, 0x45, 0x96, 0x5c, 0x93, + 0xe4, 0x7d, 0x46, 0x45, 0x1d, 0x28, 0xbc, 0xb0, 0x07, 0x01, 0xf1, 0xfc, 0xc6, 0xdc, 0x5a, 0x7e, + 0xbd, 0xb6, 0xf9, 0xe0, 0x55, 0xcb, 0xb0, 0xf1, 0x21, 0xe3, 0xef, 0x5e, 0x8e, 0x88, 0x29, 0xc7, + 0xaa, 0x99, 0xe7, 0xbc, 0x96, 0x8d, 0xdf, 0x80, 0xe2, 0x4b, 0x2a, 0x82, 0xde, 0xb2, 0x0b, 0x3c, + 0x59, 0x64, 0x6d, 0x7e, 0xc9, 0x7e, 0xe1, 0x59, 0xa7, 0x43, 0xe2, 0x04, 0xf2, 0x1e, 0x28, 0xdb, + 0xf8, 0x2e, 0x40, 0x04, 0x43, 0x5d, 0xfe, 0xfe, 0xc1, 0xe1, 0xb3, 0x6e, 0x7d, 0x06, 0x55, 0xa0, + 0xb8, 0x7f, 0xb0, 0xdd, 0xd9, 0xeb, 0xd0, 0xf8, 0x80, 0x5b, 0xd2, 0xa4, 0xda, 0x5a, 0xaa, 0x98, + 0x86, 0x86, 0x89, 0x57, 0x60, 0x39, 0x6d, 0x01, 0x69, 0x2e, 0x5a, 0x15, 0xbb, 0x74, 0xaa, 0xa3, + 0xa2, 0x42, 0xe7, 0xf4, 0xe9, 0x36, 0xa0, 0xc0, 0x77, 0x6f, 0x5f, 0x24, 0xe7, 0xb2, 0x49, 0x0d, + 0xc1, 0x37, 0x23, 0xe9, 0x8b, 0x55, 0x0a, 0xdb, 0xa9, 0xee, 0x65, 0x2e, 0xd5, 0xbd, 0xa0, 0x3b, + 0x50, 0x0d, 0x4f, 0x83, 0xe5, 0x8b, 0x5c, 0xa0, 0x64, 0x56, 0xe4, 0x46, 0xa7, 0x34, 0xcd, 0xe8, + 0x05, 0xdd, 0xe8, 0xe8, 0x2e, 0xcc, 0x93, 0x09, 0x71, 0x02, 0xbf, 0x51, 0x66, 0x11, 0xa3, 0x2a, + 0x73, 0xf7, 0x0e, 0xa5, 0x9a, 0xa2, 0x13, 0x7f, 0x17, 0x16, 0xd9, 0x1d, 0xe9, 0xb1, 0x67, 0x39, + 0xea, 0x65, 0xae, 0xdb, 0xdd, 0x13, 0xe6, 0xa6, 0x3f, 0x51, 0x0d, 0x72, 0x3b, 0xdb, 0xc2, 0x08, + 0xb9, 0x9d, 0x6d, 0xfc, 0x13, 0x03, 0x90, 0x3a, 0x6e, 0x2a, 0x3b, 0xc7, 0x84, 0x4b, 0xf8, 0x7c, + 0x04, 0xbf, 0x0c, 0x73, 0xc4, 0xf3, 0x5c, 0x8f, 0x59, 0xb4, 0x64, 0xf2, 0x06, 0x7e, 0x4b, 0xe8, + 0x60, 0x92, 0x89, 0x7b, 0x1e, 0x9e, 0x41, 0x2e, 0xcd, 0x08, 0x55, 0xdd, 0x85, 0x25, 0x8d, 0x6b, + 0xaa, 0xc8, 0xf5, 0x21, 0x2c, 0x30, 0x61, 0x5b, 0x67, 0xa4, 0x77, 0x3e, 0x72, 0x6d, 0x27, 0x81, + 0x47, 0x57, 0x2e, 0x72, 0xb0, 0x74, 0x1e, 0x7c, 0x62, 0x95, 0x90, 0xd8, 0xed, 0xee, 0xe1, 0xcf, + 0x60, 0x25, 0x26, 0x47, 0xaa, 0xff, 0x27, 0x50, 0xee, 0x85, 0x44, 0x5f, 0xe4, 0x3a, 0xb7, 0x74, + 0xe5, 0xe2, 0x43, 0xd5, 0x11, 0xf8, 0x00, 0xae, 0x27, 0x44, 0x4f, 0x35, 0xe7, 0xb7, 0xe1, 0x1a, + 0x13, 0xb8, 0x4b, 0xc8, 0xa8, 0x3d, 0xb0, 0x27, 0x99, 0x96, 0x1e, 0x89, 0x49, 0x29, 0x8c, 0x5f, + 0xef, 0xbe, 0xc0, 0x7f, 0x24, 0x10, 0xbb, 0xf6, 0x90, 0x74, 0xdd, 0xbd, 0x6c, 0xdd, 0x68, 0x34, + 0x3b, 0x27, 0x97, 0xbe, 0x48, 0x6b, 0xd8, 0x6f, 0xfc, 0x2f, 0x86, 0x30, 0x95, 0x3a, 0xfc, 0x6b, + 0xde, 0xc9, 0xab, 0x00, 0xa7, 0xf4, 0xc8, 0x90, 0x3e, 0xed, 0xe0, 0x15, 0x15, 0x85, 0x12, 0xea, + 0x49, 0xfd, 0x77, 0x45, 0xe8, 0xb9, 0x2c, 0xf6, 0x39, 0xfb, 0x27, 0xf4, 0x72, 0xb7, 0xa0, 0xcc, + 0x08, 0x47, 0x81, 0x15, 0x8c, 0xfd, 0xc4, 0x62, 0xfc, 0x95, 0xd8, 0xf6, 0x72, 0xd0, 0x54, 0xf3, + 0xfa, 0x16, 0xcc, 0xb3, 0xcb, 0x84, 0x4c, 0xa5, 0x6f, 0xa4, 0xec, 0x47, 0xae, 0x87, 0x29, 0x18, + 0xf1, 0xcf, 0x0c, 0x98, 0x7f, 0xca, 0x4a, 0xb0, 0x8a, 0x6a, 0xb3, 0x72, 0x2d, 0x1c, 0x6b, 0xc8, + 0x0b, 0x43, 0x25, 0x93, 0xfd, 0x66, 0xa9, 0x27, 0x21, 0xde, 0x33, 0x73, 0x8f, 0xa7, 0xb8, 0x25, + 0x33, 0x6c, 0x53, 0x9b, 0xf5, 0x06, 0x36, 0x71, 0x02, 0xd6, 0x3b, 0xcb, 0x7a, 0x15, 0x0a, 0xcd, + 0x9e, 0x6d, 0x7f, 0x8f, 0x58, 0x9e, 0x23, 0x8a, 0xa6, 0x45, 0x33, 0x22, 0xe0, 0x3d, 0xa8, 0x73, + 0x3d, 0xda, 0xfd, 0xbe, 0x92, 0x60, 0x86, 0x68, 0x46, 0x0c, 0x4d, 0x93, 0x96, 0x8b, 0x4b, 0xfb, + 0xa5, 0x01, 0x8b, 0x8a, 0xb8, 0xa9, 0xac, 0xfa, 0x2e, 0xcc, 0xf3, 0x22, 0xb5, 0xc8, 0x74, 0x96, + 0xf5, 0x51, 0x1c, 0xc6, 0x14, 0x3c, 0x68, 0x03, 0x0a, 0xfc, 0x97, 0xbc, 0x03, 0xa4, 0xb3, 0x4b, + 0x26, 0x7c, 0x17, 0x96, 0x04, 0x89, 0x0c, 0xdd, 0xb4, 0x83, 0xc1, 0x16, 0x03, 0xff, 0x05, 0x2c, + 0xeb, 0x6c, 0x53, 0x4d, 0x49, 0x51, 0x32, 0xf7, 0x3a, 0x4a, 0xb6, 0xa5, 0x92, 0xcf, 0x46, 0x7d, + 0x25, 0x8f, 0x8a, 0xef, 0x18, 0x75, 0xbd, 0x72, 0xfa, 0x7a, 0x45, 0x13, 0x90, 0x22, 0xbe, 0xd1, + 0x09, 0x7c, 0x20, 0xb7, 0xc3, 0x9e, 0xed, 0x87, 0x3e, 0x1c, 0x43, 0x65, 0x60, 0x3b, 0xc4, 0xf2, + 0x44, 0xe5, 0xdc, 0xe0, 0x95, 0x73, 0x95, 0x86, 0xbf, 0x00, 0xa4, 0x0e, 0xfc, 0x46, 0x95, 0xbe, + 0x27, 0x4d, 0x76, 0xe8, 0xb9, 0x43, 0x37, 0xd3, 0xec, 0xf8, 0x2f, 0xe1, 0x5a, 0x8c, 0xef, 0x1b, + 0x55, 0x73, 0x09, 0x16, 0xb7, 0x89, 0x4c, 0x68, 0xa4, 0xdb, 0xfb, 0x08, 0x90, 0x4a, 0x9c, 0x2a, + 0xb2, 0xb5, 0x60, 0xf1, 0xa9, 0x3b, 0xa1, 0x2e, 0x92, 0x52, 0x23, 0xdf, 0xc0, 0xeb, 0x10, 0xa1, + 0x29, 0xc2, 0x36, 0x05, 0x57, 0x07, 0x4c, 0x05, 0xfe, 0x1f, 0x06, 0x54, 0xda, 0x03, 0xcb, 0x1b, + 0x4a, 0xe0, 0xef, 0xc1, 0x3c, 0xbf, 0x5d, 0x8b, 0x82, 0xd6, 0x3d, 0x5d, 0x8c, 0xca, 0xcb, 0x1b, + 0x6d, 0x7e, 0x17, 0x17, 0xa3, 0xa8, 0xe2, 0xe2, 0xcd, 0x6b, 0x3b, 0xf6, 0x06, 0xb6, 0x8d, 0xde, + 0x83, 0x39, 0x8b, 0x0e, 0x61, 0xa1, 0xa8, 0x16, 0xaf, 0x6b, 0x30, 0x69, 0xec, 0x0e, 0xc0, 0xb9, + 0xf0, 0x77, 0xa0, 0xac, 0x20, 0xa0, 0x02, 0xe4, 0x1f, 0x77, 0x44, 0xc2, 0xde, 0xde, 0xea, 0xee, + 0x3c, 0xe7, 0x05, 0x9d, 0x1a, 0xc0, 0x76, 0x27, 0x6c, 0xe7, 0xf0, 0xa7, 0x62, 0x94, 0x70, 0xfb, + 0xaa, 0x3e, 0x46, 0x96, 0x3e, 0xb9, 0xd7, 0xd2, 0xe7, 0x02, 0xaa, 0x62, 0xfa, 0xd3, 0x86, 0x31, + 0x26, 0x2f, 0x23, 0x8c, 0x29, 0xca, 0x9b, 0x82, 0x11, 0xff, 0xda, 0x80, 0xfa, 0xb6, 0xfb, 0xd2, + 0x39, 0xf5, 0xac, 0x7e, 0x78, 0x4e, 0x3e, 0x8c, 0xad, 0xd4, 0x46, 0xac, 0x38, 0x1a, 0xe3, 0x8f, + 0x08, 0xb1, 0x15, 0x6b, 0x44, 0x65, 0x43, 0x1e, 0x0b, 0x65, 0x13, 0x7f, 0x00, 0x0b, 0xb1, 0x41, + 0xd4, 0xf6, 0xcf, 0xdb, 0x7b, 0x3b, 0xdb, 0xd4, 0xd6, 0xac, 0xb0, 0xd6, 0xd9, 0x6f, 0x3f, 0xda, + 0xeb, 0x88, 0x07, 0xa4, 0xf6, 0xfe, 0x56, 0x67, 0xaf, 0x9e, 0xc3, 0x3d, 0x58, 0x54, 0xe0, 0xa7, + 0x7d, 0x19, 0xc8, 0xd0, 0x6e, 0x01, 0xaa, 0x22, 0xda, 0x8b, 0x43, 0xf9, 0xef, 0x39, 0xa8, 0x49, + 0xca, 0xd7, 0x83, 0x89, 0x56, 0x60, 0xbe, 0x7f, 0x72, 0x64, 0x7f, 0x21, 0x5f, 0x8e, 0x44, 0x8b, + 0xd2, 0x07, 0x1c, 0x87, 0x3f, 0xdf, 0x8a, 0x16, 0x0d, 0xe3, 0x9e, 0xf5, 0x22, 0xd8, 0x71, 0xfa, + 0xe4, 0x82, 0x25, 0x05, 0xb3, 0x66, 0x44, 0x60, 0x15, 0x26, 0xf1, 0xcc, 0xcb, 0x6e, 0x56, 0xca, + 0xb3, 0x2f, 0xba, 0x0f, 0x75, 0xfa, 0xbb, 0x3d, 0x1a, 0x0d, 0x6c, 0xd2, 0xe7, 0x02, 0x0a, 0x8c, + 0x27, 0x41, 0xa7, 0xe8, 0xec, 0x2e, 0xe2, 0x37, 0x8a, 0x2c, 0x2c, 0x89, 0x16, 0x5a, 0x83, 0x32, + 0xd7, 0x6f, 0xc7, 0x79, 0xe6, 0x13, 0xf6, 0xf6, 0x99, 0x37, 0x55, 0x92, 0x9e, 0x66, 0x40, 0x3c, + 0xcd, 0x58, 0x82, 0xc5, 0xf6, 0x38, 0x38, 0xeb, 0x38, 0x34, 0x56, 0x48, 0x2b, 0x2f, 0x03, 0xa2, + 0xc4, 0x6d, 0xdb, 0x57, 0xa9, 0x82, 0x55, 0x5f, 0x90, 0x0e, 0x2c, 0x51, 0x22, 0x71, 0x02, 0xbb, + 0xa7, 0xc4, 0x55, 0x99, 0x79, 0x19, 0xb1, 0xcc, 0xcb, 0xf2, 0xfd, 0x97, 0xae, 0xd7, 0x17, 0x36, + 0x0f, 0xdb, 0xf8, 0x9f, 0x0c, 0x0e, 0xf9, 0xcc, 0xd7, 0xd2, 0xa7, 0xdf, 0x51, 0x0c, 0x7a, 0x1f, + 0x0a, 0xee, 0x88, 0xbd, 0xf0, 0x8b, 0x32, 0xcc, 0xca, 0x06, 0xff, 0x26, 0x60, 0x43, 0x08, 0x3e, + 0xe0, 0xbd, 0xa6, 0x64, 0x43, 0xf7, 0xa0, 0x76, 0x66, 0xf9, 0x67, 0xa4, 0x7f, 0x28, 0x65, 0xf2, + 0x9b, 0x5f, 0x8c, 0x8a, 0xd7, 0x23, 0xfd, 0x1e, 0x93, 0xe0, 0x0a, 0xfd, 0xf0, 0x03, 0xb8, 0x26, + 0x39, 0xc5, 0xeb, 0xc4, 0x15, 0xcc, 0x2f, 0xe1, 0x96, 0x64, 0xde, 0x3a, 0xb3, 0x9c, 0x53, 0x22, + 0x01, 0x7f, 0x5f, 0x0b, 0x24, 0xe7, 0x93, 0x4f, 0x9d, 0xcf, 0x23, 0x68, 0x84, 0xf3, 0x61, 0x37, + 0x6b, 0x77, 0xa0, 0x2a, 0x3a, 0xf6, 0xc5, 0x79, 0x2a, 0x99, 0xec, 0x37, 0xa5, 0x79, 0xee, 0x20, + 0x4c, 0xa5, 0xe9, 0x6f, 0xbc, 0x05, 0x37, 0xa4, 0x0c, 0x71, 0xe7, 0xd5, 0x85, 0x24, 0x14, 0x4f, + 0x13, 0x22, 0x0c, 0x4b, 0x87, 0x5e, 0xbd, 0xf0, 0x2a, 0xa7, 0xbe, 0x04, 0x4c, 0xa6, 0xa1, 0xc8, + 0xbc, 0xc6, 0x37, 0x25, 0x55, 0x4c, 0xc9, 0x96, 0x24, 0x99, 0x0a, 0x50, 0xc9, 0x62, 0xc1, 0x28, + 0x39, 0xb1, 0x60, 0x09, 0xd1, 0x3f, 0x80, 0xd5, 0x50, 0x09, 0x6a, 0xb7, 0x43, 0xe2, 0x0d, 0x6d, + 0xdf, 0x57, 0xea, 0xde, 0x69, 0x13, 0xbf, 0x07, 0xb3, 0x23, 0x22, 0x82, 0x50, 0x79, 0x13, 0xc9, + 0x4d, 0xa9, 0x0c, 0x66, 0xfd, 0xb8, 0x0f, 0xb7, 0xa5, 0x74, 0x6e, 0xd1, 0x54, 0xf1, 0x71, 0xa5, + 0x64, 0x35, 0x30, 0x97, 0x51, 0x0d, 0xcc, 0xc7, 0xde, 0x62, 0x3e, 0xe2, 0x86, 0x94, 0x67, 0x7e, + 0xaa, 0xe4, 0x62, 0x97, 0xdb, 0x34, 0x74, 0x15, 0x53, 0x09, 0xfb, 0x1b, 0xe1, 0x05, 0xbe, 0x2a, + 0x0f, 0x4f, 0xd8, 0x0c, 0xe5, 0x43, 0x87, 0x6c, 0xd2, 0xac, 0x99, 0x2e, 0x80, 0xa9, 0xd6, 0x42, + 0x67, 0x4d, 0x8d, 0x86, 0x4f, 0x60, 0x59, 0xf7, 0x6b, 0x53, 0xe9, 0xb2, 0x0c, 0x73, 0x81, 0x7b, + 0x4e, 0x64, 0xac, 0xe1, 0x0d, 0x69, 0xbb, 0xd0, 0xe7, 0x4d, 0x65, 0x3b, 0x2b, 0x12, 0xc6, 0x4e, + 0xc7, 0xb4, 0xfa, 0xd2, 0x8d, 0x25, 0xef, 0x40, 0xbc, 0x81, 0xf7, 0x61, 0x25, 0xee, 0xd9, 0xa6, + 0x52, 0xf9, 0x39, 0x3f, 0x4b, 0x69, 0xce, 0x6f, 0x2a, 0xb9, 0x1f, 0x47, 0x7e, 0x49, 0xf1, 0x6d, + 0x53, 0x89, 0x34, 0xa1, 0x99, 0xe6, 0xea, 0xbe, 0x8a, 0xa3, 0x13, 0x7a, 0xbe, 0xa9, 0x84, 0xf9, + 0x91, 0xb0, 0xe9, 0x97, 0x3f, 0x72, 0x57, 0xf9, 0x2b, 0xdd, 0x95, 0x38, 0x24, 0x91, 0x43, 0xfd, + 0x1a, 0x36, 0x9d, 0xc0, 0x88, 0x7c, 0xf9, 0xb4, 0x18, 0x34, 0x9c, 0x85, 0x18, 0xac, 0x21, 0x37, + 0xb6, 0x1a, 0x01, 0xa6, 0x5a, 0x8c, 0x4f, 0x22, 0x37, 0x9e, 0x08, 0x12, 0x53, 0x09, 0xfe, 0x14, + 0xd6, 0xb2, 0xe3, 0xc3, 0x34, 0x92, 0xef, 0xb7, 0xa0, 0x14, 0x5e, 0x86, 0x94, 0xef, 0xcd, 0xca, + 0x50, 0xd8, 0x3f, 0x38, 0x3a, 0x6c, 0x6f, 0x75, 0xf8, 0x07, 0x67, 0x5b, 0x07, 0xa6, 0xf9, 0xec, + 0xb0, 0x5b, 0xcf, 0x6d, 0xfe, 0x26, 0x0f, 0xb9, 0xdd, 0xe7, 0xe8, 0x33, 0x98, 0xe3, 0x5f, 0x5f, + 0x5c, 0xf1, 0xc9, 0x4d, 0xf3, 0xaa, 0x0f, 0x4c, 0xf0, 0xf5, 0x9f, 0xfc, 0xf7, 0x6f, 0x7e, 0x91, + 0x5b, 0xc4, 0x95, 0xd6, 0xe4, 0xdb, 0xad, 0xf3, 0x49, 0x8b, 0x85, 0xa9, 0x87, 0xc6, 0x7d, 0xf4, + 0x31, 0xe4, 0x0f, 0xc7, 0x01, 0xca, 0xfc, 0x14, 0xa7, 0x99, 0xfd, 0xcd, 0x09, 0xbe, 0xc6, 0x84, + 0x2e, 0x60, 0x10, 0x42, 0x47, 0xe3, 0x80, 0x8a, 0xfc, 0x21, 0x94, 0xd5, 0x2f, 0x46, 0x5e, 0xf9, + 0x7d, 0x4e, 0xf3, 0xd5, 0x5f, 0xa3, 0xe0, 0x5b, 0x0c, 0xea, 0x3a, 0x46, 0x02, 0x8a, 0x7f, 0xd3, + 0xa2, 0xce, 0xa2, 0x7b, 0xe1, 0xa0, 0xcc, 0xaf, 0x77, 0x9a, 0xd9, 0x1f, 0xa8, 0x24, 0x66, 0x11, + 0x5c, 0x38, 0x54, 0xe4, 0x9f, 0x89, 0x6f, 0x53, 0x7a, 0x01, 0xba, 0x9d, 0xf2, 0x6d, 0x82, 0xfa, + 0x0a, 0xdf, 0x5c, 0xcb, 0x66, 0x10, 0x20, 0x37, 0x19, 0xc8, 0x0a, 0x5e, 0x14, 0x20, 0xbd, 0x90, + 0xe5, 0xa1, 0x71, 0x7f, 0xb3, 0x07, 0x73, 0xec, 0x85, 0x0b, 0x7d, 0x2e, 0x7f, 0x34, 0x53, 0x9e, + 0xfa, 0x32, 0x16, 0x5a, 0x7b, 0x1b, 0xc3, 0xcb, 0x0c, 0xa8, 0x86, 0x4b, 0x14, 0x88, 0xbd, 0x6f, + 0x3d, 0x34, 0xee, 0xaf, 0x1b, 0xef, 0x1b, 0x9b, 0xbf, 0x9e, 0x83, 0x39, 0x56, 0xda, 0x45, 0xe7, + 0x00, 0xd1, 0x6b, 0x4f, 0x7c, 0x76, 0x89, 0xf7, 0xa3, 0xf8, 0xec, 0x92, 0x0f, 0x45, 0xb8, 0xc9, + 0x40, 0x97, 0xf1, 0x02, 0x05, 0x65, 0x15, 0xe3, 0x16, 0x2b, 0x82, 0x53, 0x3b, 0xfe, 0xad, 0x21, + 0x2a, 0xdb, 0xfc, 0x2c, 0xa1, 0x34, 0x69, 0xda, 0x93, 0x4f, 0x7c, 0x3b, 0xa4, 0x3c, 0xf7, 0xe0, + 0xef, 0x32, 0xc0, 0x16, 0xae, 0x47, 0x80, 0x1e, 0xe3, 0x78, 0x68, 0xdc, 0xff, 0xbc, 0x81, 0x97, + 0x84, 0x95, 0x63, 0x3d, 0xe8, 0x47, 0x50, 0xd3, 0x9f, 0x34, 0xd0, 0x9d, 0x14, 0xac, 0xf8, 0xcb, + 0x48, 0xf3, 0xad, 0xab, 0x99, 0x84, 0x4e, 0xab, 0x4c, 0x27, 0x01, 0xce, 0x91, 0xcf, 0x09, 0x19, + 0x59, 0x94, 0x49, 0xac, 0x01, 0xfa, 0x47, 0x43, 0xbc, 0x38, 0x45, 0x6f, 0x14, 0x28, 0x4d, 0x7a, + 0xe2, 0x05, 0xa4, 0x79, 0xf7, 0x15, 0x5c, 0x42, 0x89, 0x3f, 0x66, 0x4a, 0x7c, 0x80, 0x97, 0x23, + 0x25, 0x02, 0x7b, 0x48, 0x02, 0x57, 0x68, 0xf1, 0xf9, 0x4d, 0x7c, 0x5d, 0x33, 0x8e, 0xd6, 0x1b, + 0x2d, 0x16, 0x7f, 0x67, 0x48, 0x5d, 0x2c, 0xed, 0xdd, 0x22, 0x75, 0xb1, 0xf4, 0x47, 0x8a, 0xb4, + 0xc5, 0xe2, 0xaf, 0x0a, 0x69, 0x8b, 0x15, 0xf6, 0x6c, 0xfe, 0xff, 0x2c, 0x14, 0xb6, 0xf8, 0x37, + 0xe1, 0xc8, 0x85, 0x52, 0x58, 0xa6, 0x47, 0xab, 0x69, 0x75, 0xc6, 0xe8, 0x5a, 0xd3, 0xbc, 0x9d, + 0xd9, 0x2f, 0x14, 0x7a, 0x93, 0x29, 0xf4, 0x06, 0x5e, 0xa1, 0xc8, 0xe2, 0xb3, 0xf3, 0x16, 0x2f, + 0x66, 0xb5, 0xac, 0x7e, 0x9f, 0x1a, 0xe2, 0xcf, 0xa1, 0xa2, 0xd6, 0xd1, 0xd1, 0x9b, 0xa9, 0xb5, + 0x4d, 0xb5, 0x14, 0xdf, 0xc4, 0x57, 0xb1, 0x08, 0xe4, 0xb7, 0x18, 0xf2, 0x2a, 0xbe, 0x91, 0x82, + 0xec, 0x31, 0x56, 0x0d, 0x9c, 0xd7, 0xc0, 0xd3, 0xc1, 0xb5, 0x12, 0x7b, 0x3a, 0xb8, 0x5e, 0x42, + 0xbf, 0x12, 0x7c, 0xcc, 0x58, 0x29, 0xb8, 0x0f, 0x10, 0x55, 0xb2, 0x51, 0xaa, 0x2d, 0x95, 0x7b, + 0x5d, 0xdc, 0x39, 0x24, 0x8b, 0xe0, 0x18, 0x33, 0x58, 0xb1, 0xef, 0x62, 0xb0, 0x03, 0xdb, 0x0f, + 0xf8, 0xc1, 0xac, 0x6a, 0xa5, 0x69, 0x94, 0x3a, 0x1f, 0xbd, 0xbe, 0xdd, 0xbc, 0x73, 0x25, 0x8f, + 0x40, 0xbf, 0xcb, 0xd0, 0x6f, 0xe3, 0x66, 0x0a, 0xfa, 0x88, 0xf3, 0xd2, 0xcd, 0xf6, 0xe3, 0x02, + 0x94, 0x9f, 0x5a, 0xb6, 0x13, 0x10, 0xc7, 0x72, 0x7a, 0x04, 0x9d, 0xc0, 0x1c, 0x8b, 0xd4, 0x71, + 0x47, 0xac, 0x96, 0x6d, 0xe3, 0x8e, 0x58, 0xab, 0x69, 0xe2, 0x35, 0x06, 0xdc, 0xc4, 0xd7, 0x28, + 0xf0, 0x30, 0x12, 0xdd, 0x62, 0xa5, 0x48, 0x3a, 0xe9, 0x17, 0x30, 0x2f, 0x5e, 0xfb, 0x62, 0x82, + 0xb4, 0xe2, 0x4f, 0xf3, 0x66, 0x7a, 0x67, 0xda, 0x5e, 0x56, 0x61, 0x7c, 0xc6, 0x47, 0x71, 0x26, + 0x00, 0x51, 0x8d, 0x3d, 0xbe, 0xa2, 0x89, 0x92, 0x7c, 0x73, 0x2d, 0x9b, 0x21, 0xcd, 0xa6, 0x2a, + 0x66, 0x3f, 0xe4, 0xa5, 0xb8, 0x7f, 0x0a, 0xb3, 0x4f, 0x2c, 0xff, 0x0c, 0xc5, 0x62, 0xaf, 0xf2, + 0xad, 0x58, 0xb3, 0x99, 0xd6, 0x25, 0x50, 0x6e, 0x33, 0x94, 0x1b, 0xdc, 0x95, 0xa9, 0x28, 0x67, + 0x96, 0x7f, 0x26, 0xec, 0xc7, 0x3f, 0x1d, 0x8b, 0xdb, 0x4f, 0xfb, 0xfc, 0x2c, 0x6e, 0x3f, 0xfd, + 0x6b, 0xb3, 0x6c, 0xfb, 0x51, 0x94, 0xf3, 0x09, 0xc5, 0x19, 0x41, 0x51, 0x7e, 0xad, 0x85, 0x62, + 0x4f, 0xf7, 0xb1, 0x2f, 0xbb, 0x9a, 0xab, 0x59, 0xdd, 0x02, 0xed, 0x0e, 0x43, 0xbb, 0x85, 0x1b, + 0x89, 0xd5, 0x12, 0x9c, 0x0f, 0x8d, 0xfb, 0xef, 0x1b, 0xe8, 0x47, 0x00, 0xd1, 0xc3, 0x44, 0xe2, + 0x0c, 0xc6, 0xdf, 0x38, 0x12, 0x67, 0x30, 0xf1, 0xa6, 0x81, 0x37, 0x18, 0xee, 0x3a, 0xbe, 0x13, + 0xc7, 0x0d, 0x3c, 0xcb, 0xf1, 0x5f, 0x10, 0xef, 0x3d, 0x5e, 0x67, 0xf5, 0xcf, 0xec, 0x11, 0x9d, + 0xb2, 0x07, 0xa5, 0xb0, 0xee, 0x1c, 0xf7, 0xb7, 0xf1, 0x7a, 0x78, 0xdc, 0xdf, 0x26, 0x0a, 0xd6, + 0xba, 0xe3, 0xd1, 0xf6, 0x8b, 0x64, 0xa5, 0x47, 0xf0, 0x97, 0x75, 0x98, 0xa5, 0x79, 0x37, 0x4d, + 0x4f, 0xa2, 0xca, 0x49, 0x7c, 0xf6, 0x89, 0x3a, 0x6a, 0x7c, 0xf6, 0xc9, 0xa2, 0x8b, 0x9e, 0x9e, + 0xd0, 0x6b, 0x56, 0x8b, 0x17, 0x29, 0xe8, 0x4c, 0x5d, 0x28, 0x2b, 0xa5, 0x15, 0x94, 0x22, 0x4c, + 0x2f, 0xd0, 0xc6, 0x03, 0x5e, 0x4a, 0x5d, 0x06, 0xbf, 0xc1, 0xf0, 0xae, 0xf1, 0x80, 0xc7, 0xf0, + 0xfa, 0x9c, 0x83, 0x02, 0x8a, 0xd9, 0x89, 0x93, 0x9f, 0x32, 0x3b, 0xfd, 0xf4, 0xaf, 0x65, 0x33, + 0x64, 0xce, 0x2e, 0x3a, 0xfa, 0x2f, 0xa1, 0xa2, 0x16, 0x58, 0x50, 0x8a, 0xf2, 0xb1, 0xa2, 0x72, + 0x3c, 0x92, 0xa4, 0xd5, 0x67, 0x74, 0xdf, 0xc6, 0x20, 0x2d, 0x85, 0x8d, 0x02, 0x0f, 0xa0, 0x20, + 0x2a, 0x2e, 0x69, 0x26, 0xd5, 0x0b, 0xd0, 0x69, 0x26, 0x8d, 0x95, 0x6b, 0xf4, 0xfc, 0x99, 0x21, + 0xd2, 0x4b, 0xa5, 0x8c, 0xd6, 0x02, 0xed, 0x31, 0x09, 0xb2, 0xd0, 0xa2, 0x5a, 0x66, 0x16, 0x9a, + 0x72, 0xa1, 0xcf, 0x42, 0x3b, 0x25, 0x81, 0xf0, 0x07, 0xf2, 0xa2, 0x8c, 0x32, 0x84, 0xa9, 0x11, + 0x12, 0x5f, 0xc5, 0x92, 0x76, 0xbd, 0x89, 0x00, 0x65, 0x78, 0xbc, 0x00, 0x88, 0xea, 0x41, 0xf1, + 0x9c, 0x35, 0xb5, 0x0e, 0x1e, 0xcf, 0x59, 0xd3, 0x4b, 0x4a, 0xba, 0x8f, 0x8d, 0x70, 0xf9, 0xed, + 0x8a, 0x22, 0xff, 0xdc, 0x00, 0x94, 0x2c, 0x1d, 0xa1, 0x07, 0xe9, 0xd2, 0x53, 0xab, 0xeb, 0xcd, + 0x77, 0x5f, 0x8f, 0x39, 0xcd, 0x21, 0x47, 0x2a, 0xf5, 0x18, 0xf7, 0xe8, 0x25, 0x55, 0xea, 0xc7, + 0x06, 0x54, 0xb5, 0xba, 0x13, 0xba, 0x97, 0xb1, 0xa6, 0xb1, 0xa2, 0x7b, 0xf3, 0xed, 0x57, 0xf2, + 0xa5, 0x25, 0xf3, 0xca, 0x0e, 0x90, 0xb7, 0x9a, 0x9f, 0x1a, 0x50, 0xd3, 0xeb, 0x54, 0x28, 0x43, + 0x76, 0xa2, 0x68, 0xdf, 0x5c, 0x7f, 0x35, 0xe3, 0xd5, 0xcb, 0x13, 0x5d, 0x68, 0x06, 0x50, 0x10, + 0x95, 0xad, 0xb4, 0x8d, 0xaf, 0x97, 0xfb, 0xd3, 0x36, 0x7e, 0xac, 0x2c, 0x96, 0xb2, 0xf1, 0x3d, + 0x77, 0x40, 0x94, 0x63, 0x26, 0x4a, 0x5f, 0x59, 0x68, 0x57, 0x1f, 0xb3, 0x58, 0xdd, 0x2c, 0x0b, + 0x2d, 0x3a, 0x66, 0xb2, 0xe6, 0x85, 0x32, 0x84, 0xbd, 0xe2, 0x98, 0xc5, 0x4b, 0x66, 0x29, 0xc7, + 0x8c, 0x01, 0x2a, 0xc7, 0x2c, 0xaa, 0x4e, 0xa5, 0x1d, 0xb3, 0xc4, 0xeb, 0x45, 0xda, 0x31, 0x4b, + 0x16, 0xb8, 0x52, 0xd6, 0x91, 0xe1, 0x6a, 0xc7, 0x6c, 0x29, 0xa5, 0x90, 0x85, 0xde, 0xcd, 0x30, + 0x62, 0xea, 0xa3, 0x48, 0xf3, 0xbd, 0xd7, 0xe4, 0xce, 0xdc, 0xe3, 0xdc, 0xfc, 0x72, 0x8f, 0xff, + 0x9d, 0x01, 0xcb, 0x69, 0x45, 0x30, 0x94, 0x81, 0x93, 0xf1, 0x98, 0xd2, 0xdc, 0x78, 0x5d, 0xf6, + 0xab, 0xad, 0x15, 0xee, 0xfa, 0x47, 0xf5, 0x7f, 0xfb, 0x72, 0xd5, 0xf8, 0xcf, 0x2f, 0x57, 0x8d, + 0xff, 0xf9, 0x72, 0xd5, 0xf8, 0xfb, 0xff, 0x5d, 0x9d, 0x39, 0x99, 0x67, 0xff, 0xd5, 0xf8, 0xdb, + 0xbf, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x8b, 0x54, 0x11, 0xdf, 0xef, 0x3c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.proto b/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.proto index 14391378a..4ccc23474 100644 --- a/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.proto +++ b/vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.proto @@ -217,7 +217,7 @@ service Maintenance { // It only iterates "key" bucket in backend storage. rpc HashKV(HashKVRequest) returns (HashKVResponse) { option (google.api.http) = { - post: "/v3/maintenance/hash" + post: "/v3/maintenance/hashkv" body: "*" }; } diff --git a/vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/error.go b/vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/error.go index ae112ae13..23201302e 100644 --- a/vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/error.go +++ b/vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/error.go @@ -45,6 +45,7 @@ var ( ErrGRPCMemberNotLearner = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member").Err() ErrGRPCLearnerNotReady = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member which is in sync with leader").Err() ErrGRPCTooManyLearners = status.New(codes.FailedPrecondition, "etcdserver: too many learner members in cluster").Err() + ErrGRPCClusterIdMismatch = status.New(codes.FailedPrecondition, "etcdserver: cluster ID mismatch").Err() ErrGRPCRequestTooLarge = status.New(codes.InvalidArgument, "etcdserver: request is too large").Err() ErrGRPCRequestTooManyRequests = status.New(codes.ResourceExhausted, "etcdserver: too many requests").Err() @@ -114,6 +115,7 @@ var ( ErrorDesc(ErrGRPCMemberNotLearner): ErrGRPCMemberNotLearner, ErrorDesc(ErrGRPCLearnerNotReady): ErrGRPCLearnerNotReady, ErrorDesc(ErrGRPCTooManyLearners): ErrGRPCTooManyLearners, + ErrorDesc(ErrGRPCClusterIdMismatch): ErrGRPCClusterIdMismatch, ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge, ErrorDesc(ErrGRPCRequestTooManyRequests): ErrGRPCRequestTooManyRequests, @@ -200,6 +202,7 @@ var ( ErrInvalidAuthToken = Error(ErrGRPCInvalidAuthToken) ErrAuthOldRevision = Error(ErrGRPCAuthOldRevision) ErrInvalidAuthMgmt = Error(ErrGRPCInvalidAuthMgmt) + ErrClusterIdMismatch = Error(ErrGRPCClusterIdMismatch) ErrNoLeader = Error(ErrGRPCNoLeader) ErrNotLeader = Error(ErrGRPCNotLeader) diff --git a/vendor/go.etcd.io/etcd/api/v3/version/version.go b/vendor/go.etcd.io/etcd/api/v3/version/version.go index 16c2c10bc..4858a08bf 100644 --- a/vendor/go.etcd.io/etcd/api/v3/version/version.go +++ b/vendor/go.etcd.io/etcd/api/v3/version/version.go @@ -26,7 +26,7 @@ import ( var ( // MinClusterVersion is the min cluster version this etcd binary is compatible with. MinClusterVersion = "3.0.0" - Version = "3.5.5" + Version = "3.5.12" APIVersion = "unknown" // Git SHA Value will be set during build diff --git a/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go b/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go index d7fd0d90d..34f35b9f2 100644 --- a/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go +++ b/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go @@ -16,6 +16,7 @@ package logutil import ( "sort" + "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -46,15 +47,20 @@ var DefaultZapLoggerConfig = zap.Config{ // copied from "zap.NewProductionEncoderConfig" with some updates EncoderConfig: zapcore.EncoderConfig{ - TimeKey: "ts", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, + + // Custom EncodeTime function to ensure we match format and precision of historic capnslog timestamps + EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02T15:04:05.999999Z0700")) + }, + EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }, diff --git a/vendor/go.etcd.io/etcd/client/v2/doc.go b/vendor/go.etcd.io/etcd/client/v2/doc.go index 5250758b0..68284c20a 100644 --- a/vendor/go.etcd.io/etcd/client/v2/doc.go +++ b/vendor/go.etcd.io/etcd/client/v2/doc.go @@ -68,6 +68,5 @@ Use a custom context to set timeouts on your operations: // handle error } } - */ package client diff --git a/vendor/go.etcd.io/etcd/client/v3/client.go b/vendor/go.etcd.io/etcd/client/v3/client.go index 4dfae89c6..efa44e890 100644 --- a/vendor/go.etcd.io/etcd/client/v3/client.go +++ b/vendor/go.etcd.io/etcd/client/v3/client.go @@ -264,6 +264,7 @@ func (c *Client) getToken(ctx context.Context) error { resp, err := c.Auth.Authenticate(ctx, c.Username, c.Password) if err != nil { if err == rpctypes.ErrAuthNotEnabled { + c.authTokenBundle.UpdateAuthToken("") return nil } return err @@ -501,7 +502,7 @@ func (c *Client) checkVersion() (err error) { return } } - if maj < 3 || (maj == 3 && min < 2) { + if maj < 3 || (maj == 3 && min < 4) { rerr = ErrOldCluster } errc <- rerr @@ -509,7 +510,7 @@ func (c *Client) checkVersion() (err error) { } // wait for success for range eps { - if err = <-errc; err == nil { + if err = <-errc; err != nil { break } } diff --git a/vendor/go.etcd.io/etcd/client/v3/doc.go b/vendor/go.etcd.io/etcd/client/v3/doc.go index 645d744a5..fd61aff11 100644 --- a/vendor/go.etcd.io/etcd/client/v3/doc.go +++ b/vendor/go.etcd.io/etcd/client/v3/doc.go @@ -61,7 +61,8 @@ // // 1. context error: canceled or deadline exceeded. // 2. gRPC error: e.g. when clock drifts in server-side before client's context deadline exceeded. -// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go +// +// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go // // Here is the example code to handle client errors: // @@ -102,5 +103,4 @@ // The grpc load balancer is registered statically and is shared across etcd clients. // To enable detailed load balancer logging, set the ETCD_CLIENT_DEBUG environment // variable. E.g. "ETCD_CLIENT_DEBUG=1". -// package clientv3 diff --git a/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go b/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go index 1d3f1a7a2..35a3fe8c3 100644 --- a/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go +++ b/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go @@ -41,12 +41,8 @@ func extractHostFromHostPort(ep string) string { return host } -func extractHostFromPath(pathStr string) string { - return extractHostFromHostPort(path.Base(pathStr)) -} - -//mustSplit2 returns the values from strings.SplitN(s, sep, 2). -//If sep is not found, it returns ("", "", false) instead. +// mustSplit2 returns the values from strings.SplitN(s, sep, 2). +// If sep is not found, it returns ("", "", false) instead. func mustSplit2(s, sep string) (string, string) { spl := strings.SplitN(s, sep, 2) if len(spl) < 2 { @@ -81,11 +77,12 @@ func schemeToCredsRequirement(schema string) CredsRequirement { // The main differences: // - etcd supports unixs & https names as opposed to unix & http to // distinguish need to configure certificates. -// - etcd support http(s) names as opposed to tcp supported by grpc/dial method. -// - etcd supports unix(s)://local-file naming schema +// - etcd support http(s) names as opposed to tcp supported by grpc/dial method. +// - etcd supports unix(s)://local-file naming schema // (as opposed to unix:local-file canonical name used by grpc for current dir files). -// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon) -// is considered serverName - to allow local testing of cert-protected communication. +// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon) +// is considered serverName - to allow local testing of cert-protected communication. +// // See more: // - https://github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47 // - https://golang.org/pkg/net/#Dial @@ -95,29 +92,29 @@ func translateEndpoint(ep string) (addr string, serverName string, requireCreds if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") { // absolute path case schema, absolutePath := mustSplit2(ep, "://") - return "unix://" + absolutePath, extractHostFromPath(absolutePath), schemeToCredsRequirement(schema) + return "unix://" + absolutePath, path.Base(absolutePath), schemeToCredsRequirement(schema) } if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") { // legacy etcd local path schema, localPath := mustSplit2(ep, "://") - return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) + return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema) } schema, localPath := mustSplit2(ep, ":") - return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) + return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema) } if strings.Contains(ep, "://") { url, err := url.Parse(ep) if err != nil { - return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL + return ep, ep, CREDS_OPTIONAL } if url.Scheme == "http" || url.Scheme == "https" { - return url.Host, url.Hostname(), schemeToCredsRequirement(url.Scheme) + return url.Host, url.Host, schemeToCredsRequirement(url.Scheme) } - return ep, url.Hostname(), schemeToCredsRequirement(url.Scheme) + return ep, url.Host, schemeToCredsRequirement(url.Scheme) } // Handles plain addresses like 10.0.0.44:437. - return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL + return ep, ep, CREDS_OPTIONAL } // RequiresCredentials returns whether given endpoint requires diff --git a/vendor/go.etcd.io/etcd/client/v3/lease.go b/vendor/go.etcd.io/etcd/client/v3/lease.go index 9e1b70464..19af9c093 100644 --- a/vendor/go.etcd.io/etcd/client/v3/lease.go +++ b/vendor/go.etcd.io/etcd/client/v3/lease.go @@ -294,7 +294,9 @@ func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAl } l.mu.Unlock() - go l.keepAliveCtxCloser(ctx, id, ka.donec) + if ctx.Done() != nil { + go l.keepAliveCtxCloser(ctx, id, ka.donec) + } l.firstKeepAliveOnce.Do(func() { go l.recvKeepAliveLoop() go l.deadlineLoop() diff --git a/vendor/go.etcd.io/etcd/client/v3/maintenance.go b/vendor/go.etcd.io/etcd/client/v3/maintenance.go index dbea530e6..a98b8ca51 100644 --- a/vendor/go.etcd.io/etcd/client/v3/maintenance.go +++ b/vendor/go.etcd.io/etcd/client/v3/maintenance.go @@ -92,6 +92,7 @@ func NewMaintenance(c *Client) Maintenance { err = c.getToken(dctx) cancel() if err != nil { + conn.Close() return nil, nil, fmt.Errorf("failed to getToken from endpoint %s with maintenance client: %v", endpoint, err) } cancel = func() { conn.Close() } diff --git a/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go b/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go index 04f157a1d..7dc5ddae0 100644 --- a/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go +++ b/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go @@ -74,13 +74,7 @@ func (c *Client) unaryClientInterceptor(optFuncs ...retryOption) grpc.UnaryClien continue } if c.shouldRefreshToken(lastErr, callOpts) { - // clear auth token before refreshing it. - // call c.Auth.Authenticate with an invalid token will always fail the auth check on the server-side, - // if the server has not apply the patch of pr #12165 (https://github.com/etcd-io/etcd/pull/12165) - // and a rpctypes.ErrInvalidAuthToken will recursively call c.getToken until system run out of resource. - c.authTokenBundle.UpdateAuthToken("") - - gterr := c.getToken(ctx) + gterr := c.refreshToken(ctx) if gterr != nil { c.GetLogger().Warn( "retrying of unary invoker failed to fetch new auth token", @@ -161,6 +155,24 @@ func (c *Client) shouldRefreshToken(err error, callOpts *options) bool { (rpctypes.Error(err) == rpctypes.ErrInvalidAuthToken || rpctypes.Error(err) == rpctypes.ErrAuthOldRevision) } +func (c *Client) refreshToken(ctx context.Context) error { + if c.authTokenBundle == nil { + // c.authTokenBundle will be initialized only when + // c.Username != "" && c.Password != "". + // + // When users use the TLS CommonName based authentication, the + // authTokenBundle is always nil. But it's possible for the clients + // to get `rpctypes.ErrAuthOldRevision` response when the clients + // concurrently modify auth data (e.g, addUser, deleteUser etc.). + // In this case, there is no need to refresh the token; instead the + // clients just need to retry the operations (e.g. Put, Delete etc). + return nil + } + // clear auth token before refreshing it. + c.authTokenBundle.UpdateAuthToken("") + return c.getToken(ctx) +} + // type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a // proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish // a new ClientStream according to the retry policy. @@ -259,10 +271,7 @@ func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{} return true, err } if s.client.shouldRefreshToken(err, s.callOpts) { - // clear auth token to avoid failure when call getToken - s.client.authTokenBundle.UpdateAuthToken("") - - gterr := s.client.getToken(s.ctx) + gterr := s.client.refreshToken(s.ctx) if gterr != nil { s.client.lg.Warn("retry failed to fetch new auth token", zap.Error(gterr)) return false, err // return the original error for simplicity diff --git a/vendor/go.etcd.io/etcd/client/v3/txn.go b/vendor/go.etcd.io/etcd/client/v3/txn.go index 22301fba6..3f6a953cf 100644 --- a/vendor/go.etcd.io/etcd/client/v3/txn.go +++ b/vendor/go.etcd.io/etcd/client/v3/txn.go @@ -25,15 +25,14 @@ import ( // Txn is the interface that wraps mini-transactions. // -// Txn(context.TODO()).If( -// Compare(Value(k1), ">", v1), -// Compare(Version(k1), "=", 2) -// ).Then( -// OpPut(k2,v2), OpPut(k3,v3) -// ).Else( -// OpPut(k4,v4), OpPut(k5,v5) -// ).Commit() -// +// Txn(context.TODO()).If( +// Compare(Value(k1), ">", v1), +// Compare(Version(k1), "=", 2) +// ).Then( +// OpPut(k2,v2), OpPut(k3,v3) +// ).Else( +// OpPut(k4,v4), OpPut(k5,v5) +// ).Commit() type Txn interface { // If takes a list of comparison. If all comparisons passed in succeed, // the operations passed into Then() will be executed. Or the operations diff --git a/vendor/go.etcd.io/etcd/client/v3/watch.go b/vendor/go.etcd.io/etcd/client/v3/watch.go index b73925ba1..41a6ec976 100644 --- a/vendor/go.etcd.io/etcd/client/v3/watch.go +++ b/vendor/go.etcd.io/etcd/client/v3/watch.go @@ -37,6 +37,13 @@ const ( EventTypePut = mvccpb.PUT closeSendErrTimeout = 250 * time.Millisecond + + // AutoWatchID is the watcher ID passed in WatchStream.Watch when no + // user-provided ID is available. If pass, an ID will automatically be assigned. + AutoWatchID = 0 + + // InvalidWatchID represents an invalid watch ID and prevents duplication with an existing watch. + InvalidWatchID = -1 ) type Event mvccpb.Event @@ -450,7 +457,7 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) { func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) { // check watch ID for backward compatibility (<= v3.3) - if resp.WatchId == -1 || (resp.Canceled && resp.CancelReason != "") { + if resp.WatchId == InvalidWatchID || (resp.Canceled && resp.CancelReason != "") { w.closeErr = v3rpc.Error(errors.New(resp.CancelReason)) // failed; no channel close(ws.recvc) @@ -481,7 +488,7 @@ func (w *watchGrpcStream) closeSubstream(ws *watcherStream) { } else if ws.outc != nil { close(ws.outc) } - if ws.id != -1 { + if ws.id != InvalidWatchID { delete(w.substreams, ws.id) return } @@ -533,6 +540,7 @@ func (w *watchGrpcStream) run() { cancelSet := make(map[int64]struct{}) var cur *pb.WatchResponse + backoff := time.Millisecond for { select { // Watch() requested @@ -543,7 +551,7 @@ func (w *watchGrpcStream) run() { // TODO: pass custom watch ID? ws := &watcherStream{ initReq: *wreq, - id: -1, + id: InvalidWatchID, outc: outc, // unbuffered so resumes won't cause repeat events recvc: make(chan *WatchResponse), @@ -649,6 +657,7 @@ func (w *watchGrpcStream) run() { closeErr = err return } + backoff = w.backoffIfUnavailable(backoff, err) if wc, closeErr = w.newWatchClient(); closeErr != nil { return } @@ -669,7 +678,7 @@ func (w *watchGrpcStream) run() { if len(w.substreams)+len(w.resuming) == 0 { return } - if ws.id != -1 { + if ws.id != InvalidWatchID { // client is closing an established watch; close it on the server proactively instead of waiting // to close when the next message arrives cancelSet[ws.id] = struct{}{} @@ -716,9 +725,9 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool { cancelReason: pbresp.CancelReason, } - // watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of -1 to + // watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of InvalidWatchID to // indicate they should be broadcast. - if wr.IsProgressNotify() && pbresp.WatchId == -1 { + if wr.IsProgressNotify() && pbresp.WatchId == InvalidWatchID { return w.broadcastResponse(wr) } @@ -839,7 +848,7 @@ func (w *watchGrpcStream) serveSubstream(ws *watcherStream, resumec chan struct{ } } else { // current progress of watch; <= store revision - nextRev = wr.Header.Revision + nextRev = wr.Header.Revision + 1 } if len(wr.Events) > 0 { @@ -873,7 +882,7 @@ func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) { w.resumec = make(chan struct{}) w.joinSubstreams() for _, ws := range w.substreams { - ws.id = -1 + ws.id = InvalidWatchID w.resuming = append(w.resuming, ws) } // strip out nils, if any @@ -963,6 +972,21 @@ func (w *watchGrpcStream) joinSubstreams() { var maxBackoff = 100 * time.Millisecond +func (w *watchGrpcStream) backoffIfUnavailable(backoff time.Duration, err error) time.Duration { + if isUnavailableErr(w.ctx, err) { + // retry, but backoff + if backoff < maxBackoff { + // 25% backoff factor + backoff = backoff + backoff/4 + if backoff > maxBackoff { + backoff = maxBackoff + } + } + time.Sleep(backoff) + } + return backoff +} + // openWatchClient retries opening a watch client until success or halt. // manually retry in case "ws==nil && err==nil" // TODO: remove FailFast=false @@ -983,17 +1007,7 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) if isHaltErr(w.ctx, err) { return nil, v3rpc.Error(err) } - if isUnavailableErr(w.ctx, err) { - // retry, but backoff - if backoff < maxBackoff { - // 25% backoff factor - backoff = backoff + backoff/4 - if backoff > maxBackoff { - backoff = maxBackoff - } - } - time.Sleep(backoff) - } + backoff = w.backoffIfUnavailable(backoff, err) } return ws, nil } diff --git a/vendor/go.uber.org/atomic/CHANGELOG.md b/vendor/go.uber.org/atomic/CHANGELOG.md index 38f564e2b..6f87f33fa 100644 --- a/vendor/go.uber.org/atomic/CHANGELOG.md +++ b/vendor/go.uber.org/atomic/CHANGELOG.md @@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.11.0] - 2023-05-02 +### Fixed +- Fix initialization of `Value` wrappers. + +### Added +- Add `String` method to `atomic.Pointer[T]` type allowing users to safely print +underlying values of pointers. + +[1.11.0]: https://github.com/uber-go/atomic/compare/v1.10.0...v1.11.0 + +## [1.10.0] - 2022-08-11 +### Added +- Add `atomic.Float32` type for atomic operations on `float32`. +- Add `CompareAndSwap` and `Swap` methods to `atomic.String`, `atomic.Error`, + and `atomic.Value`. +- Add generic `atomic.Pointer[T]` type for atomic operations on pointers of any + type. This is present only for Go 1.18 or higher, and is a drop-in for + replacement for the standard library's `sync/atomic.Pointer` type. + +### Changed +- Deprecate `CAS` methods on all types in favor of corresponding + `CompareAndSwap` methods. + +Thanks to @eNV25 and @icpd for their contributions to this release. + +[1.10.0]: https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0 + ## [1.9.0] - 2021-07-15 ### Added - Add `Float64.Swap` to match int atomic operations. diff --git a/vendor/go.uber.org/atomic/bool.go b/vendor/go.uber.org/atomic/bool.go index 209df7bbc..f0a2ddd14 100644 --- a/vendor/go.uber.org/atomic/bool.go +++ b/vendor/go.uber.org/atomic/bool.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -55,8 +55,15 @@ func (x *Bool) Store(val bool) { } // CAS is an atomic compare-and-swap for bool values. +// +// Deprecated: Use CompareAndSwap. func (x *Bool) CAS(old, new bool) (swapped bool) { - return x.v.CAS(boolToInt(old), boolToInt(new)) + return x.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap for bool values. +func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) { + return x.v.CompareAndSwap(boolToInt(old), boolToInt(new)) } // Swap atomically stores the given bool and returns the old diff --git a/vendor/go.uber.org/atomic/duration.go b/vendor/go.uber.org/atomic/duration.go index 207594f5e..7c23868fc 100644 --- a/vendor/go.uber.org/atomic/duration.go +++ b/vendor/go.uber.org/atomic/duration.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -56,8 +56,15 @@ func (x *Duration) Store(val time.Duration) { } // CAS is an atomic compare-and-swap for time.Duration values. +// +// Deprecated: Use CompareAndSwap. func (x *Duration) CAS(old, new time.Duration) (swapped bool) { - return x.v.CAS(int64(old), int64(new)) + return x.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap for time.Duration values. +func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) { + return x.v.CompareAndSwap(int64(old), int64(new)) } // Swap atomically stores the given time.Duration and returns the old diff --git a/vendor/go.uber.org/atomic/error.go b/vendor/go.uber.org/atomic/error.go index 3be19c35e..b7e3f1291 100644 --- a/vendor/go.uber.org/atomic/error.go +++ b/vendor/go.uber.org/atomic/error.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -49,3 +49,24 @@ func (x *Error) Load() error { func (x *Error) Store(val error) { x.v.Store(packError(val)) } + +// CompareAndSwap is an atomic compare-and-swap for error values. +func (x *Error) CompareAndSwap(old, new error) (swapped bool) { + if x.v.CompareAndSwap(packError(old), packError(new)) { + return true + } + + if old == _zeroError { + // If the old value is the empty value, then it's possible the + // underlying Value hasn't been set and is nil, so retry with nil. + return x.v.CompareAndSwap(nil, packError(new)) + } + + return false +} + +// Swap atomically stores the given error and returns the old +// value. +func (x *Error) Swap(val error) (old error) { + return unpackError(x.v.Swap(packError(val))) +} diff --git a/vendor/go.uber.org/atomic/error_ext.go b/vendor/go.uber.org/atomic/error_ext.go index ffe0be21c..d31fb633b 100644 --- a/vendor/go.uber.org/atomic/error_ext.go +++ b/vendor/go.uber.org/atomic/error_ext.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Uber Technologies, Inc. +// Copyright (c) 2020-2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package atomic // atomic.Value panics on nil inputs, or if the underlying type changes. // Stabilize by always storing a custom struct that we control. -//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go +//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -compareandswap -swap -file=error.go type packedError struct{ Value error } diff --git a/vendor/go.uber.org/atomic/float32.go b/vendor/go.uber.org/atomic/float32.go new file mode 100644 index 000000000..62c36334f --- /dev/null +++ b/vendor/go.uber.org/atomic/float32.go @@ -0,0 +1,77 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "encoding/json" + "math" +) + +// Float32 is an atomic type-safe wrapper for float32 values. +type Float32 struct { + _ nocmp // disallow non-atomic comparison + + v Uint32 +} + +var _zeroFloat32 float32 + +// NewFloat32 creates a new Float32. +func NewFloat32(val float32) *Float32 { + x := &Float32{} + if val != _zeroFloat32 { + x.Store(val) + } + return x +} + +// Load atomically loads the wrapped float32. +func (x *Float32) Load() float32 { + return math.Float32frombits(x.v.Load()) +} + +// Store atomically stores the passed float32. +func (x *Float32) Store(val float32) { + x.v.Store(math.Float32bits(val)) +} + +// Swap atomically stores the given float32 and returns the old +// value. +func (x *Float32) Swap(val float32) (old float32) { + return math.Float32frombits(x.v.Swap(math.Float32bits(val))) +} + +// MarshalJSON encodes the wrapped float32 into JSON. +func (x *Float32) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} + +// UnmarshalJSON decodes a float32 from JSON. +func (x *Float32) UnmarshalJSON(b []byte) error { + var v float32 + if err := json.Unmarshal(b, &v); err != nil { + return err + } + x.Store(v) + return nil +} diff --git a/vendor/go.uber.org/atomic/float32_ext.go b/vendor/go.uber.org/atomic/float32_ext.go new file mode 100644 index 000000000..b0cd8d9c8 --- /dev/null +++ b/vendor/go.uber.org/atomic/float32_ext.go @@ -0,0 +1,76 @@ +// Copyright (c) 2020-2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "math" + "strconv" +) + +//go:generate bin/gen-atomicwrapper -name=Float32 -type=float32 -wrapped=Uint32 -pack=math.Float32bits -unpack=math.Float32frombits -swap -json -imports math -file=float32.go + +// Add atomically adds to the wrapped float32 and returns the new value. +func (f *Float32) Add(delta float32) float32 { + for { + old := f.Load() + new := old + delta + if f.CAS(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float32 and returns the new value. +func (f *Float32) Sub(delta float32) float32 { + return f.Add(-delta) +} + +// CAS is an atomic compare-and-swap for float32 values. +// +// Deprecated: Use CompareAndSwap +func (f *Float32) CAS(old, new float32) (swapped bool) { + return f.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap for float32 values. +// +// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators +// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN. +// This avoids typical CompareAndSwap loops from blocking forever, e.g., +// +// for { +// old := atom.Load() +// new = f(old) +// if atom.CompareAndSwap(old, new) { +// break +// } +// } +// +// If CompareAndSwap did not match NaN to match, then the above would loop forever. +func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) { + return f.v.CompareAndSwap(math.Float32bits(old), math.Float32bits(new)) +} + +// String encodes the wrapped value as a string. +func (f *Float32) String() string { + // 'g' is the behavior for floats with %v. + return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32) +} diff --git a/vendor/go.uber.org/atomic/float64.go b/vendor/go.uber.org/atomic/float64.go index 8a1367184..5bc11caab 100644 --- a/vendor/go.uber.org/atomic/float64.go +++ b/vendor/go.uber.org/atomic/float64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/atomic/float64_ext.go b/vendor/go.uber.org/atomic/float64_ext.go index df36b0107..48c52b0ab 100644 --- a/vendor/go.uber.org/atomic/float64_ext.go +++ b/vendor/go.uber.org/atomic/float64_ext.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Uber Technologies, Inc. +// Copyright (c) 2020-2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,21 +45,28 @@ func (f *Float64) Sub(delta float64) float64 { // CAS is an atomic compare-and-swap for float64 values. // -// Note: CAS handles NaN incorrectly. NaN != NaN using Go's inbuilt operators -// but CAS allows a stored NaN to compare equal to a passed in NaN. -// This avoids typical CAS loops from blocking forever, e.g., +// Deprecated: Use CompareAndSwap +func (f *Float64) CAS(old, new float64) (swapped bool) { + return f.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap for float64 values. // -// for { -// old := atom.Load() -// new = f(old) -// if atom.CAS(old, new) { -// break -// } -// } +// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators +// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN. +// This avoids typical CompareAndSwap loops from blocking forever, e.g., // -// If CAS did not match NaN to match, then the above would loop forever. -func (f *Float64) CAS(old, new float64) (swapped bool) { - return f.v.CAS(math.Float64bits(old), math.Float64bits(new)) +// for { +// old := atom.Load() +// new = f(old) +// if atom.CompareAndSwap(old, new) { +// break +// } +// } +// +// If CompareAndSwap did not match NaN to match, then the above would loop forever. +func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) { + return f.v.CompareAndSwap(math.Float64bits(old), math.Float64bits(new)) } // String encodes the wrapped value as a string. diff --git a/vendor/go.uber.org/atomic/int32.go b/vendor/go.uber.org/atomic/int32.go index 640ea36a1..5320eac10 100644 --- a/vendor/go.uber.org/atomic/int32.go +++ b/vendor/go.uber.org/atomic/int32.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,14 @@ func (i *Int32) Dec() int32 { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap. func (i *Int32) CAS(old, new int32) (swapped bool) { + return i.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) { return atomic.CompareAndSwapInt32(&i.v, old, new) } diff --git a/vendor/go.uber.org/atomic/int64.go b/vendor/go.uber.org/atomic/int64.go index 9ab66b980..460821d00 100644 --- a/vendor/go.uber.org/atomic/int64.go +++ b/vendor/go.uber.org/atomic/int64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,14 @@ func (i *Int64) Dec() int64 { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap. func (i *Int64) CAS(old, new int64) (swapped bool) { + return i.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) { return atomic.CompareAndSwapInt64(&i.v, old, new) } diff --git a/vendor/go.uber.org/atomic/nocmp.go b/vendor/go.uber.org/atomic/nocmp.go index a8201cb4a..54b74174a 100644 --- a/vendor/go.uber.org/atomic/nocmp.go +++ b/vendor/go.uber.org/atomic/nocmp.go @@ -23,13 +23,13 @@ package atomic // nocmp is an uncomparable struct. Embed this inside another struct to make // it uncomparable. // -// type Foo struct { -// nocmp -// // ... -// } +// type Foo struct { +// nocmp +// // ... +// } // // This DOES NOT: // -// - Disallow shallow copies of structs -// - Disallow comparison of pointers to uncomparable structs +// - Disallow shallow copies of structs +// - Disallow comparison of pointers to uncomparable structs type nocmp [0]func() diff --git a/vendor/go.uber.org/atomic/pointer_go118.go b/vendor/go.uber.org/atomic/pointer_go118.go new file mode 100644 index 000000000..1fb6c03b2 --- /dev/null +++ b/vendor/go.uber.org/atomic/pointer_go118.go @@ -0,0 +1,31 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.18 +// +build go1.18 + +package atomic + +import "fmt" + +// String returns a human readable representation of a Pointer's underlying value. +func (p *Pointer[T]) String() string { + return fmt.Sprint(p.Load()) +} diff --git a/vendor/go.uber.org/atomic/pointer_go118_pre119.go b/vendor/go.uber.org/atomic/pointer_go118_pre119.go new file mode 100644 index 000000000..e0f47dba4 --- /dev/null +++ b/vendor/go.uber.org/atomic/pointer_go118_pre119.go @@ -0,0 +1,60 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.18 && !go1.19 +// +build go1.18,!go1.19 + +package atomic + +import "unsafe" + +type Pointer[T any] struct { + _ nocmp // disallow non-atomic comparison + p UnsafePointer +} + +// NewPointer creates a new Pointer. +func NewPointer[T any](v *T) *Pointer[T] { + var p Pointer[T] + if v != nil { + p.p.Store(unsafe.Pointer(v)) + } + return &p +} + +// Load atomically loads the wrapped value. +func (p *Pointer[T]) Load() *T { + return (*T)(p.p.Load()) +} + +// Store atomically stores the passed value. +func (p *Pointer[T]) Store(val *T) { + p.p.Store(unsafe.Pointer(val)) +} + +// Swap atomically swaps the wrapped pointer and returns the old value. +func (p *Pointer[T]) Swap(val *T) (old *T) { + return (*T)(p.p.Swap(unsafe.Pointer(val))) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) { + return p.p.CompareAndSwap(unsafe.Pointer(old), unsafe.Pointer(new)) +} diff --git a/vendor/go.uber.org/atomic/pointer_go119.go b/vendor/go.uber.org/atomic/pointer_go119.go new file mode 100644 index 000000000..6726f17ad --- /dev/null +++ b/vendor/go.uber.org/atomic/pointer_go119.go @@ -0,0 +1,61 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.19 +// +build go1.19 + +package atomic + +import "sync/atomic" + +// Pointer is an atomic pointer of type *T. +type Pointer[T any] struct { + _ nocmp // disallow non-atomic comparison + p atomic.Pointer[T] +} + +// NewPointer creates a new Pointer. +func NewPointer[T any](v *T) *Pointer[T] { + var p Pointer[T] + if v != nil { + p.p.Store(v) + } + return &p +} + +// Load atomically loads the wrapped value. +func (p *Pointer[T]) Load() *T { + return p.p.Load() +} + +// Store atomically stores the passed value. +func (p *Pointer[T]) Store(val *T) { + p.p.Store(val) +} + +// Swap atomically swaps the wrapped pointer and returns the old value. +func (p *Pointer[T]) Swap(val *T) (old *T) { + return p.p.Swap(val) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) { + return p.p.CompareAndSwap(old, new) +} diff --git a/vendor/go.uber.org/atomic/string.go b/vendor/go.uber.org/atomic/string.go index 80df93d09..061466c5b 100644 --- a/vendor/go.uber.org/atomic/string.go +++ b/vendor/go.uber.org/atomic/string.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,13 +42,31 @@ func NewString(val string) *String { // Load atomically loads the wrapped string. func (x *String) Load() string { - if v := x.v.Load(); v != nil { - return v.(string) - } - return _zeroString + return unpackString(x.v.Load()) } // Store atomically stores the passed string. func (x *String) Store(val string) { - x.v.Store(val) + x.v.Store(packString(val)) +} + +// CompareAndSwap is an atomic compare-and-swap for string values. +func (x *String) CompareAndSwap(old, new string) (swapped bool) { + if x.v.CompareAndSwap(packString(old), packString(new)) { + return true + } + + if old == _zeroString { + // If the old value is the empty value, then it's possible the + // underlying Value hasn't been set and is nil, so retry with nil. + return x.v.CompareAndSwap(nil, packString(new)) + } + + return false +} + +// Swap atomically stores the given string and returns the old +// value. +func (x *String) Swap(val string) (old string) { + return unpackString(x.v.Swap(packString(val))) } diff --git a/vendor/go.uber.org/atomic/string_ext.go b/vendor/go.uber.org/atomic/string_ext.go index 83d92edaf..019109c86 100644 --- a/vendor/go.uber.org/atomic/string_ext.go +++ b/vendor/go.uber.org/atomic/string_ext.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,18 @@ package atomic -//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go -// Note: No Swap as String wraps Value, which wraps the stdlib sync/atomic.Value which -// only supports Swap as of go1.17: https://github.com/golang/go/issues/39351 +//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped Value -pack packString -unpack unpackString -compareandswap -swap -file=string.go + +func packString(s string) interface{} { + return s +} + +func unpackString(v interface{}) string { + if s, ok := v.(string); ok { + return s + } + return "" +} // String returns the wrapped value. func (s *String) String() string { diff --git a/vendor/go.uber.org/atomic/time.go b/vendor/go.uber.org/atomic/time.go index 33460fc37..cc2a230c0 100644 --- a/vendor/go.uber.org/atomic/time.go +++ b/vendor/go.uber.org/atomic/time.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/atomic/uint32.go b/vendor/go.uber.org/atomic/uint32.go index 7859a9cc3..4adc294ac 100644 --- a/vendor/go.uber.org/atomic/uint32.go +++ b/vendor/go.uber.org/atomic/uint32.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,14 @@ func (i *Uint32) Dec() uint32 { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap. func (i *Uint32) CAS(old, new uint32) (swapped bool) { + return i.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { return atomic.CompareAndSwapUint32(&i.v, old, new) } diff --git a/vendor/go.uber.org/atomic/uint64.go b/vendor/go.uber.org/atomic/uint64.go index 2f2a7db63..0e2eddb30 100644 --- a/vendor/go.uber.org/atomic/uint64.go +++ b/vendor/go.uber.org/atomic/uint64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,14 @@ func (i *Uint64) Dec() uint64 { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap. func (i *Uint64) CAS(old, new uint64) (swapped bool) { + return i.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { return atomic.CompareAndSwapUint64(&i.v, old, new) } diff --git a/vendor/go.uber.org/atomic/uintptr.go b/vendor/go.uber.org/atomic/uintptr.go index ecf7a7727..7d5b000d6 100644 --- a/vendor/go.uber.org/atomic/uintptr.go +++ b/vendor/go.uber.org/atomic/uintptr.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2021 Uber Technologies, Inc. +// Copyright (c) 2020-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,14 @@ func (i *Uintptr) Dec() uintptr { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap. func (i *Uintptr) CAS(old, new uintptr) (swapped bool) { + return i.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { return atomic.CompareAndSwapUintptr(&i.v, old, new) } diff --git a/vendor/go.uber.org/atomic/unsafe_pointer.go b/vendor/go.uber.org/atomic/unsafe_pointer.go index 169f793dc..34868baf6 100644 --- a/vendor/go.uber.org/atomic/unsafe_pointer.go +++ b/vendor/go.uber.org/atomic/unsafe_pointer.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Uber Technologies, Inc. +// Copyright (c) 2021-2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -53,6 +53,13 @@ func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) { } // CAS is an atomic compare-and-swap. +// +// Deprecated: Use CompareAndSwap func (p *UnsafePointer) CAS(old, new unsafe.Pointer) (swapped bool) { + return p.CompareAndSwap(old, new) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) { return atomic.CompareAndSwapPointer(&p.v, old, new) } diff --git a/vendor/go.uber.org/atomic/value.go b/vendor/go.uber.org/atomic/value.go index 671f3a382..52caedb9a 100644 --- a/vendor/go.uber.org/atomic/value.go +++ b/vendor/go.uber.org/atomic/value.go @@ -25,7 +25,7 @@ import "sync/atomic" // Value shadows the type of the same name from sync/atomic // https://godoc.org/sync/atomic#Value type Value struct { - atomic.Value - _ nocmp // disallow non-atomic comparison + + atomic.Value } diff --git a/vendor/go.uber.org/mock/AUTHORS b/vendor/go.uber.org/mock/AUTHORS new file mode 100644 index 000000000..660b8ccc8 --- /dev/null +++ b/vendor/go.uber.org/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/go.uber.org/mock/LICENSE b/vendor/go.uber.org/mock/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/go.uber.org/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.uber.org/mock/mockgen/generic_go118.go b/vendor/go.uber.org/mock/mockgen/generic_go118.go new file mode 100644 index 000000000..b7b449476 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/generic_go118.go @@ -0,0 +1,130 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build go1.18 +// +build go1.18 + +package main + +import ( + "errors" + "fmt" + "go/ast" + "go/token" + "strings" + + "go.uber.org/mock/mockgen/model" +) + +func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field { + if ts == nil || ts.TypeParams == nil { + return nil + } + return ts.TypeParams.List +} + +func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { + switch v := typ.(type) { + case *ast.IndexExpr: + m, err := p.parseType(pkg, v.X, tps) + if err != nil { + return nil, err + } + nm, ok := m.(*model.NamedType) + if !ok { + return m, nil + } + t, err := p.parseType(pkg, v.Index, tps) + if err != nil { + return nil, err + } + nm.TypeParams = &model.TypeParametersType{TypeParameters: []model.Type{t}} + return m, nil + case *ast.IndexListExpr: + m, err := p.parseType(pkg, v.X, tps) + if err != nil { + return nil, err + } + nm, ok := m.(*model.NamedType) + if !ok { + return m, nil + } + var ts []model.Type + for _, expr := range v.Indices { + t, err := p.parseType(pkg, expr, tps) + if err != nil { + return nil, err + } + ts = append(ts, t) + } + nm.TypeParams = &model.TypeParametersType{TypeParameters: ts} + return m, nil + } + return nil, nil +} + +func getIdentTypeParams(decl any) string { + if decl == nil { + return "" + } + ts, ok := decl.(*ast.TypeSpec) + if !ok { + return "" + } + if ts.TypeParams == nil || len(ts.TypeParams.List) == 0 { + return "" + } + var sb strings.Builder + sb.WriteString("[") + for i, v := range ts.TypeParams.List { + if i != 0 { + sb.WriteString(", ") + } + sb.WriteString(v.Names[0].Name) + } + sb.WriteString("]") + return sb.String() +} + +func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + var indices []ast.Expr + var typ ast.Expr + switch v := field.Type.(type) { + case *ast.IndexExpr: + indices = []ast.Expr{v.Index} + typ = v.X + case *ast.IndexListExpr: + indices = v.Indices + typ = v.X + case *ast.UnaryExpr: + if v.Op == token.TILDE { + return nil, errConstraintInterface + } + return nil, fmt.Errorf("~T may only appear as constraint for %T", field.Type) + case *ast.BinaryExpr: + if v.Op == token.OR { + return nil, errConstraintInterface + } + return nil, fmt.Errorf("A|B may only appear as constraint for %T", field.Type) + default: + return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) + } + + nf := &ast.Field{ + Doc: field.Comment, + Names: field.Names, + Type: typ, + Tag: field.Tag, + Comment: field.Comment, + } + + it.embeddedInstTypeParams = indices + + return p.parseMethod(nf, it, iface, pkg, tps) +} + +var errConstraintInterface = errors.New("interface contains constraints") diff --git a/vendor/go.uber.org/mock/mockgen/generic_notgo118.go b/vendor/go.uber.org/mock/mockgen/generic_notgo118.go new file mode 100644 index 000000000..8a779c8b2 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/generic_notgo118.go @@ -0,0 +1,41 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !go1.18 +// +build !go1.18 + +package main + +import ( + "fmt" + "go/ast" + + "go.uber.org/mock/mockgen/model" +) + +func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field { + return nil +} + +func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { + return nil, nil +} + +func getIdentTypeParams(decl any) string { + return "" +} + +func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) +} diff --git a/vendor/go.uber.org/mock/mockgen/mockgen.go b/vendor/go.uber.org/mock/mockgen/mockgen.go new file mode 100644 index 000000000..df7d85f09 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/mockgen.go @@ -0,0 +1,883 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// MockGen generates mock implementations of Go interfaces. +package main + +// TODO: This does not support recursive embedded interfaces. +// TODO: This does not support embedding package-local interfaces in a separate file. + +import ( + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "go/token" + "io" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "unicode" + + "golang.org/x/mod/modfile" + toolsimports "golang.org/x/tools/imports" + + "go.uber.org/mock/mockgen/model" +) + +const ( + gomockImportPath = "go.uber.org/mock/gomock" +) + +var ( + version = "" + commit = "none" + date = "unknown" +) + +var ( + source = flag.String("source", "", "(source mode) Input Go source file; enables source mode.") + destination = flag.String("destination", "", "Output file; defaults to stdout.") + mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.") + packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.") + selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.") + writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.") + writeSourceComment = flag.Bool("write_source_comment", true, "Writes original file (source mode) or interface names (reflect mode) comment if true.") + writeGenerateDirective = flag.Bool("write_generate_directive", false, "Add //go:generate directive to regenerate the mock") + copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header") + typed = flag.Bool("typed", false, "Generate Type-safe 'Return', 'Do', 'DoAndReturn' function") + imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.") + auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.") + excludeInterfaces = flag.String("exclude_interfaces", "", "Comma-separated names of interfaces to be excluded") + + debugParser = flag.Bool("debug_parser", false, "Print out parser results only.") + showVersion = flag.Bool("version", false, "Print version.") +) + +func main() { + flag.Usage = usage + flag.Parse() + + if *showVersion { + printVersion() + return + } + + var pkg *model.Package + var err error + var packageName string + if *source != "" { + pkg, err = sourceMode(*source) + } else { + if flag.NArg() != 2 { + usage() + log.Fatal("Expected exactly two arguments") + } + packageName = flag.Arg(0) + interfaces := strings.Split(flag.Arg(1), ",") + if packageName == "." { + dir, err := os.Getwd() + if err != nil { + log.Fatalf("Get current directory failed: %v", err) + } + packageName, err = packageNameOfDir(dir) + if err != nil { + log.Fatalf("Parse package name failed: %v", err) + } + } + pkg, err = reflectMode(packageName, interfaces) + } + if err != nil { + log.Fatalf("Loading input failed: %v", err) + } + + if *debugParser { + pkg.Print(os.Stdout) + return + } + + outputPackageName := *packageOut + if outputPackageName == "" { + // pkg.Name in reflect mode is the base name of the import path, + // which might have characters that are illegal to have in package names. + outputPackageName = "mock_" + sanitize(pkg.Name) + } + + // outputPackagePath represents the fully qualified name of the package of + // the generated code. Its purposes are to prevent the module from importing + // itself and to prevent qualifying type names that come from its own + // package (i.e. if there is a type called X then we want to print "X" not + // "package.X" since "package" is this package). This can happen if the mock + // is output into an already existing package. + outputPackagePath := *selfPackage + if outputPackagePath == "" && *destination != "" { + dstPath, err := filepath.Abs(filepath.Dir(*destination)) + if err == nil { + pkgPath, err := parsePackageImport(dstPath) + if err == nil { + outputPackagePath = pkgPath + } else { + log.Println("Unable to infer -self_package from destination file path:", err) + } + } else { + log.Println("Unable to determine destination file path:", err) + } + } + + g := new(generator) + if *source != "" { + g.filename = *source + } else { + g.srcPackage = packageName + g.srcInterfaces = flag.Arg(1) + } + g.destination = *destination + + if *mockNames != "" { + g.mockNames = parseMockNames(*mockNames) + } + if *copyrightFile != "" { + header, err := os.ReadFile(*copyrightFile) + if err != nil { + log.Fatalf("Failed reading copyright file: %v", err) + } + + g.copyrightHeader = string(header) + } + if err := g.Generate(pkg, outputPackageName, outputPackagePath); err != nil { + log.Fatalf("Failed generating mock: %v", err) + } + output := g.Output() + dst := os.Stdout + if len(*destination) > 0 { + if err := os.MkdirAll(filepath.Dir(*destination), os.ModePerm); err != nil { + log.Fatalf("Unable to create directory: %v", err) + } + existing, err := os.ReadFile(*destination) + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Fatalf("Failed reading pre-exiting destination file: %v", err) + } + if len(existing) == len(output) && bytes.Equal(existing, output) { + return + } + f, err := os.Create(*destination) + if err != nil { + log.Fatalf("Failed opening destination file: %v", err) + } + defer f.Close() + dst = f + } + if _, err := dst.Write(output); err != nil { + log.Fatalf("Failed writing to destination: %v", err) + } +} + +func parseMockNames(names string) map[string]string { + mocksMap := make(map[string]string) + for _, kv := range strings.Split(names, ",") { + parts := strings.SplitN(kv, "=", 2) + if len(parts) != 2 || parts[1] == "" { + log.Fatalf("bad mock names spec: %v", kv) + } + mocksMap[parts[0]] = parts[1] + } + return mocksMap +} + +func parseExcludeInterfaces(names string) map[string]struct{} { + splitNames := strings.Split(names, ",") + namesSet := make(map[string]struct{}, len(splitNames)) + for _, name := range splitNames { + if name == "" { + continue + } + + namesSet[name] = struct{}{} + } + + if len(namesSet) == 0 { + return nil + } + + return namesSet +} + +func usage() { + _, _ = io.WriteString(os.Stderr, usageText) + flag.PrintDefaults() +} + +const usageText = `mockgen has two modes of operation: source and reflect. + +Source mode generates mock interfaces from a source file. +It is enabled by using the -source flag. Other flags that +may be useful in this mode are -imports and -aux_files. +Example: + mockgen -source=foo.go [other options] + +Reflect mode generates mock interfaces by building a program +that uses reflection to understand interfaces. It is enabled +by passing two non-flag arguments: an import path, and a +comma-separated list of symbols. +Example: + mockgen database/sql/driver Conn,Driver + +` + +type generator struct { + buf bytes.Buffer + indent string + mockNames map[string]string // may be empty + filename string // may be empty + destination string // may be empty + srcPackage, srcInterfaces string // may be empty + copyrightHeader string + + packageMap map[string]string // map from import path to package name +} + +func (g *generator) p(format string, args ...any) { + fmt.Fprintf(&g.buf, g.indent+format+"\n", args...) +} + +func (g *generator) in() { + g.indent += "\t" +} + +func (g *generator) out() { + if len(g.indent) > 0 { + g.indent = g.indent[0 : len(g.indent)-1] + } +} + +// sanitize cleans up a string to make a suitable package name. +func sanitize(s string) string { + t := "" + for _, r := range s { + if t == "" { + if unicode.IsLetter(r) || r == '_' { + t += string(r) + continue + } + } else { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + t += string(r) + continue + } + } + t += "_" + } + if t == "_" { + t = "x" + } + return t +} + +func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPackagePath string) error { + if outputPkgName != pkg.Name && *selfPackage == "" { + // reset outputPackagePath if it's not passed in through -self_package + outputPackagePath = "" + } + + if g.copyrightHeader != "" { + lines := strings.Split(g.copyrightHeader, "\n") + for _, line := range lines { + g.p("// %s", line) + } + g.p("") + } + + g.p("// Code generated by MockGen. DO NOT EDIT.") + if *writeSourceComment { + if g.filename != "" { + g.p("// Source: %v", g.filename) + } else { + g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces) + } + } + g.p("//") + g.p("// Generated by this command:") + g.p("//") + // only log the name of the executable, not the full path + name := filepath.Base(os.Args[0]) + if runtime.GOOS == "windows" { + name = strings.TrimSuffix(name, ".exe") + } + g.p("//\t%v", strings.Join(append([]string{name}, os.Args[1:]...), " ")) + g.p("//") + + // Get all required imports, and generate unique names for them all. + im := pkg.Imports() + im[gomockImportPath] = true + + // Only import reflect if it's used. We only use reflect in mocked methods + // so only import if any of the mocked interfaces have methods. + for _, intf := range pkg.Interfaces { + if len(intf.Methods) > 0 { + im["reflect"] = true + break + } + } + + // Sort keys to make import alias generation predictable + sortedPaths := make([]string, len(im)) + x := 0 + for pth := range im { + sortedPaths[x] = pth + x++ + } + sort.Strings(sortedPaths) + + packagesName := createPackageMap(sortedPaths) + + definedImports := make(map[string]string, len(im)) + if *imports != "" { + for _, kv := range strings.Split(*imports, ",") { + eq := strings.Index(kv, "=") + if k, v := kv[:eq], kv[eq+1:]; k != "." { + definedImports[v] = k + } + } + } + + g.packageMap = make(map[string]string, len(im)) + localNames := make(map[string]bool, len(im)) + for _, pth := range sortedPaths { + base, ok := packagesName[pth] + if !ok { + base = sanitize(path.Base(pth)) + } + + // Local names for an imported package can usually be the basename of the import path. + // A couple of situations don't permit that, such as duplicate local names + // (e.g. importing "html/template" and "text/template"), or where the basename is + // a keyword (e.g. "foo/case") or when defining a name for that by using the -imports flag. + // try base0, base1, ... + pkgName := base + + if _, ok := definedImports[base]; ok { + pkgName = definedImports[base] + } + + i := 0 + for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() || pkgName == "any" { + pkgName = base + strconv.Itoa(i) + i++ + } + + // Avoid importing package if source pkg == output pkg + if pth == pkg.PkgPath && outputPackagePath == pkg.PkgPath { + continue + } + + g.packageMap[pth] = pkgName + localNames[pkgName] = true + } + + if *writePkgComment { + // Ensure there's an empty line before the package to follow the recommendations: + // https://github.com/golang/go/wiki/CodeReviewComments#package-comments + g.p("") + + g.p("// Package %v is a generated GoMock package.", outputPkgName) + } + g.p("package %v", outputPkgName) + g.p("") + g.p("import (") + g.in() + for pkgPath, pkgName := range g.packageMap { + if pkgPath == outputPackagePath { + continue + } + g.p("%v %q", pkgName, pkgPath) + } + for _, pkgPath := range pkg.DotImports { + g.p(". %q", pkgPath) + } + g.out() + g.p(")") + + if *writeGenerateDirective { + g.p("//go:generate %v", strings.Join(os.Args, " ")) + } + + for _, intf := range pkg.Interfaces { + if err := g.GenerateMockInterface(intf, outputPackagePath); err != nil { + return err + } + } + + return nil +} + +// The name of the mock type to use for the given interface identifier. +func (g *generator) mockName(typeName string) string { + if mockName, ok := g.mockNames[typeName]; ok { + return mockName + } + + return "Mock" + typeName +} + +// formattedTypeParams returns a long and short form of type param info used for +// printing. If analyzing a interface with type param [I any, O any] the result +// will be: +// "[I any, O any]", "[I, O]" +func (g *generator) formattedTypeParams(it *model.Interface, pkgOverride string) (string, string) { + if len(it.TypeParams) == 0 { + return "", "" + } + var long, short strings.Builder + long.WriteString("[") + short.WriteString("[") + for i, v := range it.TypeParams { + if i != 0 { + long.WriteString(", ") + short.WriteString(", ") + } + long.WriteString(v.Name) + short.WriteString(v.Name) + long.WriteString(fmt.Sprintf(" %s", v.Type.String(g.packageMap, pkgOverride))) + } + long.WriteString("]") + short.WriteString("]") + return long.String(), short.String() +} + +func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePath string) error { + mockType := g.mockName(intf.Name) + longTp, shortTp := g.formattedTypeParams(intf, outputPackagePath) + + g.p("") + g.p("// %v is a mock of %v interface.", mockType, intf.Name) + g.p("type %v%v struct {", mockType, longTp) + g.in() + g.p("ctrl *gomock.Controller") + g.p("recorder *%vMockRecorder%v", mockType, shortTp) + g.out() + g.p("}") + g.p("") + + g.p("// %vMockRecorder is the mock recorder for %v.", mockType, mockType) + g.p("type %vMockRecorder%v struct {", mockType, longTp) + g.in() + g.p("mock *%v%v", mockType, shortTp) + g.out() + g.p("}") + g.p("") + + g.p("// New%v creates a new mock instance.", mockType) + g.p("func New%v%v(ctrl *gomock.Controller) *%v%v {", mockType, longTp, mockType, shortTp) + g.in() + g.p("mock := &%v%v{ctrl: ctrl}", mockType, shortTp) + g.p("mock.recorder = &%vMockRecorder%v{mock}", mockType, shortTp) + g.p("return mock") + g.out() + g.p("}") + g.p("") + + // XXX: possible name collision here if someone has EXPECT in their interface. + g.p("// EXPECT returns an object that allows the caller to indicate expected use.") + g.p("func (m *%v%v) EXPECT() *%vMockRecorder%v {", mockType, shortTp, mockType, shortTp) + g.in() + g.p("return m.recorder") + g.out() + g.p("}") + + g.GenerateMockMethods(mockType, intf, outputPackagePath, longTp, shortTp, *typed) + + return nil +} + +type byMethodName []*model.Method + +func (b byMethodName) Len() int { return len(b) } +func (b byMethodName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byMethodName) Less(i, j int) bool { return b[i].Name < b[j].Name } + +func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride, longTp, shortTp string, typed bool) { + sort.Sort(byMethodName(intf.Methods)) + for _, m := range intf.Methods { + g.p("") + _ = g.GenerateMockMethod(mockType, m, pkgOverride, shortTp) + g.p("") + _ = g.GenerateMockRecorderMethod(intf, m, shortTp, typed) + if typed { + g.p("") + _ = g.GenerateMockReturnCallMethod(intf, m, pkgOverride, longTp, shortTp) + } + } +} + +func makeArgString(argNames, argTypes []string) string { + args := make([]string, len(argNames)) + for i, name := range argNames { + // specify the type only once for consecutive args of the same type + if i+1 < len(argTypes) && argTypes[i] == argTypes[i+1] { + args[i] = name + } else { + args[i] = name + " " + argTypes[i] + } + } + return strings.Join(args, ", ") +} + +// GenerateMockMethod generates a mock method implementation. +// If non-empty, pkgOverride is the package in which unqualified types reside. +func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride, shortTp string) error { + argNames := g.getArgNames(m, true /* in */) + argTypes := g.getArgTypes(m, pkgOverride, true /* in */) + argString := makeArgString(argNames, argTypes) + + rets := make([]string, len(m.Out)) + for i, p := range m.Out { + rets[i] = p.Type.String(g.packageMap, pkgOverride) + } + retString := strings.Join(rets, ", ") + if len(rets) > 1 { + retString = "(" + retString + ")" + } + if retString != "" { + retString = " " + retString + } + + ia := newIdentifierAllocator(argNames) + idRecv := ia.allocateIdentifier("m") + + g.p("// %v mocks base method.", m.Name) + g.p("func (%v *%v%v) %v(%v)%v {", idRecv, mockType, shortTp, m.Name, argString, retString) + g.in() + g.p("%s.ctrl.T.Helper()", idRecv) + + var callArgs string + if m.Variadic == nil { + if len(argNames) > 0 { + callArgs = ", " + strings.Join(argNames, ", ") + } + } else { + // Non-trivial. The generated code must build a []any, + // but the variadic argument may be any type. + idVarArgs := ia.allocateIdentifier("varargs") + idVArg := ia.allocateIdentifier("a") + g.p("%s := []any{%s}", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", ")) + g.p("for _, %s := range %s {", idVArg, argNames[len(argNames)-1]) + g.in() + g.p("%s = append(%s, %s)", idVarArgs, idVarArgs, idVArg) + g.out() + g.p("}") + callArgs = ", " + idVarArgs + "..." + } + if len(m.Out) == 0 { + g.p(`%v.ctrl.Call(%v, %q%v)`, idRecv, idRecv, m.Name, callArgs) + } else { + idRet := ia.allocateIdentifier("ret") + g.p(`%v := %v.ctrl.Call(%v, %q%v)`, idRet, idRecv, idRecv, m.Name, callArgs) + + // Go does not allow "naked" type assertions on nil values, so we use the two-value form here. + // The value of that is either (x.(T), true) or (Z, false), where Z is the zero value for T. + // Happily, this coincides with the semantics we want here. + retNames := make([]string, len(rets)) + for i, t := range rets { + retNames[i] = ia.allocateIdentifier(fmt.Sprintf("ret%d", i)) + g.p("%s, _ := %s[%d].(%s)", retNames[i], idRet, i, t) + } + g.p("return " + strings.Join(retNames, ", ")) + } + + g.out() + g.p("}") + return nil +} + +func (g *generator) GenerateMockRecorderMethod(intf *model.Interface, m *model.Method, shortTp string, typed bool) error { + mockType := g.mockName(intf.Name) + argNames := g.getArgNames(m, true) + + var argString string + if m.Variadic == nil { + argString = strings.Join(argNames, ", ") + } else { + argString = strings.Join(argNames[:len(argNames)-1], ", ") + } + if argString != "" { + argString += " any" + } + + if m.Variadic != nil { + if argString != "" { + argString += ", " + } + argString += fmt.Sprintf("%s ...any", argNames[len(argNames)-1]) + } + + ia := newIdentifierAllocator(argNames) + idRecv := ia.allocateIdentifier("mr") + + g.p("// %v indicates an expected call of %v.", m.Name, m.Name) + if typed { + g.p("func (%s *%vMockRecorder%v) %v(%v) *%s%sCall%s {", idRecv, mockType, shortTp, m.Name, argString, mockType, m.Name, shortTp) + } else { + g.p("func (%s *%vMockRecorder%v) %v(%v) *gomock.Call {", idRecv, mockType, shortTp, m.Name, argString) + } + + g.in() + g.p("%s.mock.ctrl.T.Helper()", idRecv) + + var callArgs string + if m.Variadic == nil { + if len(argNames) > 0 { + callArgs = ", " + strings.Join(argNames, ", ") + } + } else { + if len(argNames) == 1 { + // Easy: just use ... to push the arguments through. + callArgs = ", " + argNames[0] + "..." + } else { + // Hard: create a temporary slice. + idVarArgs := ia.allocateIdentifier("varargs") + g.p("%s := append([]any{%s}, %s...)", + idVarArgs, + strings.Join(argNames[:len(argNames)-1], ", "), + argNames[len(argNames)-1]) + callArgs = ", " + idVarArgs + "..." + } + } + if typed { + g.p(`call := %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs) + g.p(`return &%s%sCall%s{Call: call}`, mockType, m.Name, shortTp) + } else { + g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs) + } + + g.out() + g.p("}") + return nil +} + +func (g *generator) GenerateMockReturnCallMethod(intf *model.Interface, m *model.Method, pkgOverride, longTp, shortTp string) error { + mockType := g.mockName(intf.Name) + argNames := g.getArgNames(m, true /* in */) + retNames := g.getArgNames(m, false /* out */) + argTypes := g.getArgTypes(m, pkgOverride, true /* in */) + retTypes := g.getArgTypes(m, pkgOverride, false /* out */) + argString := strings.Join(argTypes, ", ") + + rets := make([]string, len(m.Out)) + for i, p := range m.Out { + rets[i] = p.Type.String(g.packageMap, pkgOverride) + } + + var retString string + switch { + case len(rets) == 1: + retString = " " + rets[0] + case len(rets) > 1: + retString = " (" + strings.Join(rets, ", ") + ")" + } + + ia := newIdentifierAllocator(argNames) + idRecv := ia.allocateIdentifier("c") + + recvStructName := mockType + m.Name + + g.p("// %s%sCall wrap *gomock.Call", mockType, m.Name) + g.p("type %s%sCall%s struct{", mockType, m.Name, longTp) + g.in() + g.p("*gomock.Call") + g.out() + g.p("}") + + g.p("// Return rewrite *gomock.Call.Return") + g.p("func (%s *%sCall%s) Return(%v) *%sCall%s {", idRecv, recvStructName, shortTp, makeArgString(retNames, retTypes), recvStructName, shortTp) + g.in() + var retArgs string + if len(retNames) > 0 { + retArgs = strings.Join(retNames, ", ") + } + g.p(`%s.Call = %v.Call.Return(%v)`, idRecv, idRecv, retArgs) + g.p("return %s", idRecv) + g.out() + g.p("}") + + g.p("// Do rewrite *gomock.Call.Do") + g.p("func (%s *%sCall%s) Do(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp) + g.in() + g.p(`%s.Call = %v.Call.Do(f)`, idRecv, idRecv) + g.p("return %s", idRecv) + g.out() + g.p("}") + + g.p("// DoAndReturn rewrite *gomock.Call.DoAndReturn") + g.p("func (%s *%sCall%s) DoAndReturn(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp) + g.in() + g.p(`%s.Call = %v.Call.DoAndReturn(f)`, idRecv, idRecv) + g.p("return %s", idRecv) + g.out() + g.p("}") + return nil +} + +func (g *generator) getArgNames(m *model.Method, in bool) []string { + var params []*model.Parameter + if in { + params = m.In + } else { + params = m.Out + } + argNames := make([]string, len(params)) + for i, p := range params { + name := p.Name + if name == "" || name == "_" { + name = fmt.Sprintf("arg%d", i) + } + argNames[i] = name + } + if m.Variadic != nil && in { + name := m.Variadic.Name + if name == "" { + name = fmt.Sprintf("arg%d", len(params)) + } + argNames = append(argNames, name) + } + return argNames +} + +func (g *generator) getArgTypes(m *model.Method, pkgOverride string, in bool) []string { + var params []*model.Parameter + if in { + params = m.In + } else { + params = m.Out + } + argTypes := make([]string, len(params)) + for i, p := range params { + argTypes[i] = p.Type.String(g.packageMap, pkgOverride) + } + if m.Variadic != nil { + argTypes = append(argTypes, "..."+m.Variadic.Type.String(g.packageMap, pkgOverride)) + } + return argTypes +} + +type identifierAllocator map[string]struct{} + +func newIdentifierAllocator(taken []string) identifierAllocator { + a := make(identifierAllocator, len(taken)) + for _, s := range taken { + a[s] = struct{}{} + } + return a +} + +func (o identifierAllocator) allocateIdentifier(want string) string { + id := want + for i := 2; ; i++ { + if _, ok := o[id]; !ok { + o[id] = struct{}{} + return id + } + id = want + "_" + strconv.Itoa(i) + } +} + +// Output returns the generator's output, formatted in the standard Go style. +func (g *generator) Output() []byte { + src, err := toolsimports.Process(g.destination, g.buf.Bytes(), nil) + if err != nil { + log.Fatalf("Failed to format generated source code: %s\n%s", err, g.buf.String()) + } + return src +} + +// createPackageMap returns a map of import path to package name +// for specified importPaths. +func createPackageMap(importPaths []string) map[string]string { + var pkg struct { + Name string + ImportPath string + } + pkgMap := make(map[string]string) + b := bytes.NewBuffer(nil) + args := []string{"list", "-json"} + args = append(args, importPaths...) + cmd := exec.Command("go", args...) + cmd.Stdout = b + cmd.Run() + dec := json.NewDecoder(b) + for dec.More() { + err := dec.Decode(&pkg) + if err != nil { + log.Printf("failed to decode 'go list' output: %v", err) + continue + } + pkgMap[pkg.ImportPath] = pkg.Name + } + return pkgMap +} + +func printVersion() { + if version != "" { + fmt.Printf("v%s\nCommit: %s\nDate: %s\n", version, commit, date) + } else { + printModuleVersion() + } +} + +// parseImportPackage get package import path via source file +// an alternative implementation is to use: +// cfg := &packages.Config{Mode: packages.NeedName, Tests: true, Dir: srcDir} +// pkgs, err := packages.Load(cfg, "file="+source) +// However, it will call "go list" and slow down the performance +func parsePackageImport(srcDir string) (string, error) { + moduleMode := os.Getenv("GO111MODULE") + // trying to find the module + if moduleMode != "off" { + currentDir := srcDir + for { + dat, err := os.ReadFile(filepath.Join(currentDir, "go.mod")) + if os.IsNotExist(err) { + if currentDir == filepath.Dir(currentDir) { + // at the root + break + } + currentDir = filepath.Dir(currentDir) + continue + } else if err != nil { + return "", err + } + modulePath := modfile.ModulePath(dat) + return filepath.ToSlash(filepath.Join(modulePath, strings.TrimPrefix(srcDir, currentDir))), nil + } + } + // fall back to GOPATH mode + goPaths := os.Getenv("GOPATH") + if goPaths == "" { + return "", fmt.Errorf("GOPATH is not set") + } + goPathList := strings.Split(goPaths, string(os.PathListSeparator)) + for _, goPath := range goPathList { + sourceRoot := filepath.Join(goPath, "src") + string(os.PathSeparator) + if strings.HasPrefix(srcDir, sourceRoot) { + return filepath.ToSlash(strings.TrimPrefix(srcDir, sourceRoot)), nil + } + } + return "", errOutsideGoPath +} diff --git a/vendor/go.uber.org/mock/mockgen/model/model.go b/vendor/go.uber.org/mock/mockgen/model/model.go new file mode 100644 index 000000000..853dbf2d6 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/model/model.go @@ -0,0 +1,533 @@ +// Copyright 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package model contains the data model necessary for generating mock implementations. +package model + +import ( + "encoding/gob" + "fmt" + "io" + "reflect" + "strings" +) + +// pkgPath is the importable path for package model +const pkgPath = "go.uber.org/mock/mockgen/model" + +// Package is a Go package. It may be a subset. +type Package struct { + Name string + PkgPath string + Interfaces []*Interface + DotImports []string +} + +// Print writes the package name and its exported interfaces. +func (pkg *Package) Print(w io.Writer) { + _, _ = fmt.Fprintf(w, "package %s\n", pkg.Name) + for _, intf := range pkg.Interfaces { + intf.Print(w) + } +} + +// Imports returns the imports needed by the Package as a set of import paths. +func (pkg *Package) Imports() map[string]bool { + im := make(map[string]bool) + for _, intf := range pkg.Interfaces { + intf.addImports(im) + for _, tp := range intf.TypeParams { + tp.Type.addImports(im) + } + } + return im +} + +// Interface is a Go interface. +type Interface struct { + Name string + Methods []*Method + TypeParams []*Parameter +} + +// Print writes the interface name and its methods. +func (intf *Interface) Print(w io.Writer) { + _, _ = fmt.Fprintf(w, "interface %s\n", intf.Name) + for _, m := range intf.Methods { + m.Print(w) + } +} + +func (intf *Interface) addImports(im map[string]bool) { + for _, m := range intf.Methods { + m.addImports(im) + } +} + +// AddMethod adds a new method, de-duplicating by method name. +func (intf *Interface) AddMethod(m *Method) { + for _, me := range intf.Methods { + if me.Name == m.Name { + return + } + } + intf.Methods = append(intf.Methods, m) +} + +// Method is a single method of an interface. +type Method struct { + Name string + In, Out []*Parameter + Variadic *Parameter // may be nil +} + +// Print writes the method name and its signature. +func (m *Method) Print(w io.Writer) { + _, _ = fmt.Fprintf(w, " - method %s\n", m.Name) + if len(m.In) > 0 { + _, _ = fmt.Fprintf(w, " in:\n") + for _, p := range m.In { + p.Print(w) + } + } + if m.Variadic != nil { + _, _ = fmt.Fprintf(w, " ...:\n") + m.Variadic.Print(w) + } + if len(m.Out) > 0 { + _, _ = fmt.Fprintf(w, " out:\n") + for _, p := range m.Out { + p.Print(w) + } + } +} + +func (m *Method) addImports(im map[string]bool) { + for _, p := range m.In { + p.Type.addImports(im) + } + if m.Variadic != nil { + m.Variadic.Type.addImports(im) + } + for _, p := range m.Out { + p.Type.addImports(im) + } +} + +// Parameter is an argument or return parameter of a method. +type Parameter struct { + Name string // may be empty + Type Type +} + +// Print writes a method parameter. +func (p *Parameter) Print(w io.Writer) { + n := p.Name + if n == "" { + n = `""` + } + _, _ = fmt.Fprintf(w, " - %v: %v\n", n, p.Type.String(nil, "")) +} + +// Type is a Go type. +type Type interface { + String(pm map[string]string, pkgOverride string) string + addImports(im map[string]bool) +} + +func init() { + // Call gob.RegisterName with pkgPath as prefix to avoid conflicting with + // github.com/golang/mock/mockgen/model 's registration. + gob.RegisterName(pkgPath+".ArrayType", &ArrayType{}) + gob.RegisterName(pkgPath+".ChanType", &ChanType{}) + gob.RegisterName(pkgPath+".FuncType", &FuncType{}) + gob.RegisterName(pkgPath+".MapType", &MapType{}) + gob.RegisterName(pkgPath+".NamedType", &NamedType{}) + gob.RegisterName(pkgPath+".PointerType", &PointerType{}) + + // Call gob.RegisterName to make sure it has the consistent name registered + // for both gob decoder and encoder. + // + // For a non-pointer type, gob.Register will try to get package full path by + // calling rt.PkgPath() for a name to register. If your project has vendor + // directory, it is possible that PkgPath will get a path like this: + // ../../../vendor/go.uber.org/mock/mockgen/model + gob.RegisterName(pkgPath+".PredeclaredType", PredeclaredType("")) +} + +// ArrayType is an array or slice type. +type ArrayType struct { + Len int // -1 for slices, >= 0 for arrays + Type Type +} + +func (at *ArrayType) String(pm map[string]string, pkgOverride string) string { + s := "[]" + if at.Len > -1 { + s = fmt.Sprintf("[%d]", at.Len) + } + return s + at.Type.String(pm, pkgOverride) +} + +func (at *ArrayType) addImports(im map[string]bool) { at.Type.addImports(im) } + +// ChanType is a channel type. +type ChanType struct { + Dir ChanDir // 0, 1 or 2 + Type Type +} + +func (ct *ChanType) String(pm map[string]string, pkgOverride string) string { + s := ct.Type.String(pm, pkgOverride) + if ct.Dir == RecvDir { + return "<-chan " + s + } + if ct.Dir == SendDir { + return "chan<- " + s + } + return "chan " + s +} + +func (ct *ChanType) addImports(im map[string]bool) { ct.Type.addImports(im) } + +// ChanDir is a channel direction. +type ChanDir int + +// Constants for channel directions. +const ( + RecvDir ChanDir = 1 + SendDir ChanDir = 2 +) + +// FuncType is a function type. +type FuncType struct { + In, Out []*Parameter + Variadic *Parameter // may be nil +} + +func (ft *FuncType) String(pm map[string]string, pkgOverride string) string { + args := make([]string, len(ft.In)) + for i, p := range ft.In { + args[i] = p.Type.String(pm, pkgOverride) + } + if ft.Variadic != nil { + args = append(args, "..."+ft.Variadic.Type.String(pm, pkgOverride)) + } + rets := make([]string, len(ft.Out)) + for i, p := range ft.Out { + rets[i] = p.Type.String(pm, pkgOverride) + } + retString := strings.Join(rets, ", ") + if nOut := len(ft.Out); nOut == 1 { + retString = " " + retString + } else if nOut > 1 { + retString = " (" + retString + ")" + } + return "func(" + strings.Join(args, ", ") + ")" + retString +} + +func (ft *FuncType) addImports(im map[string]bool) { + for _, p := range ft.In { + p.Type.addImports(im) + } + if ft.Variadic != nil { + ft.Variadic.Type.addImports(im) + } + for _, p := range ft.Out { + p.Type.addImports(im) + } +} + +// MapType is a map type. +type MapType struct { + Key, Value Type +} + +func (mt *MapType) String(pm map[string]string, pkgOverride string) string { + return "map[" + mt.Key.String(pm, pkgOverride) + "]" + mt.Value.String(pm, pkgOverride) +} + +func (mt *MapType) addImports(im map[string]bool) { + mt.Key.addImports(im) + mt.Value.addImports(im) +} + +// NamedType is an exported type in a package. +type NamedType struct { + Package string // may be empty + Type string + TypeParams *TypeParametersType +} + +func (nt *NamedType) String(pm map[string]string, pkgOverride string) string { + if pkgOverride == nt.Package { + return nt.Type + nt.TypeParams.String(pm, pkgOverride) + } + prefix := pm[nt.Package] + if prefix != "" { + return prefix + "." + nt.Type + nt.TypeParams.String(pm, pkgOverride) + } + + return nt.Type + nt.TypeParams.String(pm, pkgOverride) +} + +func (nt *NamedType) addImports(im map[string]bool) { + if nt.Package != "" { + im[nt.Package] = true + } + nt.TypeParams.addImports(im) +} + +// PointerType is a pointer to another type. +type PointerType struct { + Type Type +} + +func (pt *PointerType) String(pm map[string]string, pkgOverride string) string { + return "*" + pt.Type.String(pm, pkgOverride) +} +func (pt *PointerType) addImports(im map[string]bool) { pt.Type.addImports(im) } + +// PredeclaredType is a predeclared type such as "int". +type PredeclaredType string + +func (pt PredeclaredType) String(map[string]string, string) string { return string(pt) } +func (pt PredeclaredType) addImports(map[string]bool) {} + +// TypeParametersType contains type parameters for a NamedType. +type TypeParametersType struct { + TypeParameters []Type +} + +func (tp *TypeParametersType) String(pm map[string]string, pkgOverride string) string { + if tp == nil || len(tp.TypeParameters) == 0 { + return "" + } + var sb strings.Builder + sb.WriteString("[") + for i, v := range tp.TypeParameters { + if i != 0 { + sb.WriteString(", ") + } + sb.WriteString(v.String(pm, pkgOverride)) + } + sb.WriteString("]") + return sb.String() +} + +func (tp *TypeParametersType) addImports(im map[string]bool) { + if tp == nil { + return + } + for _, v := range tp.TypeParameters { + v.addImports(im) + } +} + +// The following code is intended to be called by the program generated by ../reflect.go. + +// InterfaceFromInterfaceType returns a pointer to an interface for the +// given reflection interface type. +func InterfaceFromInterfaceType(it reflect.Type) (*Interface, error) { + if it.Kind() != reflect.Interface { + return nil, fmt.Errorf("%v is not an interface", it) + } + intf := &Interface{} + + for i := 0; i < it.NumMethod(); i++ { + mt := it.Method(i) + // TODO: need to skip unexported methods? or just raise an error? + m := &Method{ + Name: mt.Name, + } + + var err error + m.In, m.Variadic, m.Out, err = funcArgsFromType(mt.Type) + if err != nil { + return nil, err + } + + intf.AddMethod(m) + } + + return intf, nil +} + +// t's Kind must be a reflect.Func. +func funcArgsFromType(t reflect.Type) (in []*Parameter, variadic *Parameter, out []*Parameter, err error) { + nin := t.NumIn() + if t.IsVariadic() { + nin-- + } + var p *Parameter + for i := 0; i < nin; i++ { + p, err = parameterFromType(t.In(i)) + if err != nil { + return + } + in = append(in, p) + } + if t.IsVariadic() { + p, err = parameterFromType(t.In(nin).Elem()) + if err != nil { + return + } + variadic = p + } + for i := 0; i < t.NumOut(); i++ { + p, err = parameterFromType(t.Out(i)) + if err != nil { + return + } + out = append(out, p) + } + return +} + +func parameterFromType(t reflect.Type) (*Parameter, error) { + tt, err := typeFromType(t) + if err != nil { + return nil, err + } + return &Parameter{Type: tt}, nil +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +var byteType = reflect.TypeOf(byte(0)) + +func typeFromType(t reflect.Type) (Type, error) { + // Hack workaround for https://golang.org/issue/3853. + // This explicit check should not be necessary. + if t == byteType { + return PredeclaredType("byte"), nil + } + + if imp := t.PkgPath(); imp != "" { + return &NamedType{ + Package: impPath(imp), + Type: t.Name(), + }, nil + } + + // only unnamed or predeclared types after here + + // Lots of types have element types. Let's do the parsing and error checking for all of them. + var elemType Type + switch t.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice: + var err error + elemType, err = typeFromType(t.Elem()) + if err != nil { + return nil, err + } + } + + switch t.Kind() { + case reflect.Array: + return &ArrayType{ + Len: t.Len(), + Type: elemType, + }, nil + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String: + return PredeclaredType(t.Kind().String()), nil + case reflect.Chan: + var dir ChanDir + switch t.ChanDir() { + case reflect.RecvDir: + dir = RecvDir + case reflect.SendDir: + dir = SendDir + } + return &ChanType{ + Dir: dir, + Type: elemType, + }, nil + case reflect.Func: + in, variadic, out, err := funcArgsFromType(t) + if err != nil { + return nil, err + } + return &FuncType{ + In: in, + Out: out, + Variadic: variadic, + }, nil + case reflect.Interface: + // Two special interfaces. + if t.NumMethod() == 0 { + return PredeclaredType("any"), nil + } + if t == errorType { + return PredeclaredType("error"), nil + } + case reflect.Map: + kt, err := typeFromType(t.Key()) + if err != nil { + return nil, err + } + return &MapType{ + Key: kt, + Value: elemType, + }, nil + case reflect.Ptr: + return &PointerType{ + Type: elemType, + }, nil + case reflect.Slice: + return &ArrayType{ + Len: -1, + Type: elemType, + }, nil + case reflect.Struct: + if t.NumField() == 0 { + return PredeclaredType("struct{}"), nil + } + } + + // TODO: Struct, UnsafePointer + return nil, fmt.Errorf("can't yet turn %v (%v) into a model.Type", t, t.Kind()) +} + +// impPath sanitizes the package path returned by `PkgPath` method of a reflect Type so that +// it is importable. PkgPath might return a path that includes "vendor". These paths do not +// compile, so we need to remove everything up to and including "/vendor/". +// See https://github.com/golang/go/issues/12019. +func impPath(imp string) string { + if strings.HasPrefix(imp, "vendor/") { + imp = "/" + imp + } + if i := strings.LastIndex(imp, "/vendor/"); i != -1 { + imp = imp[i+len("/vendor/"):] + } + return imp +} + +// ErrorInterface represent built-in error interface. +var ErrorInterface = Interface{ + Name: "error", + Methods: []*Method{ + { + Name: "Error", + Out: []*Parameter{ + { + Name: "", + Type: PredeclaredType("string"), + }, + }, + }, + }, +} diff --git a/vendor/go.uber.org/mock/mockgen/parse.go b/vendor/go.uber.org/mock/mockgen/parse.go new file mode 100644 index 000000000..f43321c33 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/parse.go @@ -0,0 +1,805 @@ +// Copyright 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +// This file contains the model construction by parsing source files. + +import ( + "errors" + "fmt" + "go/ast" + "go/build" + "go/importer" + "go/parser" + "go/token" + "go/types" + "log" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + "go.uber.org/mock/mockgen/model" +) + +// sourceMode generates mocks via source file. +func sourceMode(source string) (*model.Package, error) { + srcDir, err := filepath.Abs(filepath.Dir(source)) + if err != nil { + return nil, fmt.Errorf("failed getting source directory: %v", err) + } + + packageImport, err := parsePackageImport(srcDir) + if err != nil { + return nil, err + } + + fs := token.NewFileSet() + file, err := parser.ParseFile(fs, source, nil, 0) + if err != nil { + return nil, fmt.Errorf("failed parsing source file %v: %v", source, err) + } + + p := &fileParser{ + fileSet: fs, + imports: make(map[string]importedPackage), + importedInterfaces: newInterfaceCache(), + auxInterfaces: newInterfaceCache(), + srcDir: srcDir, + } + + // Handle -imports. + dotImports := make(map[string]bool) + if *imports != "" { + for _, kv := range strings.Split(*imports, ",") { + eq := strings.Index(kv, "=") + k, v := kv[:eq], kv[eq+1:] + if k == "." { + dotImports[v] = true + } else { + p.imports[k] = importedPkg{path: v} + } + } + } + + if *excludeInterfaces != "" { + p.excludeNamesSet = parseExcludeInterfaces(*excludeInterfaces) + } + + // Handle -aux_files. + if err := p.parseAuxFiles(*auxFiles); err != nil { + return nil, err + } + p.addAuxInterfacesFromFile(packageImport, file) // this file + + pkg, err := p.parseFile(packageImport, file) + if err != nil { + return nil, err + } + for pkgPath := range dotImports { + pkg.DotImports = append(pkg.DotImports, pkgPath) + } + return pkg, nil +} + +type importedPackage interface { + Path() string + Parser() *fileParser +} + +type importedPkg struct { + path string + parser *fileParser +} + +func (i importedPkg) Path() string { return i.path } +func (i importedPkg) Parser() *fileParser { return i.parser } + +// duplicateImport is a bit of a misnomer. Currently the parser can't +// handle cases of multi-file packages importing different packages +// under the same name. Often these imports would not be problematic, +// so this type lets us defer raising an error unless the package name +// is actually used. +type duplicateImport struct { + name string + duplicates []string +} + +func (d duplicateImport) Error() string { + return fmt.Sprintf("%q is ambiguous because of duplicate imports: %v", d.name, d.duplicates) +} + +func (d duplicateImport) Path() string { log.Fatal(d.Error()); return "" } +func (d duplicateImport) Parser() *fileParser { log.Fatal(d.Error()); return nil } + +type interfaceCache struct { + m map[string]map[string]*namedInterface +} + +func newInterfaceCache() *interfaceCache { + return &interfaceCache{ + m: make(map[string]map[string]*namedInterface), + } +} + +func (i *interfaceCache) Set(pkg, name string, it *namedInterface) { + if _, ok := i.m[pkg]; !ok { + i.m[pkg] = make(map[string]*namedInterface) + } + i.m[pkg][name] = it +} + +func (i *interfaceCache) Get(pkg, name string) *namedInterface { + if _, ok := i.m[pkg]; !ok { + return nil + } + return i.m[pkg][name] +} + +func (i *interfaceCache) GetASTIface(pkg, name string) *ast.InterfaceType { + if _, ok := i.m[pkg]; !ok { + return nil + } + it, ok := i.m[pkg][name] + if !ok { + return nil + } + return it.it +} + +type fileParser struct { + fileSet *token.FileSet + imports map[string]importedPackage // package name => imported package + importedInterfaces *interfaceCache + auxFiles []*ast.File + auxInterfaces *interfaceCache + srcDir string + excludeNamesSet map[string]struct{} +} + +func (p *fileParser) errorf(pos token.Pos, format string, args ...any) error { + ps := p.fileSet.Position(pos) + format = "%s:%d:%d: " + format + args = append([]any{ps.Filename, ps.Line, ps.Column}, args...) + return fmt.Errorf(format, args...) +} + +func (p *fileParser) parseAuxFiles(auxFiles string) error { + auxFiles = strings.TrimSpace(auxFiles) + if auxFiles == "" { + return nil + } + for _, kv := range strings.Split(auxFiles, ",") { + parts := strings.SplitN(kv, "=", 2) + if len(parts) != 2 { + return fmt.Errorf("bad aux file spec: %v", kv) + } + pkg, fpath := parts[0], parts[1] + + file, err := parser.ParseFile(p.fileSet, fpath, nil, 0) + if err != nil { + return err + } + p.auxFiles = append(p.auxFiles, file) + p.addAuxInterfacesFromFile(pkg, file) + } + return nil +} + +func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) { + for ni := range iterInterfaces(file) { + p.auxInterfaces.Set(pkg, ni.name.Name, ni) + } +} + +// parseFile loads all file imports and auxiliary files import into the +// fileParser, parses all file interfaces and returns package model. +func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Package, error) { + allImports, dotImports := importsOfFile(file) + // Don't stomp imports provided by -imports. Those should take precedence. + for pkg, pkgI := range allImports { + if _, ok := p.imports[pkg]; !ok { + p.imports[pkg] = pkgI + } + } + // Add imports from auxiliary files, which might be needed for embedded interfaces. + // Don't stomp any other imports. + for _, f := range p.auxFiles { + auxImports, _ := importsOfFile(f) + for pkg, pkgI := range auxImports { + if _, ok := p.imports[pkg]; !ok { + p.imports[pkg] = pkgI + } + } + } + + var is []*model.Interface + for ni := range iterInterfaces(file) { + if _, ok := p.excludeNamesSet[ni.name.String()]; ok { + continue + } + i, err := p.parseInterface(ni.name.String(), importPath, ni) + if errors.Is(err, errConstraintInterface) { + continue + } + if err != nil { + return nil, err + } + is = append(is, i) + } + return &model.Package{ + Name: file.Name.String(), + PkgPath: importPath, + Interfaces: is, + DotImports: dotImports, + }, nil +} + +// parsePackage loads package specified by path, parses it and returns +// a new fileParser with the parsed imports and interfaces. +func (p *fileParser) parsePackage(path string) (*fileParser, error) { + newP := &fileParser{ + fileSet: token.NewFileSet(), + imports: make(map[string]importedPackage), + importedInterfaces: newInterfaceCache(), + auxInterfaces: newInterfaceCache(), + srcDir: p.srcDir, + } + + var pkgs map[string]*ast.Package + if imp, err := build.Import(path, newP.srcDir, build.FindOnly); err != nil { + return nil, err + } else if pkgs, err = parser.ParseDir(newP.fileSet, imp.Dir, nil, 0); err != nil { + return nil, err + } + + for _, pkg := range pkgs { + file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates) + for ni := range iterInterfaces(file) { + newP.importedInterfaces.Set(path, ni.name.Name, ni) + } + imports, _ := importsOfFile(file) + for pkgName, pkgI := range imports { + newP.imports[pkgName] = pkgI + } + } + return newP, nil +} + +func (p *fileParser) constructInstParams(pkg string, params []*ast.Field, instParams []model.Type, embeddedInstParams []ast.Expr, tps map[string]model.Type) ([]model.Type, error) { + pm := make(map[string]int) + var i int + for _, v := range params { + for _, n := range v.Names { + pm[n.Name] = i + instParams = append(instParams, model.PredeclaredType(n.Name)) + i++ + } + } + + var runtimeInstParams []model.Type + for _, instParam := range embeddedInstParams { + switch t := instParam.(type) { + case *ast.Ident: + if idx, ok := pm[t.Name]; ok { + runtimeInstParams = append(runtimeInstParams, instParams[idx]) + continue + } + } + modelType, err := p.parseType(pkg, instParam, tps) + if err != nil { + return nil, err + } + runtimeInstParams = append(runtimeInstParams, modelType) + } + + return runtimeInstParams, nil +} + +func (p *fileParser) constructTps(it *namedInterface) (tps map[string]model.Type) { + tps = make(map[string]model.Type) + n := 0 + for _, tp := range it.typeParams { + for _, tm := range tp.Names { + tps[tm.Name] = nil + if len(it.instTypes) != 0 { + tps[tm.Name] = it.instTypes[n] + n++ + } + } + } + return tps +} + +// parseInterface loads interface specified by pkg and name, parses it and returns +// a new model with the parsed. +func (p *fileParser) parseInterface(name, pkg string, it *namedInterface) (*model.Interface, error) { + iface := &model.Interface{Name: name} + tps := p.constructTps(it) + tp, err := p.parseFieldList(pkg, it.typeParams, tps) + if err != nil { + return nil, fmt.Errorf("unable to parse interface type parameters: %v", name) + } + + iface.TypeParams = tp + for _, field := range it.it.Methods.List { + var methods []*model.Method + if methods, err = p.parseMethod(field, it, iface, pkg, tps); err != nil { + return nil, err + } + for _, m := range methods { + iface.AddMethod(m) + } + } + return iface, nil +} + +func (p *fileParser) parseMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + // {} for git diff + { + switch v := field.Type.(type) { + case *ast.FuncType: + if nn := len(field.Names); nn != 1 { + return nil, fmt.Errorf("expected one name for interface %v, got %d", iface.Name, nn) + } + m := &model.Method{ + Name: field.Names[0].String(), + } + var err error + m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v, tps) + if err != nil { + return nil, err + } + return []*model.Method{m}, nil + case *ast.Ident: + // Embedded interface in this package. + embeddedIfaceType := p.auxInterfaces.Get(pkg, v.String()) + if embeddedIfaceType == nil { + embeddedIfaceType = p.importedInterfaces.Get(pkg, v.String()) + } + + var embeddedIface *model.Interface + if embeddedIfaceType != nil { + var err error + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } + embeddedIface, err = p.parseInterface(v.String(), pkg, embeddedIfaceType) + if err != nil { + return nil, err + } + + } else { + // This is built-in error interface. + if v.String() == model.ErrorInterface.Name { + embeddedIface = &model.ErrorInterface + } else { + ip, err := p.parsePackage(pkg) + if err != nil { + return nil, p.errorf(v.Pos(), "could not parse package %s: %v", pkg, err) + } + + if embeddedIfaceType = ip.importedInterfaces.Get(pkg, v.String()); embeddedIfaceType == nil { + return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", pkg, v.String()) + } + + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } + embeddedIface, err = ip.parseInterface(v.String(), pkg, embeddedIfaceType) + if err != nil { + return nil, err + } + } + } + return embeddedIface.Methods, nil + case *ast.SelectorExpr: + // Embedded interface in another package. + filePkg, sel := v.X.(*ast.Ident).String(), v.Sel.String() + embeddedPkg, ok := p.imports[filePkg] + if !ok { + return nil, p.errorf(v.X.Pos(), "unknown package %s", filePkg) + } + + var embeddedIface *model.Interface + var err error + embeddedIfaceType := p.auxInterfaces.Get(filePkg, sel) + if embeddedIfaceType != nil { + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } + embeddedIface, err = p.parseInterface(sel, filePkg, embeddedIfaceType) + if err != nil { + return nil, err + } + } else { + path := embeddedPkg.Path() + parser := embeddedPkg.Parser() + if parser == nil { + ip, err := p.parsePackage(path) + if err != nil { + return nil, p.errorf(v.Pos(), "could not parse package %s: %v", path, err) + } + parser = ip + p.imports[filePkg] = importedPkg{ + path: embeddedPkg.Path(), + parser: parser, + } + } + if embeddedIfaceType = parser.importedInterfaces.Get(path, sel); embeddedIfaceType == nil { + return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel) + } + + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } + embeddedIface, err = parser.parseInterface(sel, path, embeddedIfaceType) + if err != nil { + return nil, err + } + } + // TODO: apply shadowing rules. + return embeddedIface.Methods, nil + default: + return p.parseGenericMethod(field, it, iface, pkg, tps) + } + } +} + +func (p *fileParser) parseFunc(pkg string, f *ast.FuncType, tps map[string]model.Type) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) { + if f.Params != nil { + regParams := f.Params.List + if isVariadic(f) { + n := len(regParams) + varParams := regParams[n-1:] + regParams = regParams[:n-1] + vp, err := p.parseFieldList(pkg, varParams, tps) + if err != nil { + return nil, nil, nil, p.errorf(varParams[0].Pos(), "failed parsing variadic argument: %v", err) + } + variadic = vp[0] + } + inParam, err = p.parseFieldList(pkg, regParams, tps) + if err != nil { + return nil, nil, nil, p.errorf(f.Pos(), "failed parsing arguments: %v", err) + } + } + if f.Results != nil { + outParam, err = p.parseFieldList(pkg, f.Results.List, tps) + if err != nil { + return nil, nil, nil, p.errorf(f.Pos(), "failed parsing returns: %v", err) + } + } + return +} + +func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field, tps map[string]model.Type) ([]*model.Parameter, error) { + nf := 0 + for _, f := range fields { + nn := len(f.Names) + if nn == 0 { + nn = 1 // anonymous parameter + } + nf += nn + } + if nf == 0 { + return nil, nil + } + ps := make([]*model.Parameter, nf) + i := 0 // destination index + for _, f := range fields { + t, err := p.parseType(pkg, f.Type, tps) + if err != nil { + return nil, err + } + + if len(f.Names) == 0 { + // anonymous arg + ps[i] = &model.Parameter{Type: t} + i++ + continue + } + for _, name := range f.Names { + ps[i] = &model.Parameter{Name: name.Name, Type: t} + i++ + } + } + return ps, nil +} + +func (p *fileParser) parseType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { + switch v := typ.(type) { + case *ast.ArrayType: + ln := -1 + if v.Len != nil { + value, err := p.parseArrayLength(v.Len) + if err != nil { + return nil, err + } + ln, err = strconv.Atoi(value) + if err != nil { + return nil, p.errorf(v.Len.Pos(), "bad array size: %v", err) + } + } + t, err := p.parseType(pkg, v.Elt, tps) + if err != nil { + return nil, err + } + return &model.ArrayType{Len: ln, Type: t}, nil + case *ast.ChanType: + t, err := p.parseType(pkg, v.Value, tps) + if err != nil { + return nil, err + } + var dir model.ChanDir + if v.Dir == ast.SEND { + dir = model.SendDir + } + if v.Dir == ast.RECV { + dir = model.RecvDir + } + return &model.ChanType{Dir: dir, Type: t}, nil + case *ast.Ellipsis: + // assume we're parsing a variadic argument + return p.parseType(pkg, v.Elt, tps) + case *ast.FuncType: + in, variadic, out, err := p.parseFunc(pkg, v, tps) + if err != nil { + return nil, err + } + return &model.FuncType{In: in, Out: out, Variadic: variadic}, nil + case *ast.Ident: + it, ok := tps[v.Name] + if v.IsExported() && !ok { + // `pkg` may be an aliased imported pkg + // if so, patch the import w/ the fully qualified import + maybeImportedPkg, ok := p.imports[pkg] + if ok { + pkg = maybeImportedPkg.Path() + } + // assume type in this package + return &model.NamedType{Package: pkg, Type: v.Name}, nil + } + if ok && it != nil { + return it, nil + } + // assume predeclared type + return model.PredeclaredType(v.Name), nil + case *ast.InterfaceType: + if v.Methods != nil && len(v.Methods.List) > 0 { + return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed interface types") + } + return model.PredeclaredType("any"), nil + case *ast.MapType: + key, err := p.parseType(pkg, v.Key, tps) + if err != nil { + return nil, err + } + value, err := p.parseType(pkg, v.Value, tps) + if err != nil { + return nil, err + } + return &model.MapType{Key: key, Value: value}, nil + case *ast.SelectorExpr: + pkgName := v.X.(*ast.Ident).String() + pkg, ok := p.imports[pkgName] + if !ok { + return nil, p.errorf(v.Pos(), "unknown package %q", pkgName) + } + return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil + case *ast.StarExpr: + t, err := p.parseType(pkg, v.X, tps) + if err != nil { + return nil, err + } + return &model.PointerType{Type: t}, nil + case *ast.StructType: + if v.Fields != nil && len(v.Fields.List) > 0 { + return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed struct types") + } + return model.PredeclaredType("struct{}"), nil + case *ast.ParenExpr: + return p.parseType(pkg, v.X, tps) + default: + mt, err := p.parseGenericType(pkg, typ, tps) + if err != nil { + return nil, err + } + if mt == nil { + break + } + return mt, nil + } + + return nil, fmt.Errorf("don't know how to parse type %T", typ) +} + +func (p *fileParser) parseArrayLength(expr ast.Expr) (string, error) { + switch val := expr.(type) { + case (*ast.BasicLit): + return val.Value, nil + case (*ast.Ident): + // when the length is a const defined locally + return val.Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value, nil + case (*ast.SelectorExpr): + // when the length is a const defined in an external package + usedPkg, err := importer.Default().Import(fmt.Sprintf("%s", val.X)) + if err != nil { + return "", p.errorf(expr.Pos(), "unknown package in array length: %v", err) + } + ev, err := types.Eval(token.NewFileSet(), usedPkg, token.NoPos, val.Sel.Name) + if err != nil { + return "", p.errorf(expr.Pos(), "unknown constant in array length: %v", err) + } + return ev.Value.String(), nil + case (*ast.ParenExpr): + return p.parseArrayLength(val.X) + case (*ast.BinaryExpr): + x, err := p.parseArrayLength(val.X) + if err != nil { + return "", err + } + y, err := p.parseArrayLength(val.Y) + if err != nil { + return "", err + } + biExpr := fmt.Sprintf("%s%v%s", x, val.Op, y) + tv, err := types.Eval(token.NewFileSet(), nil, token.NoPos, biExpr) + if err != nil { + return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", err) + } + return tv.Value.String(), nil + default: + return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", val) + } +} + +// importsOfFile returns a map of package name to import path +// of the imports in file. +func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) { + var importPaths []string + for _, is := range file.Imports { + if is.Name != nil { + continue + } + importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes + importPaths = append(importPaths, importPath) + } + packagesName := createPackageMap(importPaths) + normalImports = make(map[string]importedPackage) + dotImports = make([]string, 0) + for _, is := range file.Imports { + var pkgName string + importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes + + if is.Name != nil { + // Named imports are always certain. + if is.Name.Name == "_" { + continue + } + pkgName = is.Name.Name + } else { + pkg, ok := packagesName[importPath] + if !ok { + // Fallback to import path suffix. Note that this is uncertain. + _, last := path.Split(importPath) + // If the last path component has dots, the first dot-delimited + // field is used as the name. + pkgName = strings.SplitN(last, ".", 2)[0] + } else { + pkgName = pkg + } + } + + if pkgName == "." { + dotImports = append(dotImports, importPath) + } else { + if pkg, ok := normalImports[pkgName]; ok { + switch p := pkg.(type) { + case duplicateImport: + normalImports[pkgName] = duplicateImport{ + name: p.name, + duplicates: append([]string{importPath}, p.duplicates...), + } + case importedPkg: + normalImports[pkgName] = duplicateImport{ + name: pkgName, + duplicates: []string{p.path, importPath}, + } + } + } else { + normalImports[pkgName] = importedPkg{path: importPath} + } + } + } + return +} + +type namedInterface struct { + name *ast.Ident + it *ast.InterfaceType + typeParams []*ast.Field + embeddedInstTypeParams []ast.Expr + instTypes []model.Type +} + +// Create an iterator over all interfaces in file. +func iterInterfaces(file *ast.File) <-chan *namedInterface { + ch := make(chan *namedInterface) + go func() { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + it, ok := ts.Type.(*ast.InterfaceType) + if !ok { + continue + } + + ch <- &namedInterface{name: ts.Name, it: it, typeParams: getTypeSpecTypeParams(ts)} + } + } + close(ch) + }() + return ch +} + +// isVariadic returns whether the function is variadic. +func isVariadic(f *ast.FuncType) bool { + nargs := len(f.Params.List) + if nargs == 0 { + return false + } + _, ok := f.Params.List[nargs-1].Type.(*ast.Ellipsis) + return ok +} + +// packageNameOfDir get package import path via dir +func packageNameOfDir(srcDir string) (string, error) { + files, err := os.ReadDir(srcDir) + if err != nil { + log.Fatal(err) + } + + var goFilePath string + for _, file := range files { + if !file.IsDir() && strings.HasSuffix(file.Name(), ".go") { + goFilePath = file.Name() + break + } + } + if goFilePath == "" { + return "", fmt.Errorf("go source file not found %s", srcDir) + } + + packageImport, err := parsePackageImport(srcDir) + if err != nil { + return "", err + } + return packageImport, nil +} + +var errOutsideGoPath = errors.New("source directory is outside GOPATH") diff --git a/vendor/go.uber.org/mock/mockgen/reflect.go b/vendor/go.uber.org/mock/mockgen/reflect.go new file mode 100644 index 000000000..ca80ebbb6 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/reflect.go @@ -0,0 +1,256 @@ +// Copyright 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +// This file contains the model construction by reflection. + +import ( + "bytes" + "encoding/gob" + "flag" + "fmt" + "go/build" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "text/template" + + "go.uber.org/mock/mockgen/model" +) + +var ( + progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.") + execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.") + buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.") +) + +// reflectMode generates mocks via reflection on an interface. +func reflectMode(importPath string, symbols []string) (*model.Package, error) { + if *execOnly != "" { + return run(*execOnly) + } + + program, err := writeProgram(importPath, symbols) + if err != nil { + return nil, err + } + + if *progOnly { + if _, err := os.Stdout.Write(program); err != nil { + return nil, err + } + os.Exit(0) + } + + wd, _ := os.Getwd() + + // Try to run the reflection program in the current working directory. + if p, err := runInDir(program, wd); err == nil { + return p, nil + } + + // Try to run the program in the same directory as the input package. + if p, err := build.Import(importPath, wd, build.FindOnly); err == nil { + dir := p.Dir + if p, err := runInDir(program, dir); err == nil { + return p, nil + } + } + + // Try to run it in a standard temp directory. + return runInDir(program, "") +} + +func writeProgram(importPath string, symbols []string) ([]byte, error) { + var program bytes.Buffer + data := reflectData{ + ImportPath: importPath, + Symbols: symbols, + } + if err := reflectProgram.Execute(&program, &data); err != nil { + return nil, err + } + return program.Bytes(), nil +} + +// run the given program and parse the output as a model.Package. +func run(program string) (*model.Package, error) { + f, err := os.CreateTemp("", "") + if err != nil { + return nil, err + } + + filename := f.Name() + defer os.Remove(filename) + if err := f.Close(); err != nil { + return nil, err + } + + // Run the program. + cmd := exec.Command(program, "-output", filename) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return nil, err + } + + f, err = os.Open(filename) + if err != nil { + return nil, err + } + + // Process output. + var pkg model.Package + if err := gob.NewDecoder(f).Decode(&pkg); err != nil { + return nil, err + } + + if err := f.Close(); err != nil { + return nil, err + } + + return &pkg, nil +} + +// runInDir writes the given program into the given dir, runs it there, and +// parses the output as a model.Package. +func runInDir(program []byte, dir string) (*model.Package, error) { + // We use TempDir instead of TempFile so we can control the filename. + tmpDir, err := os.MkdirTemp(dir, "gomock_reflect_") + if err != nil { + return nil, err + } + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + log.Printf("failed to remove temp directory: %s", err) + } + }() + const progSource = "prog.go" + var progBinary = "prog.bin" + if runtime.GOOS == "windows" { + // Windows won't execute a program unless it has a ".exe" suffix. + progBinary += ".exe" + } + + if err := os.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil { + return nil, err + } + + cmdArgs := []string{} + cmdArgs = append(cmdArgs, "build") + if *buildFlags != "" { + cmdArgs = append(cmdArgs, strings.Split(*buildFlags, " ")...) + } + cmdArgs = append(cmdArgs, "-o", progBinary, progSource) + + // Build the program. + buf := bytes.NewBuffer(nil) + cmd := exec.Command("go", cmdArgs...) + cmd.Dir = tmpDir + cmd.Stdout = os.Stdout + cmd.Stderr = io.MultiWriter(os.Stderr, buf) + if err := cmd.Run(); err != nil { + sErr := buf.String() + if strings.Contains(sErr, `cannot find package "."`) && + strings.Contains(sErr, "go.uber.org/mock/mockgen/model") { + fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://go.uber.org/mock#reflect-vendoring-error.\n") + return nil, err + } + return nil, err + } + + return run(filepath.Join(tmpDir, progBinary)) +} + +type reflectData struct { + ImportPath string + Symbols []string +} + +// This program reflects on an interface value, and prints the +// gob encoding of a model.Package to standard output. +// JSON doesn't work because of the model.Type interface. +var reflectProgram = template.Must(template.New("program").Parse(` +// Code generated by MockGen. DO NOT EDIT. +package main + +import ( + "encoding/gob" + "flag" + "fmt" + "os" + "path" + "reflect" + + "go.uber.org/mock/mockgen/model" + + pkg_ {{printf "%q" .ImportPath}} +) + +var output = flag.String("output", "", "The output file name, or empty to use stdout.") + +func main() { + flag.Parse() + + its := []struct{ + sym string + typ reflect.Type + }{ + {{range .Symbols}} + { {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()}, + {{end}} + } + pkg := &model.Package{ + // NOTE: This behaves contrary to documented behaviour if the + // package name is not the final component of the import path. + // The reflect package doesn't expose the package name, though. + Name: path.Base({{printf "%q" .ImportPath}}), + } + + for _, it := range its { + intf, err := model.InterfaceFromInterfaceType(it.typ) + if err != nil { + fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) + os.Exit(1) + } + intf.Name = it.sym + pkg.Interfaces = append(pkg.Interfaces, intf) + } + + outfile := os.Stdout + if len(*output) != 0 { + var err error + outfile, err = os.Create(*output) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) + } + defer func() { + if err := outfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) + os.Exit(1) + } + }() + } + + if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { + fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) + os.Exit(1) + } +} +`)) diff --git a/vendor/go.uber.org/mock/mockgen/version.go b/vendor/go.uber.org/mock/mockgen/version.go new file mode 100644 index 000000000..6db160ac2 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/version.go @@ -0,0 +1,31 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + "runtime/debug" +) + +func printModuleVersion() { + if bi, exists := debug.ReadBuildInfo(); exists { + fmt.Println(bi.Main.Version) + } else { + log.Printf("No version information found. Make sure to use " + + "GO111MODULE=on when running 'go get' in order to use specific " + + "version of the binary.") + } +} diff --git a/vendor/go.uber.org/multierr/.travis.yml b/vendor/go.uber.org/multierr/.travis.yml deleted file mode 100644 index 8636ab42a..000000000 --- a/vendor/go.uber.org/multierr/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go_import_path: go.uber.org/multierr - -env: - global: - - GO111MODULE=on - -go: - - oldstable - - stable - -before_install: -- go version - -script: -- | - set -e - make lint - make cover - -after_success: -- bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md index 6f1db9ef4..f8177b978 100644 --- a/vendor/go.uber.org/multierr/CHANGELOG.md +++ b/vendor/go.uber.org/multierr/CHANGELOG.md @@ -1,6 +1,41 @@ Releases ======== +v1.11.0 (2023-03-28) +==================== +- `Errors` now supports any error that implements multiple-error + interface. +- Add `Every` function to allow checking if all errors in the chain + satisfies `errors.Is` against the target error. + +v1.10.0 (2023-03-08) +==================== + +- Comply with Go 1.20's multiple-error interface. +- Drop Go 1.18 support. + Per the support policy, only Go 1.19 and 1.20 are supported now. +- Drop all non-test external dependencies. + +v1.9.0 (2022-12-12) +=================== + +- Add `AppendFunc` that allow passsing functions to similar to + `AppendInvoke`. + +- Bump up yaml.v3 dependency to 3.0.1. + +v1.8.0 (2022-02-28) +=================== + +- `Combine`: perform zero allocations when there are no errors. + + +v1.7.0 (2021-05-06) +=================== + +- Add `AppendInvoke` to append into errors from `defer` blocks. + + v1.6.0 (2020-09-14) =================== diff --git a/vendor/go.uber.org/multierr/LICENSE.txt b/vendor/go.uber.org/multierr/LICENSE.txt index 858e02475..413e30f7c 100644 --- a/vendor/go.uber.org/multierr/LICENSE.txt +++ b/vendor/go.uber.org/multierr/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Uber Technologies, Inc. +Copyright (c) 2017-2021 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile index 316004400..dcb6fe723 100644 --- a/vendor/go.uber.org/multierr/Makefile +++ b/vendor/go.uber.org/multierr/Makefile @@ -34,9 +34,5 @@ lint: gofmt golint staticcheck .PHONY: cover cover: - go test -coverprofile=cover.out -coverpkg=./... -v ./... + go test -race -coverprofile=cover.out -coverpkg=./... -v ./... go tool cover -html=cover.out -o cover.html - -update-license: - @cd tools && go install go.uber.org/tools/update-license - @$(GOBIN)/update-license $(GO_FILES) diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md index 751bd65e5..5ab6ac40f 100644 --- a/vendor/go.uber.org/multierr/README.md +++ b/vendor/go.uber.org/multierr/README.md @@ -2,9 +2,29 @@ `multierr` allows combining one or more Go `error`s together. +## Features + +- **Idiomatic**: + multierr follows best practices in Go, and keeps your code idiomatic. + - It keeps the underlying error type hidden, + allowing you to deal in `error` values exclusively. + - It provides APIs to safely append into an error from a `defer` statement. +- **Performant**: + multierr is optimized for performance: + - It avoids allocations where possible. + - It utilizes slice resizing semantics to optimize common cases + like appending into the same error object from a loop. +- **Interoperable**: + multierr interoperates with the Go standard library's error APIs seamlessly: + - The `errors.Is` and `errors.As` functions *just work*. +- **Lightweight**: + multierr comes with virtually no dependencies. + ## Installation - go get -u go.uber.org/multierr +```bash +go get -u go.uber.org/multierr@latest +``` ## Status @@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0. Released under the [MIT License]. [MIT License]: LICENSE.txt -[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg -[doc]: https://godoc.org/go.uber.org/multierr -[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr +[doc]: https://pkg.go.dev/go.uber.org/multierr +[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg [cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://travis-ci.com/uber-go/multierr +[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml [cov]: https://codecov.io/gh/uber-go/multierr diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go index 5c9b67d53..3a828b2df 100644 --- a/vendor/go.uber.org/multierr/error.go +++ b/vendor/go.uber.org/multierr/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,54 +20,109 @@ // Package multierr allows combining one or more errors together. // -// Overview +// # Overview // // Errors can be combined with the use of the Combine function. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// conn.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) // // If only two errors are being combined, the Append function may be used // instead. // -// err = multierr.Append(reader.Close(), writer.Close()) -// -// This makes it possible to record resource cleanup failures from deferred -// blocks with the help of named return values. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer func() { -// err = multierr.Append(err, conn.Close()) -// }() -// // ... -// } +// err = multierr.Append(reader.Close(), writer.Close()) // // The underlying list of errors for a returned error object may be retrieved // with the Errors function. // -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) -// } +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:", errors) +// } +// +// # Appending from a loop +// +// You sometimes need to append into an error from a loop. +// +// var err error +// for _, item := range items { +// err = multierr.Append(err, process(item)) +// } +// +// Cases like this may require knowledge of whether an individual instance +// failed. This usually requires introduction of a new variable. +// +// var err error +// for _, item := range items { +// if perr := process(item); perr != nil { +// log.Warn("skipping item", item) +// err = multierr.Append(err, perr) +// } +// } +// +// multierr includes AppendInto to simplify cases like this. +// +// var err error +// for _, item := range items { +// if multierr.AppendInto(&err, process(item)) { +// log.Warn("skipping item", item) +// } +// } +// +// This will append the error into the err variable, and return true if that +// individual error was non-nil. // -// Advanced Usage +// See [AppendInto] for more information. +// +// # Deferred Functions +// +// Go makes it possible to modify the return value of a function in a defer +// block if the function was using named returns. This makes it possible to +// record resource cleanup failures from deferred blocks. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// multierr provides the Invoker type and AppendInvoke function to make cases +// like the above simpler and obviate the need for a closure. The following is +// roughly equivalent to the example above. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(conn)) +// // ... +// } +// +// See [AppendInvoke] and [Invoker] for more information. +// +// NOTE: If you're modifying an error from inside a defer, you MUST use a named +// return value for that function. +// +// # Advanced Usage // // Errors returned by Combine and Append MAY implement the following // interface. // -// type errorGroup interface { -// // Returns a slice containing the underlying list of errors. -// // -// // This slice MUST NOT be modified by the caller. -// Errors() []error -// } +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } // // Note that if you need access to list of errors behind a multierr error, you // should prefer using the Errors function. That said, if you need cheap @@ -76,23 +131,23 @@ // because errors returned by Combine and Append are not guaranteed to // implement this interface. // -// var errors []error -// group, ok := err.(errorGroup) -// if ok { -// errors = group.Errors() -// } else { -// errors = []error{err} -// } +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } package multierr // import "go.uber.org/multierr" import ( "bytes" + "errors" "fmt" "io" "strings" "sync" - - "go.uber.org/atomic" + "sync/atomic" ) var ( @@ -132,34 +187,15 @@ type errorGroup interface { // Errors returns a slice containing zero or more errors that the supplied // error is composed of. If the error is nil, a nil slice is returned. // -// err := multierr.Append(r.Close(), w.Close()) -// errors := multierr.Errors(err) +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) // // If the error is not composed of other errors, the returned slice contains // just the error that was passed in. // // Callers of this function are free to modify the returned slice. func Errors(err error) []error { - if err == nil { - return nil - } - - // Note that we're casting to multiError, not errorGroup. Our contract is - // that returned errors MAY implement errorGroup. Errors, however, only - // has special behavior for multierr-specific error objects. - // - // This behavior can be expanded in the future but I think it's prudent to - // start with as little as possible in terms of contract and possibility - // of misuse. - eg, ok := err.(*multiError) - if !ok { - return []error{err} - } - - errors := eg.Errors() - result := make([]error, len(errors)) - copy(result, errors) - return result + return extractErrors(err) } // multiError is an error that holds one or more errors. @@ -174,8 +210,6 @@ type multiError struct { errors []error } -var _ errorGroup = (*multiError)(nil) - // Errors returns the list of underlying errors. // // This slice MUST NOT be modified. @@ -201,6 +235,17 @@ func (merr *multiError) Error() string { return result } +// Every compares every error in the given err against the given target error +// using [errors.Is], and returns true only if every comparison returned true. +func Every(err error, target error) bool { + for _, e := range extractErrors(err) { + if !errors.Is(e, target) { + return false + } + } + return true +} + func (merr *multiError) Format(f fmt.State, c rune) { if c == 'v' && f.Flag('+') { merr.writeMultiline(f) @@ -292,6 +337,14 @@ func inspect(errors []error) (res inspectResult) { // fromSlice converts the given list of errors into a single error. func fromSlice(errors []error) error { + // Don't pay to inspect small slices. + switch len(errors) { + case 0: + return nil + case 1: + return errors[0] + } + res := inspect(errors) switch res.Count { case 0: @@ -301,8 +354,12 @@ func fromSlice(errors []error) error { return errors[res.FirstErrorIdx] case len(errors): if !res.ContainsMultiError { - // already flat - return &multiError{errors: errors} + // Error list is flat. Make a copy of it + // Otherwise "errors" escapes to the heap + // unconditionally for all other cases. + // This lets us optimize for the "no errors" case. + out := append(([]error)(nil), errors...) + return &multiError{errors: out} } } @@ -327,32 +384,32 @@ func fromSlice(errors []error) error { // If zero arguments were passed or if all items are nil, a nil error is // returned. // -// Combine(nil, nil) // == nil +// Combine(nil, nil) // == nil // // If only a single error was passed, it is returned as-is. // -// Combine(err) // == err +// Combine(err) // == err // // Combine skips over nil arguments so this function may be used to combine // together errors from operations that fail independently of each other. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// pipe.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) // // If any of the passed errors is a multierr error, it will be flattened along // with the other errors. // -// multierr.Combine(multierr.Combine(err1, err2), err3) -// // is the same as -// multierr.Combine(err1, err2, err3) +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) // // The returned error formats into a readable multi-line error message if // formatted with %+v. // -// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) func Combine(errors ...error) error { return fromSlice(errors) } @@ -362,16 +419,19 @@ func Combine(errors ...error) error { // This function is a specialization of Combine for the common case where // there are only two errors. // -// err = multierr.Append(reader.Close(), writer.Close()) +// err = multierr.Append(reader.Close(), writer.Close()) // // The following pattern may also be used to record failure of deferred // operations without losing information about the original error. // -// func doSomething(..) (err error) { -// f := acquireResource() -// defer func() { -// err = multierr.Append(err, f.Close()) -// }() +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +// +// Note that the variable MUST be a named return to append an error to it from +// the defer statement. See also [AppendInvoke]. func Append(left error, right error) error { switch { case left == nil: @@ -401,37 +461,37 @@ func Append(left error, right error) error { // AppendInto appends an error into the destination of an error pointer and // returns whether the error being appended was non-nil. // -// var err error -// multierr.AppendInto(&err, r.Close()) -// multierr.AppendInto(&err, w.Close()) +// var err error +// multierr.AppendInto(&err, r.Close()) +// multierr.AppendInto(&err, w.Close()) // // The above is equivalent to, // -// err := multierr.Append(r.Close(), w.Close()) +// err := multierr.Append(r.Close(), w.Close()) // // As AppendInto reports whether the provided error was non-nil, it may be // used to build a multierr error in a loop more ergonomically. For example: // -// var err error -// for line := range lines { -// var item Item -// if multierr.AppendInto(&err, parse(line, &item)) { -// continue -// } -// items = append(items, item) -// } -// -// Compare this with a verison that relies solely on Append: -// -// var err error -// for line := range lines { -// var item Item -// if parseErr := parse(line, &item); parseErr != nil { -// err = multierr.Append(err, parseErr) -// continue -// } -// items = append(items, item) -// } +// var err error +// for line := range lines { +// var item Item +// if multierr.AppendInto(&err, parse(line, &item)) { +// continue +// } +// items = append(items, item) +// } +// +// Compare this with a version that relies solely on Append: +// +// var err error +// for line := range lines { +// var item Item +// if parseErr := parse(line, &item); parseErr != nil { +// err = multierr.Append(err, parseErr) +// continue +// } +// items = append(items, item) +// } func AppendInto(into *error, err error) (errored bool) { if into == nil { // We panic if 'into' is nil. This is not documented above @@ -447,3 +507,140 @@ func AppendInto(into *error, err error) (errored bool) { *into = Append(*into, err) return true } + +// Invoker is an operation that may fail with an error. Use it with +// AppendInvoke to append the result of calling the function into an error. +// This allows you to conveniently defer capture of failing operations. +// +// See also, [Close] and [Invoke]. +type Invoker interface { + Invoke() error +} + +// Invoke wraps a function which may fail with an error to match the Invoker +// interface. Use it to supply functions matching this signature to +// AppendInvoke. +// +// For example, +// +// func processReader(r io.Reader) (err error) { +// scanner := bufio.NewScanner(r) +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// for scanner.Scan() { +// // ... +// } +// // ... +// } +// +// In this example, the following line will construct the Invoker right away, +// but defer the invocation of scanner.Err() until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +type Invoke func() error + +// Invoke calls the supplied function and returns its result. +func (i Invoke) Invoke() error { return i() } + +// Close builds an Invoker that closes the provided io.Closer. Use it with +// AppendInvoke to close io.Closers and append their results into an error. +// +// For example, +// +// func processFile(path string) (err error) { +// f, err := os.Open(path) +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// return processReader(f) +// } +// +// In this example, multierr.Close will construct the Invoker right away, but +// defer the invocation of f.Close until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +func Close(closer io.Closer) Invoker { + return Invoke(closer.Close) +} + +// AppendInvoke appends the result of calling the given Invoker into the +// provided error pointer. Use it with named returns to safely defer +// invocation of fallible operations until a function returns, and capture the +// resulting errors. +// +// func doSomething(...) (err error) { +// // ... +// f, err := openFile(..) +// if err != nil { +// return err +// } +// +// // multierr will call f.Close() when this function returns and +// // if the operation fails, its append its error into the +// // returned error. +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// scanner := bufio.NewScanner(f) +// // Similarly, this scheduled scanner.Err to be called and +// // inspected when the function returns and append its error +// // into the returned error. +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// // ... +// } +// +// NOTE: If used with a defer, the error variable MUST be a named return. +// +// Without defer, AppendInvoke behaves exactly like AppendInto. +// +// err := // ... +// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) +// +// // ...is roughly equivalent to... +// +// err := // ... +// multierr.AppendInto(&err, foo()) +// +// The advantage of the indirection introduced by Invoker is to make it easy +// to defer the invocation of a function. Without this indirection, the +// invoked function will be evaluated at the time of the defer block rather +// than when the function returns. +// +// // BAD: This is likely not what the caller intended. This will evaluate +// // foo() right away and append its result into the error when the +// // function returns. +// defer multierr.AppendInto(&err, foo()) +// +// // GOOD: This will defer invocation of foo unutil the function returns. +// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) +// +// multierr provides a few Invoker implementations out of the box for +// convenience. See [Invoker] for more information. +func AppendInvoke(into *error, invoker Invoker) { + AppendInto(into, invoker.Invoke()) +} + +// AppendFunc is a shorthand for [AppendInvoke]. +// It allows using function or method value directly +// without having to wrap it into an [Invoker] interface. +// +// func doSomething(...) (err error) { +// w, err := startWorker(...) +// if err != nil { +// return err +// } +// +// // multierr will call w.Stop() when this function returns and +// // if the operation fails, it appends its error into the +// // returned error. +// defer multierr.AppendFunc(&err, w.Stop) +// } +func AppendFunc(into *error, fn func() error) { + AppendInvoke(into, Invoke(fn)) +} diff --git a/vendor/go.uber.org/multierr/error_post_go120.go b/vendor/go.uber.org/multierr/error_post_go120.go new file mode 100644 index 000000000..a173f9c25 --- /dev/null +++ b/vendor/go.uber.org/multierr/error_post_go120.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.20 +// +build go1.20 + +package multierr + +// Unwrap returns a list of errors wrapped by this multierr. +func (merr *multiError) Unwrap() []error { + return merr.Errors() +} + +type multipleErrors interface { + Unwrap() []error +} + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // check if the given err is an Unwrapable error that + // implements multipleErrors interface. + eg, ok := err.(multipleErrors) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Unwrap()...) +} diff --git a/vendor/go.uber.org/multierr/go113.go b/vendor/go.uber.org/multierr/error_pre_go120.go similarity index 66% rename from vendor/go.uber.org/multierr/go113.go rename to vendor/go.uber.org/multierr/error_pre_go120.go index 264b0eac0..93872a3fc 100644 --- a/vendor/go.uber.org/multierr/go113.go +++ b/vendor/go.uber.org/multierr/error_pre_go120.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,12 +18,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// +build go1.13 +//go:build !go1.20 +// +build !go1.20 package multierr import "errors" +// Versions of Go before 1.20 did not support the Unwrap() []error method. +// This provides a similar behavior by implementing the Is(..) and As(..) +// methods. +// See the errors.Join proposal for details: +// https://github.com/golang/go/issues/53435 + // As attempts to find the first error in the error list that matches the type // of the value that target points to. // @@ -50,3 +57,23 @@ func (merr *multiError) Is(target error) bool { } return false } + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Errors()...) +} diff --git a/vendor/go.uber.org/multierr/glide.yaml b/vendor/go.uber.org/multierr/glide.yaml deleted file mode 100644 index 6ef084ec2..000000000 --- a/vendor/go.uber.org/multierr/glide.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: go.uber.org/multierr -import: -- package: go.uber.org/atomic - version: ^1 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/crypto/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/crypto/PATENTS b/vendor/golang.org/x/crypto/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/crypto/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go new file mode 100644 index 000000000..661ea132e --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package chacha20 + +const bufSize = 256 + +//go:noescape +func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s new file mode 100644 index 000000000..7dd2638e8 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s @@ -0,0 +1,307 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#include "textflag.h" + +#define NUM_ROUNDS 10 + +// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) +TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 + MOVD dst+0(FP), R1 + MOVD src+24(FP), R2 + MOVD src_len+32(FP), R3 + MOVD key+48(FP), R4 + MOVD nonce+56(FP), R6 + MOVD counter+64(FP), R7 + + MOVD $·constants(SB), R10 + MOVD $·incRotMatrix(SB), R11 + + MOVW (R7), R20 + + AND $~255, R3, R13 + ADD R2, R13, R12 // R12 for block end + AND $255, R3, R13 +loop: + MOVD $NUM_ROUNDS, R21 + VLD1 (R11), [V30.S4, V31.S4] + + // load contants + // VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4] + WORD $0x4D60E940 + + // load keys + // VLD4R 16(R4), [V4.S4, V5.S4, V6.S4, V7.S4] + WORD $0x4DFFE884 + // VLD4R 16(R4), [V8.S4, V9.S4, V10.S4, V11.S4] + WORD $0x4DFFE888 + SUB $32, R4 + + // load counter + nonce + // VLD1R (R7), [V12.S4] + WORD $0x4D40C8EC + + // VLD3R (R6), [V13.S4, V14.S4, V15.S4] + WORD $0x4D40E8CD + + // update counter + VADD V30.S4, V12.S4, V12.S4 + +chacha: + // V0..V3 += V4..V7 + // V12..V15 <<<= ((V12..V15 XOR V0..V3), 16) + VADD V0.S4, V4.S4, V0.S4 + VADD V1.S4, V5.S4, V1.S4 + VADD V2.S4, V6.S4, V2.S4 + VADD V3.S4, V7.S4, V3.S4 + VEOR V12.B16, V0.B16, V12.B16 + VEOR V13.B16, V1.B16, V13.B16 + VEOR V14.B16, V2.B16, V14.B16 + VEOR V15.B16, V3.B16, V15.B16 + VREV32 V12.H8, V12.H8 + VREV32 V13.H8, V13.H8 + VREV32 V14.H8, V14.H8 + VREV32 V15.H8, V15.H8 + // V8..V11 += V12..V15 + // V4..V7 <<<= ((V4..V7 XOR V8..V11), 12) + VADD V8.S4, V12.S4, V8.S4 + VADD V9.S4, V13.S4, V9.S4 + VADD V10.S4, V14.S4, V10.S4 + VADD V11.S4, V15.S4, V11.S4 + VEOR V8.B16, V4.B16, V16.B16 + VEOR V9.B16, V5.B16, V17.B16 + VEOR V10.B16, V6.B16, V18.B16 + VEOR V11.B16, V7.B16, V19.B16 + VSHL $12, V16.S4, V4.S4 + VSHL $12, V17.S4, V5.S4 + VSHL $12, V18.S4, V6.S4 + VSHL $12, V19.S4, V7.S4 + VSRI $20, V16.S4, V4.S4 + VSRI $20, V17.S4, V5.S4 + VSRI $20, V18.S4, V6.S4 + VSRI $20, V19.S4, V7.S4 + + // V0..V3 += V4..V7 + // V12..V15 <<<= ((V12..V15 XOR V0..V3), 8) + VADD V0.S4, V4.S4, V0.S4 + VADD V1.S4, V5.S4, V1.S4 + VADD V2.S4, V6.S4, V2.S4 + VADD V3.S4, V7.S4, V3.S4 + VEOR V12.B16, V0.B16, V12.B16 + VEOR V13.B16, V1.B16, V13.B16 + VEOR V14.B16, V2.B16, V14.B16 + VEOR V15.B16, V3.B16, V15.B16 + VTBL V31.B16, [V12.B16], V12.B16 + VTBL V31.B16, [V13.B16], V13.B16 + VTBL V31.B16, [V14.B16], V14.B16 + VTBL V31.B16, [V15.B16], V15.B16 + + // V8..V11 += V12..V15 + // V4..V7 <<<= ((V4..V7 XOR V8..V11), 7) + VADD V12.S4, V8.S4, V8.S4 + VADD V13.S4, V9.S4, V9.S4 + VADD V14.S4, V10.S4, V10.S4 + VADD V15.S4, V11.S4, V11.S4 + VEOR V8.B16, V4.B16, V16.B16 + VEOR V9.B16, V5.B16, V17.B16 + VEOR V10.B16, V6.B16, V18.B16 + VEOR V11.B16, V7.B16, V19.B16 + VSHL $7, V16.S4, V4.S4 + VSHL $7, V17.S4, V5.S4 + VSHL $7, V18.S4, V6.S4 + VSHL $7, V19.S4, V7.S4 + VSRI $25, V16.S4, V4.S4 + VSRI $25, V17.S4, V5.S4 + VSRI $25, V18.S4, V6.S4 + VSRI $25, V19.S4, V7.S4 + + // V0..V3 += V5..V7, V4 + // V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16) + VADD V0.S4, V5.S4, V0.S4 + VADD V1.S4, V6.S4, V1.S4 + VADD V2.S4, V7.S4, V2.S4 + VADD V3.S4, V4.S4, V3.S4 + VEOR V15.B16, V0.B16, V15.B16 + VEOR V12.B16, V1.B16, V12.B16 + VEOR V13.B16, V2.B16, V13.B16 + VEOR V14.B16, V3.B16, V14.B16 + VREV32 V12.H8, V12.H8 + VREV32 V13.H8, V13.H8 + VREV32 V14.H8, V14.H8 + VREV32 V15.H8, V15.H8 + + // V10 += V15; V5 <<<= ((V10 XOR V5), 12) + // ... + VADD V15.S4, V10.S4, V10.S4 + VADD V12.S4, V11.S4, V11.S4 + VADD V13.S4, V8.S4, V8.S4 + VADD V14.S4, V9.S4, V9.S4 + VEOR V10.B16, V5.B16, V16.B16 + VEOR V11.B16, V6.B16, V17.B16 + VEOR V8.B16, V7.B16, V18.B16 + VEOR V9.B16, V4.B16, V19.B16 + VSHL $12, V16.S4, V5.S4 + VSHL $12, V17.S4, V6.S4 + VSHL $12, V18.S4, V7.S4 + VSHL $12, V19.S4, V4.S4 + VSRI $20, V16.S4, V5.S4 + VSRI $20, V17.S4, V6.S4 + VSRI $20, V18.S4, V7.S4 + VSRI $20, V19.S4, V4.S4 + + // V0 += V5; V15 <<<= ((V0 XOR V15), 8) + // ... + VADD V5.S4, V0.S4, V0.S4 + VADD V6.S4, V1.S4, V1.S4 + VADD V7.S4, V2.S4, V2.S4 + VADD V4.S4, V3.S4, V3.S4 + VEOR V0.B16, V15.B16, V15.B16 + VEOR V1.B16, V12.B16, V12.B16 + VEOR V2.B16, V13.B16, V13.B16 + VEOR V3.B16, V14.B16, V14.B16 + VTBL V31.B16, [V12.B16], V12.B16 + VTBL V31.B16, [V13.B16], V13.B16 + VTBL V31.B16, [V14.B16], V14.B16 + VTBL V31.B16, [V15.B16], V15.B16 + + // V10 += V15; V5 <<<= ((V10 XOR V5), 7) + // ... + VADD V15.S4, V10.S4, V10.S4 + VADD V12.S4, V11.S4, V11.S4 + VADD V13.S4, V8.S4, V8.S4 + VADD V14.S4, V9.S4, V9.S4 + VEOR V10.B16, V5.B16, V16.B16 + VEOR V11.B16, V6.B16, V17.B16 + VEOR V8.B16, V7.B16, V18.B16 + VEOR V9.B16, V4.B16, V19.B16 + VSHL $7, V16.S4, V5.S4 + VSHL $7, V17.S4, V6.S4 + VSHL $7, V18.S4, V7.S4 + VSHL $7, V19.S4, V4.S4 + VSRI $25, V16.S4, V5.S4 + VSRI $25, V17.S4, V6.S4 + VSRI $25, V18.S4, V7.S4 + VSRI $25, V19.S4, V4.S4 + + SUB $1, R21 + CBNZ R21, chacha + + // VLD4R (R10), [V16.S4, V17.S4, V18.S4, V19.S4] + WORD $0x4D60E950 + + // VLD4R 16(R4), [V20.S4, V21.S4, V22.S4, V23.S4] + WORD $0x4DFFE894 + VADD V30.S4, V12.S4, V12.S4 + VADD V16.S4, V0.S4, V0.S4 + VADD V17.S4, V1.S4, V1.S4 + VADD V18.S4, V2.S4, V2.S4 + VADD V19.S4, V3.S4, V3.S4 + // VLD4R 16(R4), [V24.S4, V25.S4, V26.S4, V27.S4] + WORD $0x4DFFE898 + // restore R4 + SUB $32, R4 + + // load counter + nonce + // VLD1R (R7), [V28.S4] + WORD $0x4D40C8FC + // VLD3R (R6), [V29.S4, V30.S4, V31.S4] + WORD $0x4D40E8DD + + VADD V20.S4, V4.S4, V4.S4 + VADD V21.S4, V5.S4, V5.S4 + VADD V22.S4, V6.S4, V6.S4 + VADD V23.S4, V7.S4, V7.S4 + VADD V24.S4, V8.S4, V8.S4 + VADD V25.S4, V9.S4, V9.S4 + VADD V26.S4, V10.S4, V10.S4 + VADD V27.S4, V11.S4, V11.S4 + VADD V28.S4, V12.S4, V12.S4 + VADD V29.S4, V13.S4, V13.S4 + VADD V30.S4, V14.S4, V14.S4 + VADD V31.S4, V15.S4, V15.S4 + + VZIP1 V1.S4, V0.S4, V16.S4 + VZIP2 V1.S4, V0.S4, V17.S4 + VZIP1 V3.S4, V2.S4, V18.S4 + VZIP2 V3.S4, V2.S4, V19.S4 + VZIP1 V5.S4, V4.S4, V20.S4 + VZIP2 V5.S4, V4.S4, V21.S4 + VZIP1 V7.S4, V6.S4, V22.S4 + VZIP2 V7.S4, V6.S4, V23.S4 + VZIP1 V9.S4, V8.S4, V24.S4 + VZIP2 V9.S4, V8.S4, V25.S4 + VZIP1 V11.S4, V10.S4, V26.S4 + VZIP2 V11.S4, V10.S4, V27.S4 + VZIP1 V13.S4, V12.S4, V28.S4 + VZIP2 V13.S4, V12.S4, V29.S4 + VZIP1 V15.S4, V14.S4, V30.S4 + VZIP2 V15.S4, V14.S4, V31.S4 + VZIP1 V18.D2, V16.D2, V0.D2 + VZIP2 V18.D2, V16.D2, V4.D2 + VZIP1 V19.D2, V17.D2, V8.D2 + VZIP2 V19.D2, V17.D2, V12.D2 + VLD1.P 64(R2), [V16.B16, V17.B16, V18.B16, V19.B16] + + VZIP1 V22.D2, V20.D2, V1.D2 + VZIP2 V22.D2, V20.D2, V5.D2 + VZIP1 V23.D2, V21.D2, V9.D2 + VZIP2 V23.D2, V21.D2, V13.D2 + VLD1.P 64(R2), [V20.B16, V21.B16, V22.B16, V23.B16] + VZIP1 V26.D2, V24.D2, V2.D2 + VZIP2 V26.D2, V24.D2, V6.D2 + VZIP1 V27.D2, V25.D2, V10.D2 + VZIP2 V27.D2, V25.D2, V14.D2 + VLD1.P 64(R2), [V24.B16, V25.B16, V26.B16, V27.B16] + VZIP1 V30.D2, V28.D2, V3.D2 + VZIP2 V30.D2, V28.D2, V7.D2 + VZIP1 V31.D2, V29.D2, V11.D2 + VZIP2 V31.D2, V29.D2, V15.D2 + VLD1.P 64(R2), [V28.B16, V29.B16, V30.B16, V31.B16] + VEOR V0.B16, V16.B16, V16.B16 + VEOR V1.B16, V17.B16, V17.B16 + VEOR V2.B16, V18.B16, V18.B16 + VEOR V3.B16, V19.B16, V19.B16 + VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R1) + VEOR V4.B16, V20.B16, V20.B16 + VEOR V5.B16, V21.B16, V21.B16 + VEOR V6.B16, V22.B16, V22.B16 + VEOR V7.B16, V23.B16, V23.B16 + VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R1) + VEOR V8.B16, V24.B16, V24.B16 + VEOR V9.B16, V25.B16, V25.B16 + VEOR V10.B16, V26.B16, V26.B16 + VEOR V11.B16, V27.B16, V27.B16 + VST1.P [V24.B16, V25.B16, V26.B16, V27.B16], 64(R1) + VEOR V12.B16, V28.B16, V28.B16 + VEOR V13.B16, V29.B16, V29.B16 + VEOR V14.B16, V30.B16, V30.B16 + VEOR V15.B16, V31.B16, V31.B16 + VST1.P [V28.B16, V29.B16, V30.B16, V31.B16], 64(R1) + + ADD $4, R20 + MOVW R20, (R7) // update counter + + CMP R2, R12 + BGT loop + + RET + + +DATA ·constants+0x00(SB)/4, $0x61707865 +DATA ·constants+0x04(SB)/4, $0x3320646e +DATA ·constants+0x08(SB)/4, $0x79622d32 +DATA ·constants+0x0c(SB)/4, $0x6b206574 +GLOBL ·constants(SB), NOPTR|RODATA, $32 + +DATA ·incRotMatrix+0x00(SB)/4, $0x00000000 +DATA ·incRotMatrix+0x04(SB)/4, $0x00000001 +DATA ·incRotMatrix+0x08(SB)/4, $0x00000002 +DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003 +DATA ·incRotMatrix+0x10(SB)/4, $0x02010003 +DATA ·incRotMatrix+0x14(SB)/4, $0x06050407 +DATA ·incRotMatrix+0x18(SB)/4, $0x0A09080B +DATA ·incRotMatrix+0x1c(SB)/4, $0x0E0D0C0F +GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_generic.go b/vendor/golang.org/x/crypto/chacha20/chacha_generic.go new file mode 100644 index 000000000..93eb5ae6d --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_generic.go @@ -0,0 +1,398 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms +// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01. +package chacha20 + +import ( + "crypto/cipher" + "encoding/binary" + "errors" + "math/bits" + + "golang.org/x/crypto/internal/alias" +) + +const ( + // KeySize is the size of the key used by this cipher, in bytes. + KeySize = 32 + + // NonceSize is the size of the nonce used with the standard variant of this + // cipher, in bytes. + // + // Note that this is too short to be safely generated at random if the same + // key is reused more than 2³² times. + NonceSize = 12 + + // NonceSizeX is the size of the nonce used with the XChaCha20 variant of + // this cipher, in bytes. + NonceSizeX = 24 +) + +// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key +// and nonce. A *Cipher implements the cipher.Stream interface. +type Cipher struct { + // The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter + // (incremented after each block), and 3 of nonce. + key [8]uint32 + counter uint32 + nonce [3]uint32 + + // The last len bytes of buf are leftover key stream bytes from the previous + // XORKeyStream invocation. The size of buf depends on how many blocks are + // computed at a time by xorKeyStreamBlocks. + buf [bufSize]byte + len int + + // overflow is set when the counter overflowed, no more blocks can be + // generated, and the next XORKeyStream call should panic. + overflow bool + + // The counter-independent results of the first round are cached after they + // are computed the first time. + precompDone bool + p1, p5, p9, p13 uint32 + p2, p6, p10, p14 uint32 + p3, p7, p11, p15 uint32 +} + +var _ cipher.Stream = (*Cipher)(nil) + +// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given +// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided, +// the XChaCha20 construction will be used. It returns an error if key or nonce +// have any other length. +// +// Note that ChaCha20, like all stream ciphers, is not authenticated and allows +// attackers to silently tamper with the plaintext. For this reason, it is more +// appropriate as a building block than as a standalone encryption mechanism. +// Instead, consider using package golang.org/x/crypto/chacha20poly1305. +func NewUnauthenticatedCipher(key, nonce []byte) (*Cipher, error) { + // This function is split into a wrapper so that the Cipher allocation will + // be inlined, and depending on how the caller uses the return value, won't + // escape to the heap. + c := &Cipher{} + return newUnauthenticatedCipher(c, key, nonce) +} + +func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20: wrong key size") + } + if len(nonce) == NonceSizeX { + // XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a + // derived key, allowing it to operate on a nonce of 24 bytes. See + // draft-irtf-cfrg-xchacha-01, Section 2.3. + key, _ = HChaCha20(key, nonce[0:16]) + cNonce := make([]byte, NonceSize) + copy(cNonce[4:12], nonce[16:24]) + nonce = cNonce + } else if len(nonce) != NonceSize { + return nil, errors.New("chacha20: wrong nonce size") + } + + key, nonce = key[:KeySize], nonce[:NonceSize] // bounds check elimination hint + c.key = [8]uint32{ + binary.LittleEndian.Uint32(key[0:4]), + binary.LittleEndian.Uint32(key[4:8]), + binary.LittleEndian.Uint32(key[8:12]), + binary.LittleEndian.Uint32(key[12:16]), + binary.LittleEndian.Uint32(key[16:20]), + binary.LittleEndian.Uint32(key[20:24]), + binary.LittleEndian.Uint32(key[24:28]), + binary.LittleEndian.Uint32(key[28:32]), + } + c.nonce = [3]uint32{ + binary.LittleEndian.Uint32(nonce[0:4]), + binary.LittleEndian.Uint32(nonce[4:8]), + binary.LittleEndian.Uint32(nonce[8:12]), + } + return c, nil +} + +// The constant first 4 words of the ChaCha20 state. +const ( + j0 uint32 = 0x61707865 // expa + j1 uint32 = 0x3320646e // nd 3 + j2 uint32 = 0x79622d32 // 2-by + j3 uint32 = 0x6b206574 // te k +) + +const blockSize = 64 + +// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words. +// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16 +// words each round, in columnar or diagonal groups of 4 at a time. +func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) { + a += b + d ^= a + d = bits.RotateLeft32(d, 16) + c += d + b ^= c + b = bits.RotateLeft32(b, 12) + a += b + d ^= a + d = bits.RotateLeft32(d, 8) + c += d + b ^= c + b = bits.RotateLeft32(b, 7) + return a, b, c, d +} + +// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will +// behave as if (64 * counter) bytes had been encrypted so far. +// +// To prevent accidental counter reuse, SetCounter panics if counter is less +// than the current value. +// +// Note that the execution time of XORKeyStream is not independent of the +// counter value. +func (s *Cipher) SetCounter(counter uint32) { + // Internally, s may buffer multiple blocks, which complicates this + // implementation slightly. When checking whether the counter has rolled + // back, we must use both s.counter and s.len to determine how many blocks + // we have already output. + outputCounter := s.counter - uint32(s.len)/blockSize + if s.overflow || counter < outputCounter { + panic("chacha20: SetCounter attempted to rollback counter") + } + + // In the general case, we set the new counter value and reset s.len to 0, + // causing the next call to XORKeyStream to refill the buffer. However, if + // we're advancing within the existing buffer, we can save work by simply + // setting s.len. + if counter < s.counter { + s.len = int(s.counter-counter) * blockSize + } else { + s.counter = counter + s.len = 0 + } +} + +// XORKeyStream XORs each byte in the given slice with a byte from the +// cipher's key stream. Dst and src must overlap entirely or not at all. +// +// If len(dst) < len(src), XORKeyStream will panic. It is acceptable +// to pass a dst bigger than src, and in that case, XORKeyStream will +// only update dst[:len(src)] and will not touch the rest of dst. +// +// Multiple calls to XORKeyStream behave as if the concatenation of +// the src buffers was passed in a single run. That is, Cipher +// maintains state and does not reset at each XORKeyStream call. +func (s *Cipher) XORKeyStream(dst, src []byte) { + if len(src) == 0 { + return + } + if len(dst) < len(src) { + panic("chacha20: output smaller than input") + } + dst = dst[:len(src)] + if alias.InexactOverlap(dst, src) { + panic("chacha20: invalid buffer overlap") + } + + // First, drain any remaining key stream from a previous XORKeyStream. + if s.len != 0 { + keyStream := s.buf[bufSize-s.len:] + if len(src) < len(keyStream) { + keyStream = keyStream[:len(src)] + } + _ = src[len(keyStream)-1] // bounds check elimination hint + for i, b := range keyStream { + dst[i] = src[i] ^ b + } + s.len -= len(keyStream) + dst, src = dst[len(keyStream):], src[len(keyStream):] + } + if len(src) == 0 { + return + } + + // If we'd need to let the counter overflow and keep generating output, + // panic immediately. If instead we'd only reach the last block, remember + // not to generate any more output after the buffer is drained. + numBlocks := (uint64(len(src)) + blockSize - 1) / blockSize + if s.overflow || uint64(s.counter)+numBlocks > 1<<32 { + panic("chacha20: counter overflow") + } else if uint64(s.counter)+numBlocks == 1<<32 { + s.overflow = true + } + + // xorKeyStreamBlocks implementations expect input lengths that are a + // multiple of bufSize. Platform-specific ones process multiple blocks at a + // time, so have bufSizes that are a multiple of blockSize. + + full := len(src) - len(src)%bufSize + if full > 0 { + s.xorKeyStreamBlocks(dst[:full], src[:full]) + } + dst, src = dst[full:], src[full:] + + // If using a multi-block xorKeyStreamBlocks would overflow, use the generic + // one that does one block at a time. + const blocksPerBuf = bufSize / blockSize + if uint64(s.counter)+blocksPerBuf > 1<<32 { + s.buf = [bufSize]byte{} + numBlocks := (len(src) + blockSize - 1) / blockSize + buf := s.buf[bufSize-numBlocks*blockSize:] + copy(buf, src) + s.xorKeyStreamBlocksGeneric(buf, buf) + s.len = len(buf) - copy(dst, buf) + return + } + + // If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and + // keep the leftover keystream for the next XORKeyStream invocation. + if len(src) > 0 { + s.buf = [bufSize]byte{} + copy(s.buf[:], src) + s.xorKeyStreamBlocks(s.buf[:], s.buf[:]) + s.len = bufSize - copy(dst, s.buf[:]) + } +} + +func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) { + if len(dst) != len(src) || len(dst)%blockSize != 0 { + panic("chacha20: internal error: wrong dst and/or src length") + } + + // To generate each block of key stream, the initial cipher state + // (represented below) is passed through 20 rounds of shuffling, + // alternatively applying quarterRounds by columns (like 1, 5, 9, 13) + // or by diagonals (like 1, 6, 11, 12). + // + // 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc + // 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk + // 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk + // 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn + // + // c=constant k=key b=blockcount n=nonce + var ( + c0, c1, c2, c3 = j0, j1, j2, j3 + c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3] + c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7] + _, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.nonce[2] + ) + + // Three quarters of the first round don't depend on the counter, so we can + // calculate them here, and reuse them for multiple blocks in the loop, and + // for future XORKeyStream invocations. + if !s.precompDone { + s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13) + s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14) + s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15) + s.precompDone = true + } + + // A condition of len(src) > 0 would be sufficient, but this also + // acts as a bounds check elimination hint. + for len(src) >= 64 && len(dst) >= 64 { + // The remainder of the first column round. + fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter) + + // The second diagonal round. + x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15) + x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12) + x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13) + x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14) + + // The remaining 18 rounds. + for i := 0; i < 9; i++ { + // Column round. + x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) + x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) + x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) + x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) + + // Diagonal round. + x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) + x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) + x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) + x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) + } + + // Add back the initial state to generate the key stream, then + // XOR the key stream with the source and write out the result. + addXor(dst[0:4], src[0:4], x0, c0) + addXor(dst[4:8], src[4:8], x1, c1) + addXor(dst[8:12], src[8:12], x2, c2) + addXor(dst[12:16], src[12:16], x3, c3) + addXor(dst[16:20], src[16:20], x4, c4) + addXor(dst[20:24], src[20:24], x5, c5) + addXor(dst[24:28], src[24:28], x6, c6) + addXor(dst[28:32], src[28:32], x7, c7) + addXor(dst[32:36], src[32:36], x8, c8) + addXor(dst[36:40], src[36:40], x9, c9) + addXor(dst[40:44], src[40:44], x10, c10) + addXor(dst[44:48], src[44:48], x11, c11) + addXor(dst[48:52], src[48:52], x12, s.counter) + addXor(dst[52:56], src[52:56], x13, c13) + addXor(dst[56:60], src[56:60], x14, c14) + addXor(dst[60:64], src[60:64], x15, c15) + + s.counter += 1 + + src, dst = src[blockSize:], dst[blockSize:] + } +} + +// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes +// key and a 16 bytes nonce. It returns an error if key or nonce have any other +// length. It is used as part of the XChaCha20 construction. +func HChaCha20(key, nonce []byte) ([]byte, error) { + // This function is split into a wrapper so that the slice allocation will + // be inlined, and depending on how the caller uses the return value, won't + // escape to the heap. + out := make([]byte, 32) + return hChaCha20(out, key, nonce) +} + +func hChaCha20(out, key, nonce []byte) ([]byte, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20: wrong HChaCha20 key size") + } + if len(nonce) != 16 { + return nil, errors.New("chacha20: wrong HChaCha20 nonce size") + } + + x0, x1, x2, x3 := j0, j1, j2, j3 + x4 := binary.LittleEndian.Uint32(key[0:4]) + x5 := binary.LittleEndian.Uint32(key[4:8]) + x6 := binary.LittleEndian.Uint32(key[8:12]) + x7 := binary.LittleEndian.Uint32(key[12:16]) + x8 := binary.LittleEndian.Uint32(key[16:20]) + x9 := binary.LittleEndian.Uint32(key[20:24]) + x10 := binary.LittleEndian.Uint32(key[24:28]) + x11 := binary.LittleEndian.Uint32(key[28:32]) + x12 := binary.LittleEndian.Uint32(nonce[0:4]) + x13 := binary.LittleEndian.Uint32(nonce[4:8]) + x14 := binary.LittleEndian.Uint32(nonce[8:12]) + x15 := binary.LittleEndian.Uint32(nonce[12:16]) + + for i := 0; i < 10; i++ { + // Diagonal round. + x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) + x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) + x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) + x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) + + // Column round. + x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) + x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) + x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) + x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) + } + + _ = out[31] // bounds check elimination hint + binary.LittleEndian.PutUint32(out[0:4], x0) + binary.LittleEndian.PutUint32(out[4:8], x1) + binary.LittleEndian.PutUint32(out[8:12], x2) + binary.LittleEndian.PutUint32(out[12:16], x3) + binary.LittleEndian.PutUint32(out[16:20], x12) + binary.LittleEndian.PutUint32(out[20:24], x13) + binary.LittleEndian.PutUint32(out[24:28], x14) + binary.LittleEndian.PutUint32(out[28:32], x15) + return out, nil +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go new file mode 100644 index 000000000..db42e6676 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego + +package chacha20 + +const bufSize = blockSize + +func (s *Cipher) xorKeyStreamBlocks(dst, src []byte) { + s.xorKeyStreamBlocksGeneric(dst, src) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go new file mode 100644 index 000000000..3a4287f99 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package chacha20 + +const bufSize = 256 + +//go:noescape +func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + chaCha20_ctr32_vsx(&dst[0], &src[0], len(src), &c.key, &c.counter) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s new file mode 100644 index 000000000..c672ccf69 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s @@ -0,0 +1,443 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on CRYPTOGAMS code with the following comment: +// # ==================================================================== +// # Written by Andy Polyakov for the OpenSSL +// # project. The module is, however, dual licensed under OpenSSL and +// # CRYPTOGAMS licenses depending on where you obtain it. For further +// # details see http://www.openssl.org/~appro/cryptogams/. +// # ==================================================================== + +// Code for the perl script that generates the ppc64 assembler +// can be found in the cryptogams repository at the link below. It is based on +// the original from openssl. + +// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91 + +// The differences in this and the original implementation are +// due to the calling conventions and initialization of constants. + +//go:build gc && !purego + +#include "textflag.h" + +#define OUT R3 +#define INP R4 +#define LEN R5 +#define KEY R6 +#define CNT R7 +#define TMP R15 + +#define CONSTBASE R16 +#define BLOCKS R17 + +// for VPERMXOR +#define MASK R18 + +DATA consts<>+0x00(SB)/8, $0x3320646e61707865 +DATA consts<>+0x08(SB)/8, $0x6b20657479622d32 +DATA consts<>+0x10(SB)/8, $0x0000000000000001 +DATA consts<>+0x18(SB)/8, $0x0000000000000000 +DATA consts<>+0x20(SB)/8, $0x0000000000000004 +DATA consts<>+0x28(SB)/8, $0x0000000000000000 +DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d +DATA consts<>+0x38(SB)/8, $0x0203000106070405 +DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c +DATA consts<>+0x48(SB)/8, $0x0102030005060704 +DATA consts<>+0x50(SB)/8, $0x6170786561707865 +DATA consts<>+0x58(SB)/8, $0x6170786561707865 +DATA consts<>+0x60(SB)/8, $0x3320646e3320646e +DATA consts<>+0x68(SB)/8, $0x3320646e3320646e +DATA consts<>+0x70(SB)/8, $0x79622d3279622d32 +DATA consts<>+0x78(SB)/8, $0x79622d3279622d32 +DATA consts<>+0x80(SB)/8, $0x6b2065746b206574 +DATA consts<>+0x88(SB)/8, $0x6b2065746b206574 +DATA consts<>+0x90(SB)/8, $0x0000000100000000 +DATA consts<>+0x98(SB)/8, $0x0000000300000002 +DATA consts<>+0xa0(SB)/8, $0x5566774411223300 +DATA consts<>+0xa8(SB)/8, $0xddeeffcc99aabb88 +DATA consts<>+0xb0(SB)/8, $0x6677445522330011 +DATA consts<>+0xb8(SB)/8, $0xeeffccddaabb8899 +GLOBL consts<>(SB), RODATA, $0xc0 + +//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) +TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40 + MOVD out+0(FP), OUT + MOVD inp+8(FP), INP + MOVD len+16(FP), LEN + MOVD key+24(FP), KEY + MOVD counter+32(FP), CNT + + // Addressing for constants + MOVD $consts<>+0x00(SB), CONSTBASE + MOVD $16, R8 + MOVD $32, R9 + MOVD $48, R10 + MOVD $64, R11 + SRD $6, LEN, BLOCKS + // for VPERMXOR + MOVD $consts<>+0xa0(SB), MASK + MOVD $16, R20 + // V16 + LXVW4X (CONSTBASE)(R0), VS48 + ADD $80,CONSTBASE + + // Load key into V17,V18 + LXVW4X (KEY)(R0), VS49 + LXVW4X (KEY)(R8), VS50 + + // Load CNT, NONCE into V19 + LXVW4X (CNT)(R0), VS51 + + // Clear V27 + VXOR V27, V27, V27 + + // V28 + LXVW4X (CONSTBASE)(R11), VS60 + + // Load mask constants for VPERMXOR + LXVW4X (MASK)(R0), V20 + LXVW4X (MASK)(R20), V21 + + // splat slot from V19 -> V26 + VSPLTW $0, V19, V26 + + VSLDOI $4, V19, V27, V19 + VSLDOI $12, V27, V19, V19 + + VADDUWM V26, V28, V26 + + MOVD $10, R14 + MOVD R14, CTR + PCALIGN $16 +loop_outer_vsx: + // V0, V1, V2, V3 + LXVW4X (R0)(CONSTBASE), VS32 + LXVW4X (R8)(CONSTBASE), VS33 + LXVW4X (R9)(CONSTBASE), VS34 + LXVW4X (R10)(CONSTBASE), VS35 + + // splat values from V17, V18 into V4-V11 + VSPLTW $0, V17, V4 + VSPLTW $1, V17, V5 + VSPLTW $2, V17, V6 + VSPLTW $3, V17, V7 + VSPLTW $0, V18, V8 + VSPLTW $1, V18, V9 + VSPLTW $2, V18, V10 + VSPLTW $3, V18, V11 + + // VOR + VOR V26, V26, V12 + + // splat values from V19 -> V13, V14, V15 + VSPLTW $1, V19, V13 + VSPLTW $2, V19, V14 + VSPLTW $3, V19, V15 + + // splat const values + VSPLTISW $-16, V27 + VSPLTISW $12, V28 + VSPLTISW $8, V29 + VSPLTISW $7, V30 + PCALIGN $16 +loop_vsx: + VADDUWM V0, V4, V0 + VADDUWM V1, V5, V1 + VADDUWM V2, V6, V2 + VADDUWM V3, V7, V3 + + VPERMXOR V12, V0, V21, V12 + VPERMXOR V13, V1, V21, V13 + VPERMXOR V14, V2, V21, V14 + VPERMXOR V15, V3, V21, V15 + + VADDUWM V8, V12, V8 + VADDUWM V9, V13, V9 + VADDUWM V10, V14, V10 + VADDUWM V11, V15, V11 + + VXOR V4, V8, V4 + VXOR V5, V9, V5 + VXOR V6, V10, V6 + VXOR V7, V11, V7 + + VRLW V4, V28, V4 + VRLW V5, V28, V5 + VRLW V6, V28, V6 + VRLW V7, V28, V7 + + VADDUWM V0, V4, V0 + VADDUWM V1, V5, V1 + VADDUWM V2, V6, V2 + VADDUWM V3, V7, V3 + + VPERMXOR V12, V0, V20, V12 + VPERMXOR V13, V1, V20, V13 + VPERMXOR V14, V2, V20, V14 + VPERMXOR V15, V3, V20, V15 + + VADDUWM V8, V12, V8 + VADDUWM V9, V13, V9 + VADDUWM V10, V14, V10 + VADDUWM V11, V15, V11 + + VXOR V4, V8, V4 + VXOR V5, V9, V5 + VXOR V6, V10, V6 + VXOR V7, V11, V7 + + VRLW V4, V30, V4 + VRLW V5, V30, V5 + VRLW V6, V30, V6 + VRLW V7, V30, V7 + + VADDUWM V0, V5, V0 + VADDUWM V1, V6, V1 + VADDUWM V2, V7, V2 + VADDUWM V3, V4, V3 + + VPERMXOR V15, V0, V21, V15 + VPERMXOR V12, V1, V21, V12 + VPERMXOR V13, V2, V21, V13 + VPERMXOR V14, V3, V21, V14 + + VADDUWM V10, V15, V10 + VADDUWM V11, V12, V11 + VADDUWM V8, V13, V8 + VADDUWM V9, V14, V9 + + VXOR V5, V10, V5 + VXOR V6, V11, V6 + VXOR V7, V8, V7 + VXOR V4, V9, V4 + + VRLW V5, V28, V5 + VRLW V6, V28, V6 + VRLW V7, V28, V7 + VRLW V4, V28, V4 + + VADDUWM V0, V5, V0 + VADDUWM V1, V6, V1 + VADDUWM V2, V7, V2 + VADDUWM V3, V4, V3 + + VPERMXOR V15, V0, V20, V15 + VPERMXOR V12, V1, V20, V12 + VPERMXOR V13, V2, V20, V13 + VPERMXOR V14, V3, V20, V14 + + VADDUWM V10, V15, V10 + VADDUWM V11, V12, V11 + VADDUWM V8, V13, V8 + VADDUWM V9, V14, V9 + + VXOR V5, V10, V5 + VXOR V6, V11, V6 + VXOR V7, V8, V7 + VXOR V4, V9, V4 + + VRLW V5, V30, V5 + VRLW V6, V30, V6 + VRLW V7, V30, V7 + VRLW V4, V30, V4 + BDNZ loop_vsx + + VADDUWM V12, V26, V12 + + VMRGEW V0, V1, V27 + VMRGEW V2, V3, V28 + + VMRGOW V0, V1, V0 + VMRGOW V2, V3, V2 + + VMRGEW V4, V5, V29 + VMRGEW V6, V7, V30 + + XXPERMDI VS32, VS34, $0, VS33 + XXPERMDI VS32, VS34, $3, VS35 + XXPERMDI VS59, VS60, $0, VS32 + XXPERMDI VS59, VS60, $3, VS34 + + VMRGOW V4, V5, V4 + VMRGOW V6, V7, V6 + + VMRGEW V8, V9, V27 + VMRGEW V10, V11, V28 + + XXPERMDI VS36, VS38, $0, VS37 + XXPERMDI VS36, VS38, $3, VS39 + XXPERMDI VS61, VS62, $0, VS36 + XXPERMDI VS61, VS62, $3, VS38 + + VMRGOW V8, V9, V8 + VMRGOW V10, V11, V10 + + VMRGEW V12, V13, V29 + VMRGEW V14, V15, V30 + + XXPERMDI VS40, VS42, $0, VS41 + XXPERMDI VS40, VS42, $3, VS43 + XXPERMDI VS59, VS60, $0, VS40 + XXPERMDI VS59, VS60, $3, VS42 + + VMRGOW V12, V13, V12 + VMRGOW V14, V15, V14 + + VSPLTISW $4, V27 + VADDUWM V26, V27, V26 + + XXPERMDI VS44, VS46, $0, VS45 + XXPERMDI VS44, VS46, $3, VS47 + XXPERMDI VS61, VS62, $0, VS44 + XXPERMDI VS61, VS62, $3, VS46 + + VADDUWM V0, V16, V0 + VADDUWM V4, V17, V4 + VADDUWM V8, V18, V8 + VADDUWM V12, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + // Bottom of loop + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V1, V16, V0 + VADDUWM V5, V17, V4 + VADDUWM V9, V18, V8 + VADDUWM V13, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + VXOR V27, V0, V27 + + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(V10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V2, V16, V0 + VADDUWM V6, V17, V4 + VADDUWM V10, V18, V8 + VADDUWM V14, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V3, V16, V0 + VADDUWM V7, V17, V4 + VADDUWM V11, V18, V8 + VADDUWM V15, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + + MOVD $10, R14 + MOVD R14, CTR + BNE loop_outer_vsx + +done_vsx: + // Increment counter by number of 64 byte blocks + MOVD (CNT), R14 + ADD BLOCKS, R14 + MOVD R14, (CNT) + RET + +tail_vsx: + ADD $32, R1, R11 + MOVD LEN, CTR + + // Save values on stack to copy from + STXVW4X VS32, (R11)(R0) + STXVW4X VS36, (R11)(R8) + STXVW4X VS40, (R11)(R9) + STXVW4X VS44, (R11)(R10) + ADD $-1, R11, R12 + ADD $-1, INP + ADD $-1, OUT + PCALIGN $16 +looptail_vsx: + // Copying the result to OUT + // in bytes. + MOVBZU 1(R12), KEY + MOVBZU 1(INP), TMP + XOR KEY, TMP, KEY + MOVBU KEY, 1(OUT) + BDNZ looptail_vsx + + // Clear the stack values + STXVW4X VS48, (R11)(R0) + STXVW4X VS48, (R11)(R8) + STXVW4X VS48, (R11)(R9) + STXVW4X VS48, (R11)(R10) + BR done_vsx diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go new file mode 100644 index 000000000..683ccfd1c --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package chacha20 + +import "golang.org/x/sys/cpu" + +var haveAsm = cpu.S390X.HasVX + +const bufSize = 256 + +// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only +// be called when the vector facility is available. Implementation in asm_s390x.s. +// +//go:noescape +func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + if cpu.S390X.HasVX { + xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) + } else { + c.xorKeyStreamBlocksGeneric(dst, src) + } +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s new file mode 100644 index 000000000..1eda91a3d --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s @@ -0,0 +1,224 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#include "go_asm.h" +#include "textflag.h" + +// This is an implementation of the ChaCha20 encryption algorithm as +// specified in RFC 7539. It uses vector instructions to compute +// 4 keystream blocks in parallel (256 bytes) which are then XORed +// with the bytes in the input slice. + +GLOBL ·constants<>(SB), RODATA|NOPTR, $32 +// BSWAP: swap bytes in each 4-byte element +DATA ·constants<>+0x00(SB)/4, $0x03020100 +DATA ·constants<>+0x04(SB)/4, $0x07060504 +DATA ·constants<>+0x08(SB)/4, $0x0b0a0908 +DATA ·constants<>+0x0c(SB)/4, $0x0f0e0d0c +// J0: [j0, j1, j2, j3] +DATA ·constants<>+0x10(SB)/4, $0x61707865 +DATA ·constants<>+0x14(SB)/4, $0x3320646e +DATA ·constants<>+0x18(SB)/4, $0x79622d32 +DATA ·constants<>+0x1c(SB)/4, $0x6b206574 + +#define BSWAP V5 +#define J0 V6 +#define KEY0 V7 +#define KEY1 V8 +#define NONCE V9 +#define CTR V10 +#define M0 V11 +#define M1 V12 +#define M2 V13 +#define M3 V14 +#define INC V15 +#define X0 V16 +#define X1 V17 +#define X2 V18 +#define X3 V19 +#define X4 V20 +#define X5 V21 +#define X6 V22 +#define X7 V23 +#define X8 V24 +#define X9 V25 +#define X10 V26 +#define X11 V27 +#define X12 V28 +#define X13 V29 +#define X14 V30 +#define X15 V31 + +#define NUM_ROUNDS 20 + +#define ROUND4(a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3, d0, d1, d2, d3) \ + VAF a1, a0, a0 \ + VAF b1, b0, b0 \ + VAF c1, c0, c0 \ + VAF d1, d0, d0 \ + VX a0, a2, a2 \ + VX b0, b2, b2 \ + VX c0, c2, c2 \ + VX d0, d2, d2 \ + VERLLF $16, a2, a2 \ + VERLLF $16, b2, b2 \ + VERLLF $16, c2, c2 \ + VERLLF $16, d2, d2 \ + VAF a2, a3, a3 \ + VAF b2, b3, b3 \ + VAF c2, c3, c3 \ + VAF d2, d3, d3 \ + VX a3, a1, a1 \ + VX b3, b1, b1 \ + VX c3, c1, c1 \ + VX d3, d1, d1 \ + VERLLF $12, a1, a1 \ + VERLLF $12, b1, b1 \ + VERLLF $12, c1, c1 \ + VERLLF $12, d1, d1 \ + VAF a1, a0, a0 \ + VAF b1, b0, b0 \ + VAF c1, c0, c0 \ + VAF d1, d0, d0 \ + VX a0, a2, a2 \ + VX b0, b2, b2 \ + VX c0, c2, c2 \ + VX d0, d2, d2 \ + VERLLF $8, a2, a2 \ + VERLLF $8, b2, b2 \ + VERLLF $8, c2, c2 \ + VERLLF $8, d2, d2 \ + VAF a2, a3, a3 \ + VAF b2, b3, b3 \ + VAF c2, c3, c3 \ + VAF d2, d3, d3 \ + VX a3, a1, a1 \ + VX b3, b1, b1 \ + VX c3, c1, c1 \ + VX d3, d1, d1 \ + VERLLF $7, a1, a1 \ + VERLLF $7, b1, b1 \ + VERLLF $7, c1, c1 \ + VERLLF $7, d1, d1 + +#define PERMUTE(mask, v0, v1, v2, v3) \ + VPERM v0, v0, mask, v0 \ + VPERM v1, v1, mask, v1 \ + VPERM v2, v2, mask, v2 \ + VPERM v3, v3, mask, v3 + +#define ADDV(x, v0, v1, v2, v3) \ + VAF x, v0, v0 \ + VAF x, v1, v1 \ + VAF x, v2, v2 \ + VAF x, v3, v3 + +#define XORV(off, dst, src, v0, v1, v2, v3) \ + VLM off(src), M0, M3 \ + PERMUTE(BSWAP, v0, v1, v2, v3) \ + VX v0, M0, M0 \ + VX v1, M1, M1 \ + VX v2, M2, M2 \ + VX v3, M3, M3 \ + VSTM M0, M3, off(dst) + +#define SHUFFLE(a, b, c, d, t, u, v, w) \ + VMRHF a, c, t \ // t = {a[0], c[0], a[1], c[1]} + VMRHF b, d, u \ // u = {b[0], d[0], b[1], d[1]} + VMRLF a, c, v \ // v = {a[2], c[2], a[3], c[3]} + VMRLF b, d, w \ // w = {b[2], d[2], b[3], d[3]} + VMRHF t, u, a \ // a = {a[0], b[0], c[0], d[0]} + VMRLF t, u, b \ // b = {a[1], b[1], c[1], d[1]} + VMRHF v, w, c \ // c = {a[2], b[2], c[2], d[2]} + VMRLF v, w, d // d = {a[3], b[3], c[3], d[3]} + +// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) +TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 + MOVD $·constants<>(SB), R1 + MOVD dst+0(FP), R2 // R2=&dst[0] + LMG src+24(FP), R3, R4 // R3=&src[0] R4=len(src) + MOVD key+48(FP), R5 // R5=key + MOVD nonce+56(FP), R6 // R6=nonce + MOVD counter+64(FP), R7 // R7=counter + + // load BSWAP and J0 + VLM (R1), BSWAP, J0 + + // setup + MOVD $95, R0 + VLM (R5), KEY0, KEY1 + VLL R0, (R6), NONCE + VZERO M0 + VLEIB $7, $32, M0 + VSRLB M0, NONCE, NONCE + + // initialize counter values + VLREPF (R7), CTR + VZERO INC + VLEIF $1, $1, INC + VLEIF $2, $2, INC + VLEIF $3, $3, INC + VAF INC, CTR, CTR + VREPIF $4, INC + +chacha: + VREPF $0, J0, X0 + VREPF $1, J0, X1 + VREPF $2, J0, X2 + VREPF $3, J0, X3 + VREPF $0, KEY0, X4 + VREPF $1, KEY0, X5 + VREPF $2, KEY0, X6 + VREPF $3, KEY0, X7 + VREPF $0, KEY1, X8 + VREPF $1, KEY1, X9 + VREPF $2, KEY1, X10 + VREPF $3, KEY1, X11 + VLR CTR, X12 + VREPF $1, NONCE, X13 + VREPF $2, NONCE, X14 + VREPF $3, NONCE, X15 + + MOVD $(NUM_ROUNDS/2), R1 + +loop: + ROUND4(X0, X4, X12, X8, X1, X5, X13, X9, X2, X6, X14, X10, X3, X7, X15, X11) + ROUND4(X0, X5, X15, X10, X1, X6, X12, X11, X2, X7, X13, X8, X3, X4, X14, X9) + + ADD $-1, R1 + BNE loop + + // decrement length + ADD $-256, R4 + + // rearrange vectors + SHUFFLE(X0, X1, X2, X3, M0, M1, M2, M3) + ADDV(J0, X0, X1, X2, X3) + SHUFFLE(X4, X5, X6, X7, M0, M1, M2, M3) + ADDV(KEY0, X4, X5, X6, X7) + SHUFFLE(X8, X9, X10, X11, M0, M1, M2, M3) + ADDV(KEY1, X8, X9, X10, X11) + VAF CTR, X12, X12 + SHUFFLE(X12, X13, X14, X15, M0, M1, M2, M3) + ADDV(NONCE, X12, X13, X14, X15) + + // increment counters + VAF INC, CTR, CTR + + // xor keystream with plaintext + XORV(0*64, R2, R3, X0, X4, X8, X12) + XORV(1*64, R2, R3, X1, X5, X9, X13) + XORV(2*64, R2, R3, X2, X6, X10, X14) + XORV(3*64, R2, R3, X3, X7, X11, X15) + + // increment pointers + MOVD $256(R2), R2 + MOVD $256(R3), R3 + + CMPBNE R4, $0, chacha + + VSTEF $0, CTR, (R7) + RET diff --git a/vendor/golang.org/x/crypto/chacha20/xor.go b/vendor/golang.org/x/crypto/chacha20/xor.go new file mode 100644 index 000000000..c2d04851e --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/xor.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found src the LICENSE file. + +package chacha20 + +import "runtime" + +// Platforms that have fast unaligned 32-bit little endian accesses. +const unaligned = runtime.GOARCH == "386" || + runtime.GOARCH == "amd64" || + runtime.GOARCH == "arm64" || + runtime.GOARCH == "ppc64le" || + runtime.GOARCH == "s390x" + +// addXor reads a little endian uint32 from src, XORs it with (a + b) and +// places the result in little endian byte order in dst. +func addXor(dst, src []byte, a, b uint32) { + _, _ = src[3], dst[3] // bounds check elimination hint + if unaligned { + // The compiler should optimize this code into + // 32-bit unaligned little endian loads and stores. + // TODO: delete once the compiler does a reliably + // good job with the generic code below. + // See issue #25111 for more details. + v := uint32(src[0]) + v |= uint32(src[1]) << 8 + v |= uint32(src[2]) << 16 + v |= uint32(src[3]) << 24 + v ^= a + b + dst[0] = byte(v) + dst[1] = byte(v >> 8) + dst[2] = byte(v >> 16) + dst[3] = byte(v >> 24) + } else { + a += b + dst[0] = src[0] ^ byte(a) + dst[1] = src[1] ^ byte(a>>8) + dst[2] = src[2] ^ byte(a>>16) + dst[3] = src[3] ^ byte(a>>24) + } +} diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go new file mode 100644 index 000000000..93da7322b --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go @@ -0,0 +1,98 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its +// extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and +// draft-irtf-cfrg-xchacha-01. +package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305" + +import ( + "crypto/cipher" + "errors" +) + +const ( + // KeySize is the size of the key used by this AEAD, in bytes. + KeySize = 32 + + // NonceSize is the size of the nonce used with the standard variant of this + // AEAD, in bytes. + // + // Note that this is too short to be safely generated at random if the same + // key is reused more than 2³² times. + NonceSize = 12 + + // NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305 + // variant of this AEAD, in bytes. + NonceSizeX = 24 + + // Overhead is the size of the Poly1305 authentication tag, and the + // difference between a ciphertext length and its plaintext. + Overhead = 16 +) + +type chacha20poly1305 struct { + key [KeySize]byte +} + +// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key. +func New(key []byte) (cipher.AEAD, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20poly1305: bad key length") + } + ret := new(chacha20poly1305) + copy(ret.key[:], key) + return ret, nil +} + +func (c *chacha20poly1305) NonceSize() int { + return NonceSize +} + +func (c *chacha20poly1305) Overhead() int { + return Overhead +} + +func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != NonceSize { + panic("chacha20poly1305: bad nonce length passed to Seal") + } + + if uint64(len(plaintext)) > (1<<38)-64 { + panic("chacha20poly1305: plaintext too large") + } + + return c.seal(dst, nonce, plaintext, additionalData) +} + +var errOpen = errors.New("chacha20poly1305: message authentication failed") + +func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != NonceSize { + panic("chacha20poly1305: bad nonce length passed to Open") + } + if len(ciphertext) < 16 { + return nil, errOpen + } + if uint64(len(ciphertext)) > (1<<38)-48 { + panic("chacha20poly1305: ciphertext too large") + } + + return c.open(dst, nonce, ciphertext, additionalData) +} + +// sliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go new file mode 100644 index 000000000..50695a14f --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go @@ -0,0 +1,86 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package chacha20poly1305 + +import ( + "encoding/binary" + + "golang.org/x/crypto/internal/alias" + "golang.org/x/sys/cpu" +) + +//go:noescape +func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool + +//go:noescape +func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte) + +var ( + useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2 +) + +// setupState writes a ChaCha20 input matrix to state. See +// https://tools.ietf.org/html/rfc7539#section-2.3. +func setupState(state *[16]uint32, key *[32]byte, nonce []byte) { + state[0] = 0x61707865 + state[1] = 0x3320646e + state[2] = 0x79622d32 + state[3] = 0x6b206574 + + state[4] = binary.LittleEndian.Uint32(key[0:4]) + state[5] = binary.LittleEndian.Uint32(key[4:8]) + state[6] = binary.LittleEndian.Uint32(key[8:12]) + state[7] = binary.LittleEndian.Uint32(key[12:16]) + state[8] = binary.LittleEndian.Uint32(key[16:20]) + state[9] = binary.LittleEndian.Uint32(key[20:24]) + state[10] = binary.LittleEndian.Uint32(key[24:28]) + state[11] = binary.LittleEndian.Uint32(key[28:32]) + + state[12] = 0 + state[13] = binary.LittleEndian.Uint32(nonce[0:4]) + state[14] = binary.LittleEndian.Uint32(nonce[4:8]) + state[15] = binary.LittleEndian.Uint32(nonce[8:12]) +} + +func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { + if !cpu.X86.HasSSSE3 { + return c.sealGeneric(dst, nonce, plaintext, additionalData) + } + + var state [16]uint32 + setupState(&state, &c.key, nonce) + + ret, out := sliceForAppend(dst, len(plaintext)+16) + if alias.InexactOverlap(out, plaintext) { + panic("chacha20poly1305: invalid buffer overlap") + } + chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData) + return ret +} + +func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if !cpu.X86.HasSSSE3 { + return c.openGeneric(dst, nonce, ciphertext, additionalData) + } + + var state [16]uint32 + setupState(&state, &c.key, nonce) + + ciphertext = ciphertext[:len(ciphertext)-16] + ret, out := sliceForAppend(dst, len(ciphertext)) + if alias.InexactOverlap(out, ciphertext) { + panic("chacha20poly1305: invalid buffer overlap") + } + if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) { + for i := range out { + out[i] = 0 + } + return nil, errOpen + } + + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s new file mode 100644 index 000000000..731d2ac6d --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s @@ -0,0 +1,2715 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare. + +//go:build gc && !purego + +#include "textflag.h" +// General register allocation +#define oup DI +#define inp SI +#define inl BX +#define adp CX // free to reuse, after we hash the additional data +#define keyp R8 // free to reuse, when we copy the key to stack +#define itr2 R9 // general iterator +#define itr1 CX // general iterator +#define acc0 R10 +#define acc1 R11 +#define acc2 R12 +#define t0 R13 +#define t1 R14 +#define t2 R15 +#define t3 R8 +// Register and stack allocation for the SSE code +#define rStore (0*16)(BP) +#define sStore (1*16)(BP) +#define state1Store (2*16)(BP) +#define state2Store (3*16)(BP) +#define tmpStore (4*16)(BP) +#define ctr0Store (5*16)(BP) +#define ctr1Store (6*16)(BP) +#define ctr2Store (7*16)(BP) +#define ctr3Store (8*16)(BP) +#define A0 X0 +#define A1 X1 +#define A2 X2 +#define B0 X3 +#define B1 X4 +#define B2 X5 +#define C0 X6 +#define C1 X7 +#define C2 X8 +#define D0 X9 +#define D1 X10 +#define D2 X11 +#define T0 X12 +#define T1 X13 +#define T2 X14 +#define T3 X15 +#define A3 T0 +#define B3 T1 +#define C3 T2 +#define D3 T3 +// Register and stack allocation for the AVX2 code +#define rsStoreAVX2 (0*32)(BP) +#define state1StoreAVX2 (1*32)(BP) +#define state2StoreAVX2 (2*32)(BP) +#define ctr0StoreAVX2 (3*32)(BP) +#define ctr1StoreAVX2 (4*32)(BP) +#define ctr2StoreAVX2 (5*32)(BP) +#define ctr3StoreAVX2 (6*32)(BP) +#define tmpStoreAVX2 (7*32)(BP) // 256 bytes on stack +#define AA0 Y0 +#define AA1 Y5 +#define AA2 Y6 +#define AA3 Y7 +#define BB0 Y14 +#define BB1 Y9 +#define BB2 Y10 +#define BB3 Y11 +#define CC0 Y12 +#define CC1 Y13 +#define CC2 Y8 +#define CC3 Y15 +#define DD0 Y4 +#define DD1 Y1 +#define DD2 Y2 +#define DD3 Y3 +#define TT0 DD3 +#define TT1 AA3 +#define TT2 BB3 +#define TT3 CC3 +// ChaCha20 constants +DATA ·chacha20Constants<>+0x00(SB)/4, $0x61707865 +DATA ·chacha20Constants<>+0x04(SB)/4, $0x3320646e +DATA ·chacha20Constants<>+0x08(SB)/4, $0x79622d32 +DATA ·chacha20Constants<>+0x0c(SB)/4, $0x6b206574 +DATA ·chacha20Constants<>+0x10(SB)/4, $0x61707865 +DATA ·chacha20Constants<>+0x14(SB)/4, $0x3320646e +DATA ·chacha20Constants<>+0x18(SB)/4, $0x79622d32 +DATA ·chacha20Constants<>+0x1c(SB)/4, $0x6b206574 +// <<< 16 with PSHUFB +DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302 +DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A +DATA ·rol16<>+0x10(SB)/8, $0x0504070601000302 +DATA ·rol16<>+0x18(SB)/8, $0x0D0C0F0E09080B0A +// <<< 8 with PSHUFB +DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003 +DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B +DATA ·rol8<>+0x10(SB)/8, $0x0605040702010003 +DATA ·rol8<>+0x18(SB)/8, $0x0E0D0C0F0A09080B + +DATA ·avx2InitMask<>+0x00(SB)/8, $0x0 +DATA ·avx2InitMask<>+0x08(SB)/8, $0x0 +DATA ·avx2InitMask<>+0x10(SB)/8, $0x1 +DATA ·avx2InitMask<>+0x18(SB)/8, $0x0 + +DATA ·avx2IncMask<>+0x00(SB)/8, $0x2 +DATA ·avx2IncMask<>+0x08(SB)/8, $0x0 +DATA ·avx2IncMask<>+0x10(SB)/8, $0x2 +DATA ·avx2IncMask<>+0x18(SB)/8, $0x0 +// Poly1305 key clamp +DATA ·polyClampMask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF +DATA ·polyClampMask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC +DATA ·polyClampMask<>+0x10(SB)/8, $0xFFFFFFFFFFFFFFFF +DATA ·polyClampMask<>+0x18(SB)/8, $0xFFFFFFFFFFFFFFFF + +DATA ·sseIncMask<>+0x00(SB)/8, $0x1 +DATA ·sseIncMask<>+0x08(SB)/8, $0x0 +// To load/store the last < 16 bytes in a buffer +DATA ·andMask<>+0x00(SB)/8, $0x00000000000000ff +DATA ·andMask<>+0x08(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x10(SB)/8, $0x000000000000ffff +DATA ·andMask<>+0x18(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x20(SB)/8, $0x0000000000ffffff +DATA ·andMask<>+0x28(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x30(SB)/8, $0x00000000ffffffff +DATA ·andMask<>+0x38(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x40(SB)/8, $0x000000ffffffffff +DATA ·andMask<>+0x48(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x50(SB)/8, $0x0000ffffffffffff +DATA ·andMask<>+0x58(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x60(SB)/8, $0x00ffffffffffffff +DATA ·andMask<>+0x68(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x70(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0x78(SB)/8, $0x0000000000000000 +DATA ·andMask<>+0x80(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0x88(SB)/8, $0x00000000000000ff +DATA ·andMask<>+0x90(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0x98(SB)/8, $0x000000000000ffff +DATA ·andMask<>+0xa0(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0xa8(SB)/8, $0x0000000000ffffff +DATA ·andMask<>+0xb0(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0xb8(SB)/8, $0x00000000ffffffff +DATA ·andMask<>+0xc0(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0xc8(SB)/8, $0x000000ffffffffff +DATA ·andMask<>+0xd0(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0xd8(SB)/8, $0x0000ffffffffffff +DATA ·andMask<>+0xe0(SB)/8, $0xffffffffffffffff +DATA ·andMask<>+0xe8(SB)/8, $0x00ffffffffffffff + +GLOBL ·chacha20Constants<>(SB), (NOPTR+RODATA), $32 +GLOBL ·rol16<>(SB), (NOPTR+RODATA), $32 +GLOBL ·rol8<>(SB), (NOPTR+RODATA), $32 +GLOBL ·sseIncMask<>(SB), (NOPTR+RODATA), $16 +GLOBL ·avx2IncMask<>(SB), (NOPTR+RODATA), $32 +GLOBL ·avx2InitMask<>(SB), (NOPTR+RODATA), $32 +GLOBL ·polyClampMask<>(SB), (NOPTR+RODATA), $32 +GLOBL ·andMask<>(SB), (NOPTR+RODATA), $240 +// No PALIGNR in Go ASM yet (but VPALIGNR is present). +#define shiftB0Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X3, X3 +#define shiftB1Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xe4; BYTE $0x04 // PALIGNR $4, X4, X4 +#define shiftB2Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x04 // PALIGNR $4, X5, X5 +#define shiftB3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x04 // PALIGNR $4, X13, X13 +#define shiftC0Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xf6; BYTE $0x08 // PALIGNR $8, X6, X6 +#define shiftC1Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x08 // PALIGNR $8, X7, X7 +#define shiftC2Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc0; BYTE $0x08 // PALIGNR $8, X8, X8 +#define shiftC3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xf6; BYTE $0x08 // PALIGNR $8, X14, X14 +#define shiftD0Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc9; BYTE $0x0c // PALIGNR $12, X9, X9 +#define shiftD1Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x0c // PALIGNR $12, X10, X10 +#define shiftD2Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x0c // PALIGNR $12, X11, X11 +#define shiftD3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x0c // PALIGNR $12, X15, X15 +#define shiftB0Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x0c // PALIGNR $12, X3, X3 +#define shiftB1Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xe4; BYTE $0x0c // PALIGNR $12, X4, X4 +#define shiftB2Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x0c // PALIGNR $12, X5, X5 +#define shiftB3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x0c // PALIGNR $12, X13, X13 +#define shiftC0Right shiftC0Left +#define shiftC1Right shiftC1Left +#define shiftC2Right shiftC2Left +#define shiftC3Right shiftC3Left +#define shiftD0Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc9; BYTE $0x04 // PALIGNR $4, X9, X9 +#define shiftD1Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x04 // PALIGNR $4, X10, X10 +#define shiftD2Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X11, X11 +#define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15 + +// Some macros + +// ROL rotates the uint32s in register R left by N bits, using temporary T. +#define ROL(N, R, T) \ + MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R + +// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL16(R, T) PSHUFB ·rol16<>(SB), R +#else +#define ROL16(R, T) ROL(16, R, T) +#endif + +// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL8(R, T) PSHUFB ·rol8<>(SB), R +#else +#define ROL8(R, T) ROL(8, R, T) +#endif + +#define chachaQR(A, B, C, D, T) \ + PADDD B, A; PXOR A, D; ROL16(D, T) \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B \ + PADDD B, A; PXOR A, D; ROL8(D, T) \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B + +#define chachaQR_AVX2(A, B, C, D, T) \ + VPADDD B, A, A; VPXOR A, D, D; VPSHUFB ·rol16<>(SB), D, D \ + VPADDD D, C, C; VPXOR C, B, B; VPSLLD $12, B, T; VPSRLD $20, B, B; VPXOR T, B, B \ + VPADDD B, A, A; VPXOR A, D, D; VPSHUFB ·rol8<>(SB), D, D \ + VPADDD D, C, C; VPXOR C, B, B; VPSLLD $7, B, T; VPSRLD $25, B, B; VPXOR T, B, B + +#define polyAdd(S) ADDQ S, acc0; ADCQ 8+S, acc1; ADCQ $1, acc2 +#define polyMulStage1 MOVQ (0*8)(BP), AX; MOVQ AX, t2; MULQ acc0; MOVQ AX, t0; MOVQ DX, t1; MOVQ (0*8)(BP), AX; MULQ acc1; IMULQ acc2, t2; ADDQ AX, t1; ADCQ DX, t2 +#define polyMulStage2 MOVQ (1*8)(BP), AX; MOVQ AX, t3; MULQ acc0; ADDQ AX, t1; ADCQ $0, DX; MOVQ DX, acc0; MOVQ (1*8)(BP), AX; MULQ acc1; ADDQ AX, t2; ADCQ $0, DX +#define polyMulStage3 IMULQ acc2, t3; ADDQ acc0, t2; ADCQ DX, t3 +#define polyMulReduceStage MOVQ t0, acc0; MOVQ t1, acc1; MOVQ t2, acc2; ANDQ $3, acc2; MOVQ t2, t0; ANDQ $-4, t0; MOVQ t3, t1; SHRQ $2, t3, t2; SHRQ $2, t3; ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $0, acc2; ADDQ t2, acc0; ADCQ t3, acc1; ADCQ $0, acc2 + +#define polyMulStage1_AVX2 MOVQ (0*8)(BP), DX; MOVQ DX, t2; MULXQ acc0, t0, t1; IMULQ acc2, t2; MULXQ acc1, AX, DX; ADDQ AX, t1; ADCQ DX, t2 +#define polyMulStage2_AVX2 MOVQ (1*8)(BP), DX; MULXQ acc0, acc0, AX; ADDQ acc0, t1; MULXQ acc1, acc1, t3; ADCQ acc1, t2; ADCQ $0, t3 +#define polyMulStage3_AVX2 IMULQ acc2, DX; ADDQ AX, t2; ADCQ DX, t3 + +#define polyMul polyMulStage1; polyMulStage2; polyMulStage3; polyMulReduceStage +#define polyMulAVX2 polyMulStage1_AVX2; polyMulStage2_AVX2; polyMulStage3_AVX2; polyMulReduceStage +// ---------------------------------------------------------------------------- +TEXT polyHashADInternal<>(SB), NOSPLIT, $0 + // adp points to beginning of additional data + // itr2 holds ad length + XORQ acc0, acc0 + XORQ acc1, acc1 + XORQ acc2, acc2 + CMPQ itr2, $13 + JNE hashADLoop + +openFastTLSAD: + // Special treatment for the TLS case of 13 bytes + MOVQ (adp), acc0 + MOVQ 5(adp), acc1 + SHRQ $24, acc1 + MOVQ $1, acc2 + polyMul + RET + +hashADLoop: + // Hash in 16 byte chunks + CMPQ itr2, $16 + JB hashADTail + polyAdd(0(adp)) + LEAQ (1*16)(adp), adp + SUBQ $16, itr2 + polyMul + JMP hashADLoop + +hashADTail: + CMPQ itr2, $0 + JE hashADDone + + // Hash last < 16 byte tail + XORQ t0, t0 + XORQ t1, t1 + XORQ t2, t2 + ADDQ itr2, adp + +hashADTailLoop: + SHLQ $8, t0, t1 + SHLQ $8, t0 + MOVB -1(adp), t2 + XORQ t2, t0 + DECQ adp + DECQ itr2 + JNE hashADTailLoop + +hashADTailFinish: + ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2 + polyMul + + // Finished AD +hashADDone: + RET + +// ---------------------------------------------------------------------------- +// func chacha20Poly1305Open(dst, key, src, ad []byte) bool +TEXT ·chacha20Poly1305Open(SB), 0, $288-97 + // For aligned stack access + MOVQ SP, BP + ADDQ $32, BP + ANDQ $-32, BP + MOVQ dst+0(FP), oup + MOVQ key+24(FP), keyp + MOVQ src+48(FP), inp + MOVQ src_len+56(FP), inl + MOVQ ad+72(FP), adp + + // Check for AVX2 support + CMPB ·useAVX2(SB), $1 + JE chacha20Poly1305Open_AVX2 + + // Special optimization, for very short buffers + CMPQ inl, $128 + JBE openSSE128 // About 16% faster + + // For long buffers, prepare the poly key first + MOVOU ·chacha20Constants<>(SB), A0 + MOVOU (1*16)(keyp), B0 + MOVOU (2*16)(keyp), C0 + MOVOU (3*16)(keyp), D0 + MOVO D0, T1 + + // Store state on stack for future use + MOVO B0, state1Store + MOVO C0, state2Store + MOVO D0, ctr3Store + MOVQ $10, itr2 + +openSSEPreparePolyKey: + chachaQR(A0, B0, C0, D0, T0) + shiftB0Left; shiftC0Left; shiftD0Left + chachaQR(A0, B0, C0, D0, T0) + shiftB0Right; shiftC0Right; shiftD0Right + DECQ itr2 + JNE openSSEPreparePolyKey + + // A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded + PADDL ·chacha20Constants<>(SB), A0; PADDL state1Store, B0 + + // Clamp and store the key + PAND ·polyClampMask<>(SB), A0 + MOVO A0, rStore; MOVO B0, sStore + + // Hash AAD + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + +openSSEMainLoop: + CMPQ inl, $256 + JB openSSEMainLoopDone + + // Load state, increment counter blocks + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0 + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3 + + // Store counters + MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store + + // There are 10 ChaCha20 iterations of 2QR each, so for 6 iterations we hash 2 blocks, and for the remaining 4 only 1 block - for a total of 16 + MOVQ $4, itr1 + MOVQ inp, itr2 + +openSSEInternalLoop: + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + polyAdd(0(itr2)) + shiftB0Left; shiftB1Left; shiftB2Left; shiftB3Left + shiftC0Left; shiftC1Left; shiftC2Left; shiftC3Left + shiftD0Left; shiftD1Left; shiftD2Left; shiftD3Left + polyMulStage1 + polyMulStage2 + LEAQ (2*8)(itr2), itr2 + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + polyMulStage3 + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + polyMulReduceStage + shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right + shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right + shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right + DECQ itr1 + JGE openSSEInternalLoop + + polyAdd(0(itr2)) + polyMul + LEAQ (2*8)(itr2), itr2 + + CMPQ itr1, $-6 + JG openSSEInternalLoop + + // Add in the state + PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3 + PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3 + PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3 + PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3 + + // Load - xor - store + MOVO D3, tmpStore + MOVOU (0*16)(inp), D3; PXOR D3, A0; MOVOU A0, (0*16)(oup) + MOVOU (1*16)(inp), D3; PXOR D3, B0; MOVOU B0, (1*16)(oup) + MOVOU (2*16)(inp), D3; PXOR D3, C0; MOVOU C0, (2*16)(oup) + MOVOU (3*16)(inp), D3; PXOR D3, D0; MOVOU D0, (3*16)(oup) + MOVOU (4*16)(inp), D0; PXOR D0, A1; MOVOU A1, (4*16)(oup) + MOVOU (5*16)(inp), D0; PXOR D0, B1; MOVOU B1, (5*16)(oup) + MOVOU (6*16)(inp), D0; PXOR D0, C1; MOVOU C1, (6*16)(oup) + MOVOU (7*16)(inp), D0; PXOR D0, D1; MOVOU D1, (7*16)(oup) + MOVOU (8*16)(inp), D0; PXOR D0, A2; MOVOU A2, (8*16)(oup) + MOVOU (9*16)(inp), D0; PXOR D0, B2; MOVOU B2, (9*16)(oup) + MOVOU (10*16)(inp), D0; PXOR D0, C2; MOVOU C2, (10*16)(oup) + MOVOU (11*16)(inp), D0; PXOR D0, D2; MOVOU D2, (11*16)(oup) + MOVOU (12*16)(inp), D0; PXOR D0, A3; MOVOU A3, (12*16)(oup) + MOVOU (13*16)(inp), D0; PXOR D0, B3; MOVOU B3, (13*16)(oup) + MOVOU (14*16)(inp), D0; PXOR D0, C3; MOVOU C3, (14*16)(oup) + MOVOU (15*16)(inp), D0; PXOR tmpStore, D0; MOVOU D0, (15*16)(oup) + LEAQ 256(inp), inp + LEAQ 256(oup), oup + SUBQ $256, inl + JMP openSSEMainLoop + +openSSEMainLoopDone: + // Handle the various tail sizes efficiently + TESTQ inl, inl + JE openSSEFinalize + CMPQ inl, $64 + JBE openSSETail64 + CMPQ inl, $128 + JBE openSSETail128 + CMPQ inl, $192 + JBE openSSETail192 + JMP openSSETail256 + +openSSEFinalize: + // Hash in the PT, AAD lengths + ADDQ ad_len+80(FP), acc0; ADCQ src_len+56(FP), acc1; ADCQ $1, acc2 + polyMul + + // Final reduce + MOVQ acc0, t0 + MOVQ acc1, t1 + MOVQ acc2, t2 + SUBQ $-5, acc0 + SBBQ $-1, acc1 + SBBQ $3, acc2 + CMOVQCS t0, acc0 + CMOVQCS t1, acc1 + CMOVQCS t2, acc2 + + // Add in the "s" part of the key + ADDQ 0+sStore, acc0 + ADCQ 8+sStore, acc1 + + // Finally, constant time compare to the tag at the end of the message + XORQ AX, AX + MOVQ $1, DX + XORQ (0*8)(inp), acc0 + XORQ (1*8)(inp), acc1 + ORQ acc1, acc0 + CMOVQEQ DX, AX + + // Return true iff tags are equal + MOVB AX, ret+96(FP) + RET + +// ---------------------------------------------------------------------------- +// Special optimization for buffers smaller than 129 bytes +openSSE128: + // For up to 128 bytes of ciphertext and 64 bytes for the poly key, we require to process three blocks + MOVOU ·chacha20Constants<>(SB), A0; MOVOU (1*16)(keyp), B0; MOVOU (2*16)(keyp), C0; MOVOU (3*16)(keyp), D0 + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO B0, T1; MOVO C0, T2; MOVO D1, T3 + MOVQ $10, itr2 + +openSSE128InnerCipherLoop: + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Left; shiftB1Left; shiftB2Left + shiftC0Left; shiftC1Left; shiftC2Left + shiftD0Left; shiftD1Left; shiftD2Left + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Right; shiftB1Right; shiftB2Right + shiftC0Right; shiftC1Right; shiftC2Right + shiftD0Right; shiftD1Right; shiftD2Right + DECQ itr2 + JNE openSSE128InnerCipherLoop + + // A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2 + PADDL T1, B0; PADDL T1, B1; PADDL T1, B2 + PADDL T2, C1; PADDL T2, C2 + PADDL T3, D1; PADDL ·sseIncMask<>(SB), T3; PADDL T3, D2 + + // Clamp and store the key + PAND ·polyClampMask<>(SB), A0 + MOVOU A0, rStore; MOVOU B0, sStore + + // Hash + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + +openSSE128Open: + CMPQ inl, $16 + JB openSSETail16 + SUBQ $16, inl + + // Load for hashing + polyAdd(0(inp)) + + // Load for decryption + MOVOU (inp), T0; PXOR T0, A1; MOVOU A1, (oup) + LEAQ (1*16)(inp), inp + LEAQ (1*16)(oup), oup + polyMul + + // Shift the stream "left" + MOVO B1, A1 + MOVO C1, B1 + MOVO D1, C1 + MOVO A2, D1 + MOVO B2, A2 + MOVO C2, B2 + MOVO D2, C2 + JMP openSSE128Open + +openSSETail16: + TESTQ inl, inl + JE openSSEFinalize + + // We can safely load the CT from the end, because it is padded with the MAC + MOVQ inl, itr2 + SHLQ $4, itr2 + LEAQ ·andMask<>(SB), t0 + MOVOU (inp), T0 + ADDQ inl, inp + PAND -16(t0)(itr2*1), T0 + MOVO T0, 0+tmpStore + MOVQ T0, t0 + MOVQ 8+tmpStore, t1 + PXOR A1, T0 + + // We can only store one byte at a time, since plaintext can be shorter than 16 bytes +openSSETail16Store: + MOVQ T0, t3 + MOVB t3, (oup) + PSRLDQ $1, T0 + INCQ oup + DECQ inl + JNE openSSETail16Store + ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2 + polyMul + JMP openSSEFinalize + +// ---------------------------------------------------------------------------- +// Special optimization for the last 64 bytes of ciphertext +openSSETail64: + // Need to decrypt up to 64 bytes - prepare single block + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store + XORQ itr2, itr2 + MOVQ inl, itr1 + CMPQ itr1, $16 + JB openSSETail64LoopB + +openSSETail64LoopA: + // Perform ChaCha rounds, while hashing the remaining input + polyAdd(0(inp)(itr2*1)) + polyMul + SUBQ $16, itr1 + +openSSETail64LoopB: + ADDQ $16, itr2 + chachaQR(A0, B0, C0, D0, T0) + shiftB0Left; shiftC0Left; shiftD0Left + chachaQR(A0, B0, C0, D0, T0) + shiftB0Right; shiftC0Right; shiftD0Right + + CMPQ itr1, $16 + JAE openSSETail64LoopA + + CMPQ itr2, $160 + JNE openSSETail64LoopB + + PADDL ·chacha20Constants<>(SB), A0; PADDL state1Store, B0; PADDL state2Store, C0; PADDL ctr0Store, D0 + +openSSETail64DecLoop: + CMPQ inl, $16 + JB openSSETail64DecLoopDone + SUBQ $16, inl + MOVOU (inp), T0 + PXOR T0, A0 + MOVOU A0, (oup) + LEAQ 16(inp), inp + LEAQ 16(oup), oup + MOVO B0, A0 + MOVO C0, B0 + MOVO D0, C0 + JMP openSSETail64DecLoop + +openSSETail64DecLoopDone: + MOVO A0, A1 + JMP openSSETail16 + +// ---------------------------------------------------------------------------- +// Special optimization for the last 128 bytes of ciphertext +openSSETail128: + // Need to decrypt up to 128 bytes - prepare two blocks + MOVO ·chacha20Constants<>(SB), A1; MOVO state1Store, B1; MOVO state2Store, C1; MOVO ctr3Store, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr0Store + MOVO A1, A0; MOVO B1, B0; MOVO C1, C0; MOVO D1, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr1Store + XORQ itr2, itr2 + MOVQ inl, itr1 + ANDQ $-16, itr1 + +openSSETail128LoopA: + // Perform ChaCha rounds, while hashing the remaining input + polyAdd(0(inp)(itr2*1)) + polyMul + +openSSETail128LoopB: + ADDQ $16, itr2 + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0) + shiftB0Left; shiftC0Left; shiftD0Left + shiftB1Left; shiftC1Left; shiftD1Left + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0) + shiftB0Right; shiftC0Right; shiftD0Right + shiftB1Right; shiftC1Right; shiftD1Right + + CMPQ itr2, itr1 + JB openSSETail128LoopA + + CMPQ itr2, $160 + JNE openSSETail128LoopB + + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1 + PADDL state1Store, B0; PADDL state1Store, B1 + PADDL state2Store, C0; PADDL state2Store, C1 + PADDL ctr1Store, D0; PADDL ctr0Store, D1 + + MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3 + PXOR T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1 + MOVOU A1, (0*16)(oup); MOVOU B1, (1*16)(oup); MOVOU C1, (2*16)(oup); MOVOU D1, (3*16)(oup) + + SUBQ $64, inl + LEAQ 64(inp), inp + LEAQ 64(oup), oup + JMP openSSETail64DecLoop + +// ---------------------------------------------------------------------------- +// Special optimization for the last 192 bytes of ciphertext +openSSETail192: + // Need to decrypt up to 192 bytes - prepare three blocks + MOVO ·chacha20Constants<>(SB), A2; MOVO state1Store, B2; MOVO state2Store, C2; MOVO ctr3Store, D2; PADDL ·sseIncMask<>(SB), D2; MOVO D2, ctr0Store + MOVO A2, A1; MOVO B2, B1; MOVO C2, C1; MOVO D2, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store + MOVO A1, A0; MOVO B1, B0; MOVO C1, C0; MOVO D1, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr2Store + + MOVQ inl, itr1 + MOVQ $160, itr2 + CMPQ itr1, $160 + CMOVQGT itr2, itr1 + ANDQ $-16, itr1 + XORQ itr2, itr2 + +openSSLTail192LoopA: + // Perform ChaCha rounds, while hashing the remaining input + polyAdd(0(inp)(itr2*1)) + polyMul + +openSSLTail192LoopB: + ADDQ $16, itr2 + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Left; shiftC0Left; shiftD0Left + shiftB1Left; shiftC1Left; shiftD1Left + shiftB2Left; shiftC2Left; shiftD2Left + + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Right; shiftC0Right; shiftD0Right + shiftB1Right; shiftC1Right; shiftD1Right + shiftB2Right; shiftC2Right; shiftD2Right + + CMPQ itr2, itr1 + JB openSSLTail192LoopA + + CMPQ itr2, $160 + JNE openSSLTail192LoopB + + CMPQ inl, $176 + JB openSSLTail192Store + + polyAdd(160(inp)) + polyMul + + CMPQ inl, $192 + JB openSSLTail192Store + + polyAdd(176(inp)) + polyMul + +openSSLTail192Store: + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2 + PADDL state1Store, B0; PADDL state1Store, B1; PADDL state1Store, B2 + PADDL state2Store, C0; PADDL state2Store, C1; PADDL state2Store, C2 + PADDL ctr2Store, D0; PADDL ctr1Store, D1; PADDL ctr0Store, D2 + + MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3 + PXOR T0, A2; PXOR T1, B2; PXOR T2, C2; PXOR T3, D2 + MOVOU A2, (0*16)(oup); MOVOU B2, (1*16)(oup); MOVOU C2, (2*16)(oup); MOVOU D2, (3*16)(oup) + + MOVOU (4*16)(inp), T0; MOVOU (5*16)(inp), T1; MOVOU (6*16)(inp), T2; MOVOU (7*16)(inp), T3 + PXOR T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1 + MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup) + + SUBQ $128, inl + LEAQ 128(inp), inp + LEAQ 128(oup), oup + JMP openSSETail64DecLoop + +// ---------------------------------------------------------------------------- +// Special optimization for the last 256 bytes of ciphertext +openSSETail256: + // Need to decrypt up to 256 bytes - prepare four blocks + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0 + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3 + + // Store counters + MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store + XORQ itr2, itr2 + +openSSETail256Loop: + // This loop inteleaves 8 ChaCha quarter rounds with 1 poly multiplication + polyAdd(0(inp)(itr2*1)) + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + shiftB0Left; shiftB1Left; shiftB2Left; shiftB3Left + shiftC0Left; shiftC1Left; shiftC2Left; shiftC3Left + shiftD0Left; shiftD1Left; shiftD2Left; shiftD3Left + polyMulStage1 + polyMulStage2 + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + polyMulStage3 + polyMulReduceStage + shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right + shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right + shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right + ADDQ $2*8, itr2 + CMPQ itr2, $160 + JB openSSETail256Loop + MOVQ inl, itr1 + ANDQ $-16, itr1 + +openSSETail256HashLoop: + polyAdd(0(inp)(itr2*1)) + polyMul + ADDQ $2*8, itr2 + CMPQ itr2, itr1 + JB openSSETail256HashLoop + + // Add in the state + PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3 + PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3 + PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3 + PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3 + MOVO D3, tmpStore + + // Load - xor - store + MOVOU (0*16)(inp), D3; PXOR D3, A0 + MOVOU (1*16)(inp), D3; PXOR D3, B0 + MOVOU (2*16)(inp), D3; PXOR D3, C0 + MOVOU (3*16)(inp), D3; PXOR D3, D0 + MOVOU A0, (0*16)(oup) + MOVOU B0, (1*16)(oup) + MOVOU C0, (2*16)(oup) + MOVOU D0, (3*16)(oup) + MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0 + PXOR A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1 + MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup) + MOVOU (8*16)(inp), A0; MOVOU (9*16)(inp), B0; MOVOU (10*16)(inp), C0; MOVOU (11*16)(inp), D0 + PXOR A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2 + MOVOU A2, (8*16)(oup); MOVOU B2, (9*16)(oup); MOVOU C2, (10*16)(oup); MOVOU D2, (11*16)(oup) + LEAQ 192(inp), inp + LEAQ 192(oup), oup + SUBQ $192, inl + MOVO A3, A0 + MOVO B3, B0 + MOVO C3, C0 + MOVO tmpStore, D0 + + JMP openSSETail64DecLoop + +// ---------------------------------------------------------------------------- +// ------------------------- AVX2 Code ---------------------------------------- +chacha20Poly1305Open_AVX2: + VZEROUPPER + VMOVDQU ·chacha20Constants<>(SB), AA0 + BYTE $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x70; BYTE $0x10 // broadcasti128 16(r8), ymm14 + BYTE $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x20 // broadcasti128 32(r8), ymm12 + BYTE $0xc4; BYTE $0xc2; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x30 // broadcasti128 48(r8), ymm4 + VPADDD ·avx2InitMask<>(SB), DD0, DD0 + + // Special optimization, for very short buffers + CMPQ inl, $192 + JBE openAVX2192 + CMPQ inl, $320 + JBE openAVX2320 + + // For the general key prepare the key first - as a byproduct we have 64 bytes of cipher stream + VMOVDQA BB0, state1StoreAVX2 + VMOVDQA CC0, state2StoreAVX2 + VMOVDQA DD0, ctr3StoreAVX2 + MOVQ $10, itr2 + +openAVX2PreparePolyKey: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0 + DECQ itr2 + JNE openAVX2PreparePolyKey + + VPADDD ·chacha20Constants<>(SB), AA0, AA0 + VPADDD state1StoreAVX2, BB0, BB0 + VPADDD state2StoreAVX2, CC0, CC0 + VPADDD ctr3StoreAVX2, DD0, DD0 + + VPERM2I128 $0x02, AA0, BB0, TT0 + + // Clamp and store poly key + VPAND ·polyClampMask<>(SB), TT0, TT0 + VMOVDQA TT0, rsStoreAVX2 + + // Stream for the first 64 bytes + VPERM2I128 $0x13, AA0, BB0, AA0 + VPERM2I128 $0x13, CC0, DD0, BB0 + + // Hash AD + first 64 bytes + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + XORQ itr1, itr1 + +openAVX2InitialHash64: + polyAdd(0(inp)(itr1*1)) + polyMulAVX2 + ADDQ $16, itr1 + CMPQ itr1, $64 + JNE openAVX2InitialHash64 + + // Decrypt the first 64 bytes + VPXOR (0*32)(inp), AA0, AA0 + VPXOR (1*32)(inp), BB0, BB0 + VMOVDQU AA0, (0*32)(oup) + VMOVDQU BB0, (1*32)(oup) + LEAQ (2*32)(inp), inp + LEAQ (2*32)(oup), oup + SUBQ $64, inl + +openAVX2MainLoop: + CMPQ inl, $512 + JB openAVX2MainLoopDone + + // Load state, increment counter blocks, store the incremented counters + VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3 + VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3 + VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2 + XORQ itr1, itr1 + +openAVX2InternalLoop: + // Lets just say this spaghetti loop interleaves 2 quarter rounds with 3 poly multiplications + // Effectively per 512 bytes of stream we hash 480 bytes of ciphertext + polyAdd(0*8(inp)(itr1*1)) + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + polyMulStage1_AVX2 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + polyMulStage2_AVX2 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyMulStage3_AVX2 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulReduceStage + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + polyAdd(2*8(inp)(itr1*1)) + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + polyMulStage1_AVX2 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulStage2_AVX2 + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + polyMulStage3_AVX2 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + polyMulReduceStage + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyAdd(4*8(inp)(itr1*1)) + LEAQ (6*8)(itr1), itr1 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulStage1_AVX2 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + polyMulStage2_AVX2 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + polyMulStage3_AVX2 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulReduceStage + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3 + CMPQ itr1, $480 + JNE openAVX2InternalLoop + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3 + VMOVDQA CC3, tmpStoreAVX2 + + // We only hashed 480 of the 512 bytes available - hash the remaining 32 here + polyAdd(480(inp)) + polyMulAVX2 + VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0 + VPXOR (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0 + VMOVDQU CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup) + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + VPXOR (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0 + VMOVDQU AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup) + + // and here + polyAdd(496(inp)) + polyMulAVX2 + VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0 + VPXOR (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0 + VMOVDQU AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup) + VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0 + VPXOR (12*32)(inp), AA0, AA0; VPXOR (13*32)(inp), BB0, BB0; VPXOR (14*32)(inp), CC0, CC0; VPXOR (15*32)(inp), DD0, DD0 + VMOVDQU AA0, (12*32)(oup); VMOVDQU BB0, (13*32)(oup); VMOVDQU CC0, (14*32)(oup); VMOVDQU DD0, (15*32)(oup) + LEAQ (32*16)(inp), inp + LEAQ (32*16)(oup), oup + SUBQ $(32*16), inl + JMP openAVX2MainLoop + +openAVX2MainLoopDone: + // Handle the various tail sizes efficiently + TESTQ inl, inl + JE openSSEFinalize + CMPQ inl, $128 + JBE openAVX2Tail128 + CMPQ inl, $256 + JBE openAVX2Tail256 + CMPQ inl, $384 + JBE openAVX2Tail384 + JMP openAVX2Tail512 + +// ---------------------------------------------------------------------------- +// Special optimization for buffers smaller than 193 bytes +openAVX2192: + // For up to 192 bytes of ciphertext and 64 bytes for the poly key, we process four blocks + VMOVDQA AA0, AA1 + VMOVDQA BB0, BB1 + VMOVDQA CC0, CC1 + VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA AA0, AA2 + VMOVDQA BB0, BB2 + VMOVDQA CC0, CC2 + VMOVDQA DD0, DD2 + VMOVDQA DD1, TT3 + MOVQ $10, itr2 + +openAVX2192InnerCipherLoop: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1 + DECQ itr2 + JNE openAVX2192InnerCipherLoop + VPADDD AA2, AA0, AA0; VPADDD AA2, AA1, AA1 + VPADDD BB2, BB0, BB0; VPADDD BB2, BB1, BB1 + VPADDD CC2, CC0, CC0; VPADDD CC2, CC1, CC1 + VPADDD DD2, DD0, DD0; VPADDD TT3, DD1, DD1 + VPERM2I128 $0x02, AA0, BB0, TT0 + + // Clamp and store poly key + VPAND ·polyClampMask<>(SB), TT0, TT0 + VMOVDQA TT0, rsStoreAVX2 + + // Stream for up to 192 bytes + VPERM2I128 $0x13, AA0, BB0, AA0 + VPERM2I128 $0x13, CC0, DD0, BB0 + VPERM2I128 $0x02, AA1, BB1, CC0 + VPERM2I128 $0x02, CC1, DD1, DD0 + VPERM2I128 $0x13, AA1, BB1, AA1 + VPERM2I128 $0x13, CC1, DD1, BB1 + +openAVX2ShortOpen: + // Hash + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + +openAVX2ShortOpenLoop: + CMPQ inl, $32 + JB openAVX2ShortTail32 + SUBQ $32, inl + + // Load for hashing + polyAdd(0*8(inp)) + polyMulAVX2 + polyAdd(2*8(inp)) + polyMulAVX2 + + // Load for decryption + VPXOR (inp), AA0, AA0 + VMOVDQU AA0, (oup) + LEAQ (1*32)(inp), inp + LEAQ (1*32)(oup), oup + + // Shift stream left + VMOVDQA BB0, AA0 + VMOVDQA CC0, BB0 + VMOVDQA DD0, CC0 + VMOVDQA AA1, DD0 + VMOVDQA BB1, AA1 + VMOVDQA CC1, BB1 + VMOVDQA DD1, CC1 + VMOVDQA AA2, DD1 + VMOVDQA BB2, AA2 + JMP openAVX2ShortOpenLoop + +openAVX2ShortTail32: + CMPQ inl, $16 + VMOVDQA A0, A1 + JB openAVX2ShortDone + + SUBQ $16, inl + + // Load for hashing + polyAdd(0*8(inp)) + polyMulAVX2 + + // Load for decryption + VPXOR (inp), A0, T0 + VMOVDQU T0, (oup) + LEAQ (1*16)(inp), inp + LEAQ (1*16)(oup), oup + VPERM2I128 $0x11, AA0, AA0, AA0 + VMOVDQA A0, A1 + +openAVX2ShortDone: + VZEROUPPER + JMP openSSETail16 + +// ---------------------------------------------------------------------------- +// Special optimization for buffers smaller than 321 bytes +openAVX2320: + // For up to 320 bytes of ciphertext and 64 bytes for the poly key, we process six blocks + VMOVDQA AA0, AA1; VMOVDQA BB0, BB1; VMOVDQA CC0, CC1; VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA AA0, AA2; VMOVDQA BB0, BB2; VMOVDQA CC0, CC2; VPADDD ·avx2IncMask<>(SB), DD1, DD2 + VMOVDQA BB0, TT1; VMOVDQA CC0, TT2; VMOVDQA DD0, TT3 + MOVQ $10, itr2 + +openAVX2320InnerCipherLoop: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2 + DECQ itr2 + JNE openAVX2320InnerCipherLoop + + VMOVDQA ·chacha20Constants<>(SB), TT0 + VPADDD TT0, AA0, AA0; VPADDD TT0, AA1, AA1; VPADDD TT0, AA2, AA2 + VPADDD TT1, BB0, BB0; VPADDD TT1, BB1, BB1; VPADDD TT1, BB2, BB2 + VPADDD TT2, CC0, CC0; VPADDD TT2, CC1, CC1; VPADDD TT2, CC2, CC2 + VMOVDQA ·avx2IncMask<>(SB), TT0 + VPADDD TT3, DD0, DD0; VPADDD TT0, TT3, TT3 + VPADDD TT3, DD1, DD1; VPADDD TT0, TT3, TT3 + VPADDD TT3, DD2, DD2 + + // Clamp and store poly key + VPERM2I128 $0x02, AA0, BB0, TT0 + VPAND ·polyClampMask<>(SB), TT0, TT0 + VMOVDQA TT0, rsStoreAVX2 + + // Stream for up to 320 bytes + VPERM2I128 $0x13, AA0, BB0, AA0 + VPERM2I128 $0x13, CC0, DD0, BB0 + VPERM2I128 $0x02, AA1, BB1, CC0 + VPERM2I128 $0x02, CC1, DD1, DD0 + VPERM2I128 $0x13, AA1, BB1, AA1 + VPERM2I128 $0x13, CC1, DD1, BB1 + VPERM2I128 $0x02, AA2, BB2, CC1 + VPERM2I128 $0x02, CC2, DD2, DD1 + VPERM2I128 $0x13, AA2, BB2, AA2 + VPERM2I128 $0x13, CC2, DD2, BB2 + JMP openAVX2ShortOpen + +// ---------------------------------------------------------------------------- +// Special optimization for the last 128 bytes of ciphertext +openAVX2Tail128: + // Need to decrypt up to 128 bytes - prepare two blocks + VMOVDQA ·chacha20Constants<>(SB), AA1 + VMOVDQA state1StoreAVX2, BB1 + VMOVDQA state2StoreAVX2, CC1 + VMOVDQA ctr3StoreAVX2, DD1 + VPADDD ·avx2IncMask<>(SB), DD1, DD1 + VMOVDQA DD1, DD0 + + XORQ itr2, itr2 + MOVQ inl, itr1 + ANDQ $-16, itr1 + TESTQ itr1, itr1 + JE openAVX2Tail128LoopB + +openAVX2Tail128LoopA: + // Perform ChaCha rounds, while hashing the remaining input + polyAdd(0(inp)(itr2*1)) + polyMulAVX2 + +openAVX2Tail128LoopB: + ADDQ $16, itr2 + chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $4, BB1, BB1, BB1 + VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $12, DD1, DD1, DD1 + chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $12, BB1, BB1, BB1 + VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $4, DD1, DD1, DD1 + CMPQ itr2, itr1 + JB openAVX2Tail128LoopA + CMPQ itr2, $160 + JNE openAVX2Tail128LoopB + + VPADDD ·chacha20Constants<>(SB), AA1, AA1 + VPADDD state1StoreAVX2, BB1, BB1 + VPADDD state2StoreAVX2, CC1, CC1 + VPADDD DD0, DD1, DD1 + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + +openAVX2TailLoop: + CMPQ inl, $32 + JB openAVX2Tail + SUBQ $32, inl + + // Load for decryption + VPXOR (inp), AA0, AA0 + VMOVDQU AA0, (oup) + LEAQ (1*32)(inp), inp + LEAQ (1*32)(oup), oup + VMOVDQA BB0, AA0 + VMOVDQA CC0, BB0 + VMOVDQA DD0, CC0 + JMP openAVX2TailLoop + +openAVX2Tail: + CMPQ inl, $16 + VMOVDQA A0, A1 + JB openAVX2TailDone + SUBQ $16, inl + + // Load for decryption + VPXOR (inp), A0, T0 + VMOVDQU T0, (oup) + LEAQ (1*16)(inp), inp + LEAQ (1*16)(oup), oup + VPERM2I128 $0x11, AA0, AA0, AA0 + VMOVDQA A0, A1 + +openAVX2TailDone: + VZEROUPPER + JMP openSSETail16 + +// ---------------------------------------------------------------------------- +// Special optimization for the last 256 bytes of ciphertext +openAVX2Tail256: + // Need to decrypt up to 256 bytes - prepare four blocks + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA DD0, TT1 + VMOVDQA DD1, TT2 + + // Compute the number of iterations that will hash data + MOVQ inl, tmpStoreAVX2 + MOVQ inl, itr1 + SUBQ $128, itr1 + SHRQ $4, itr1 + MOVQ $10, itr2 + CMPQ itr1, $10 + CMOVQGT itr2, itr1 + MOVQ inp, inl + XORQ itr2, itr2 + +openAVX2Tail256LoopA: + polyAdd(0(inl)) + polyMulAVX2 + LEAQ 16(inl), inl + + // Perform ChaCha rounds, while hashing the remaining input +openAVX2Tail256LoopB: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1 + INCQ itr2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1 + CMPQ itr2, itr1 + JB openAVX2Tail256LoopA + + CMPQ itr2, $10 + JNE openAVX2Tail256LoopB + + MOVQ inl, itr2 + SUBQ inp, inl + MOVQ inl, itr1 + MOVQ tmpStoreAVX2, inl + + // Hash the remainder of data (if any) +openAVX2Tail256Hash: + ADDQ $16, itr1 + CMPQ itr1, inl + JGT openAVX2Tail256HashEnd + polyAdd (0(itr2)) + polyMulAVX2 + LEAQ 16(itr2), itr2 + JMP openAVX2Tail256Hash + +// Store 128 bytes safely, then go to store loop +openAVX2Tail256HashEnd: + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1 + VPADDD TT1, DD0, DD0; VPADDD TT2, DD1, DD1 + VPERM2I128 $0x02, AA0, BB0, AA2; VPERM2I128 $0x02, CC0, DD0, BB2; VPERM2I128 $0x13, AA0, BB0, CC2; VPERM2I128 $0x13, CC0, DD0, DD2 + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + + VPXOR (0*32)(inp), AA2, AA2; VPXOR (1*32)(inp), BB2, BB2; VPXOR (2*32)(inp), CC2, CC2; VPXOR (3*32)(inp), DD2, DD2 + VMOVDQU AA2, (0*32)(oup); VMOVDQU BB2, (1*32)(oup); VMOVDQU CC2, (2*32)(oup); VMOVDQU DD2, (3*32)(oup) + LEAQ (4*32)(inp), inp + LEAQ (4*32)(oup), oup + SUBQ $4*32, inl + + JMP openAVX2TailLoop + +// ---------------------------------------------------------------------------- +// Special optimization for the last 384 bytes of ciphertext +openAVX2Tail384: + // Need to decrypt up to 384 bytes - prepare six blocks + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VPADDD ·avx2IncMask<>(SB), DD1, DD2 + VMOVDQA DD0, ctr0StoreAVX2 + VMOVDQA DD1, ctr1StoreAVX2 + VMOVDQA DD2, ctr2StoreAVX2 + + // Compute the number of iterations that will hash two blocks of data + MOVQ inl, tmpStoreAVX2 + MOVQ inl, itr1 + SUBQ $256, itr1 + SHRQ $4, itr1 + ADDQ $6, itr1 + MOVQ $10, itr2 + CMPQ itr1, $10 + CMOVQGT itr2, itr1 + MOVQ inp, inl + XORQ itr2, itr2 + + // Perform ChaCha rounds, while hashing the remaining input +openAVX2Tail384LoopB: + polyAdd(0(inl)) + polyMulAVX2 + LEAQ 16(inl), inl + +openAVX2Tail384LoopA: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2 + polyAdd(0(inl)) + polyMulAVX2 + LEAQ 16(inl), inl + INCQ itr2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2 + + CMPQ itr2, itr1 + JB openAVX2Tail384LoopB + + CMPQ itr2, $10 + JNE openAVX2Tail384LoopA + + MOVQ inl, itr2 + SUBQ inp, inl + MOVQ inl, itr1 + MOVQ tmpStoreAVX2, inl + +openAVX2Tail384Hash: + ADDQ $16, itr1 + CMPQ itr1, inl + JGT openAVX2Tail384HashEnd + polyAdd(0(itr2)) + polyMulAVX2 + LEAQ 16(itr2), itr2 + JMP openAVX2Tail384Hash + +// Store 256 bytes safely, then go to store loop +openAVX2Tail384HashEnd: + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2 + VPERM2I128 $0x02, AA0, BB0, TT0; VPERM2I128 $0x02, CC0, DD0, TT1; VPERM2I128 $0x13, AA0, BB0, TT2; VPERM2I128 $0x13, CC0, DD0, TT3 + VPXOR (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3 + VMOVDQU TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup) + VPERM2I128 $0x02, AA1, BB1, TT0; VPERM2I128 $0x02, CC1, DD1, TT1; VPERM2I128 $0x13, AA1, BB1, TT2; VPERM2I128 $0x13, CC1, DD1, TT3 + VPXOR (4*32)(inp), TT0, TT0; VPXOR (5*32)(inp), TT1, TT1; VPXOR (6*32)(inp), TT2, TT2; VPXOR (7*32)(inp), TT3, TT3 + VMOVDQU TT0, (4*32)(oup); VMOVDQU TT1, (5*32)(oup); VMOVDQU TT2, (6*32)(oup); VMOVDQU TT3, (7*32)(oup) + VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0 + LEAQ (8*32)(inp), inp + LEAQ (8*32)(oup), oup + SUBQ $8*32, inl + JMP openAVX2TailLoop + +// ---------------------------------------------------------------------------- +// Special optimization for the last 512 bytes of ciphertext +openAVX2Tail512: + VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3 + VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3 + VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2 + XORQ itr1, itr1 + MOVQ inp, itr2 + +openAVX2Tail512LoopB: + polyAdd(0(itr2)) + polyMulAVX2 + LEAQ (2*8)(itr2), itr2 + +openAVX2Tail512LoopA: + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyAdd(0*8(itr2)) + polyMulAVX2 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyAdd(2*8(itr2)) + polyMulAVX2 + LEAQ (4*8)(itr2), itr2 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3 + INCQ itr1 + CMPQ itr1, $4 + JLT openAVX2Tail512LoopB + + CMPQ itr1, $10 + JNE openAVX2Tail512LoopA + + MOVQ inl, itr1 + SUBQ $384, itr1 + ANDQ $-16, itr1 + +openAVX2Tail512HashLoop: + TESTQ itr1, itr1 + JE openAVX2Tail512HashEnd + polyAdd(0(itr2)) + polyMulAVX2 + LEAQ 16(itr2), itr2 + SUBQ $16, itr1 + JMP openAVX2Tail512HashLoop + +openAVX2Tail512HashEnd: + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3 + VMOVDQA CC3, tmpStoreAVX2 + VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0 + VPXOR (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0 + VMOVDQU CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup) + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + VPXOR (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0 + VMOVDQU AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup) + VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0 + VPXOR (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0 + VMOVDQU AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup) + VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0 + + LEAQ (12*32)(inp), inp + LEAQ (12*32)(oup), oup + SUBQ $12*32, inl + + JMP openAVX2TailLoop + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// func chacha20Poly1305Seal(dst, key, src, ad []byte) +TEXT ·chacha20Poly1305Seal(SB), 0, $288-96 + // For aligned stack access + MOVQ SP, BP + ADDQ $32, BP + ANDQ $-32, BP + MOVQ dst+0(FP), oup + MOVQ key+24(FP), keyp + MOVQ src+48(FP), inp + MOVQ src_len+56(FP), inl + MOVQ ad+72(FP), adp + + CMPB ·useAVX2(SB), $1 + JE chacha20Poly1305Seal_AVX2 + + // Special optimization, for very short buffers + CMPQ inl, $128 + JBE sealSSE128 // About 15% faster + + // In the seal case - prepare the poly key + 3 blocks of stream in the first iteration + MOVOU ·chacha20Constants<>(SB), A0 + MOVOU (1*16)(keyp), B0 + MOVOU (2*16)(keyp), C0 + MOVOU (3*16)(keyp), D0 + + // Store state on stack for future use + MOVO B0, state1Store + MOVO C0, state2Store + + // Load state, increment counter blocks + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3 + + // Store counters + MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store + MOVQ $10, itr2 + +sealSSEIntroLoop: + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + shiftB0Left; shiftB1Left; shiftB2Left; shiftB3Left + shiftC0Left; shiftC1Left; shiftC2Left; shiftC3Left + shiftD0Left; shiftD1Left; shiftD2Left; shiftD3Left + + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right + shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right + shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right + DECQ itr2 + JNE sealSSEIntroLoop + + // Add in the state + PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3 + PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3 + PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3 + PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3 + + // Clamp and store the key + PAND ·polyClampMask<>(SB), A0 + MOVO A0, rStore + MOVO B0, sStore + + // Hash AAD + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + + MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0 + PXOR A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1 + MOVOU A1, (0*16)(oup); MOVOU B1, (1*16)(oup); MOVOU C1, (2*16)(oup); MOVOU D1, (3*16)(oup) + MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0 + PXOR A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2 + MOVOU A2, (4*16)(oup); MOVOU B2, (5*16)(oup); MOVOU C2, (6*16)(oup); MOVOU D2, (7*16)(oup) + + MOVQ $128, itr1 + SUBQ $128, inl + LEAQ 128(inp), inp + + MOVO A3, A1; MOVO B3, B1; MOVO C3, C1; MOVO D3, D1 + + CMPQ inl, $64 + JBE sealSSE128SealHash + + MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0 + PXOR A0, A3; PXOR B0, B3; PXOR C0, C3; PXOR D0, D3 + MOVOU A3, (8*16)(oup); MOVOU B3, (9*16)(oup); MOVOU C3, (10*16)(oup); MOVOU D3, (11*16)(oup) + + ADDQ $64, itr1 + SUBQ $64, inl + LEAQ 64(inp), inp + + MOVQ $2, itr1 + MOVQ $8, itr2 + + CMPQ inl, $64 + JBE sealSSETail64 + CMPQ inl, $128 + JBE sealSSETail128 + CMPQ inl, $192 + JBE sealSSETail192 + +sealSSEMainLoop: + // Load state, increment counter blocks + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0 + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3 + + // Store counters + MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store + +sealSSEInnerLoop: + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + polyAdd(0(oup)) + shiftB0Left; shiftB1Left; shiftB2Left; shiftB3Left + shiftC0Left; shiftC1Left; shiftC2Left; shiftC3Left + shiftD0Left; shiftD1Left; shiftD2Left; shiftD3Left + polyMulStage1 + polyMulStage2 + LEAQ (2*8)(oup), oup + MOVO C3, tmpStore + chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3) + MOVO tmpStore, C3 + MOVO C1, tmpStore + polyMulStage3 + chachaQR(A3, B3, C3, D3, C1) + MOVO tmpStore, C1 + polyMulReduceStage + shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right + shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right + shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right + DECQ itr2 + JGE sealSSEInnerLoop + polyAdd(0(oup)) + polyMul + LEAQ (2*8)(oup), oup + DECQ itr1 + JG sealSSEInnerLoop + + // Add in the state + PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3 + PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3 + PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3 + PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3 + MOVO D3, tmpStore + + // Load - xor - store + MOVOU (0*16)(inp), D3; PXOR D3, A0 + MOVOU (1*16)(inp), D3; PXOR D3, B0 + MOVOU (2*16)(inp), D3; PXOR D3, C0 + MOVOU (3*16)(inp), D3; PXOR D3, D0 + MOVOU A0, (0*16)(oup) + MOVOU B0, (1*16)(oup) + MOVOU C0, (2*16)(oup) + MOVOU D0, (3*16)(oup) + MOVO tmpStore, D3 + + MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0 + PXOR A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1 + MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup) + MOVOU (8*16)(inp), A0; MOVOU (9*16)(inp), B0; MOVOU (10*16)(inp), C0; MOVOU (11*16)(inp), D0 + PXOR A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2 + MOVOU A2, (8*16)(oup); MOVOU B2, (9*16)(oup); MOVOU C2, (10*16)(oup); MOVOU D2, (11*16)(oup) + ADDQ $192, inp + MOVQ $192, itr1 + SUBQ $192, inl + MOVO A3, A1 + MOVO B3, B1 + MOVO C3, C1 + MOVO D3, D1 + CMPQ inl, $64 + JBE sealSSE128SealHash + MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0 + PXOR A0, A3; PXOR B0, B3; PXOR C0, C3; PXOR D0, D3 + MOVOU A3, (12*16)(oup); MOVOU B3, (13*16)(oup); MOVOU C3, (14*16)(oup); MOVOU D3, (15*16)(oup) + LEAQ 64(inp), inp + SUBQ $64, inl + MOVQ $6, itr1 + MOVQ $4, itr2 + CMPQ inl, $192 + JG sealSSEMainLoop + + MOVQ inl, itr1 + TESTQ inl, inl + JE sealSSE128SealHash + MOVQ $6, itr1 + CMPQ inl, $64 + JBE sealSSETail64 + CMPQ inl, $128 + JBE sealSSETail128 + JMP sealSSETail192 + +// ---------------------------------------------------------------------------- +// Special optimization for the last 64 bytes of plaintext +sealSSETail64: + // Need to encrypt up to 64 bytes - prepare single block, hash 192 or 256 bytes + MOVO ·chacha20Constants<>(SB), A1 + MOVO state1Store, B1 + MOVO state2Store, C1 + MOVO ctr3Store, D1 + PADDL ·sseIncMask<>(SB), D1 + MOVO D1, ctr0Store + +sealSSETail64LoopA: + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealSSETail64LoopB: + chachaQR(A1, B1, C1, D1, T1) + shiftB1Left; shiftC1Left; shiftD1Left + chachaQR(A1, B1, C1, D1, T1) + shiftB1Right; shiftC1Right; shiftD1Right + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + + DECQ itr1 + JG sealSSETail64LoopA + + DECQ itr2 + JGE sealSSETail64LoopB + PADDL ·chacha20Constants<>(SB), A1 + PADDL state1Store, B1 + PADDL state2Store, C1 + PADDL ctr0Store, D1 + + JMP sealSSE128Seal + +// ---------------------------------------------------------------------------- +// Special optimization for the last 128 bytes of plaintext +sealSSETail128: + // Need to encrypt up to 128 bytes - prepare two blocks, hash 192 or 256 bytes + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store + +sealSSETail128LoopA: + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealSSETail128LoopB: + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0) + shiftB0Left; shiftC0Left; shiftD0Left + shiftB1Left; shiftC1Left; shiftD1Left + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0) + shiftB0Right; shiftC0Right; shiftD0Right + shiftB1Right; shiftC1Right; shiftD1Right + + DECQ itr1 + JG sealSSETail128LoopA + + DECQ itr2 + JGE sealSSETail128LoopB + + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1 + PADDL state1Store, B0; PADDL state1Store, B1 + PADDL state2Store, C0; PADDL state2Store, C1 + PADDL ctr0Store, D0; PADDL ctr1Store, D1 + + MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3 + PXOR T0, A0; PXOR T1, B0; PXOR T2, C0; PXOR T3, D0 + MOVOU A0, (0*16)(oup); MOVOU B0, (1*16)(oup); MOVOU C0, (2*16)(oup); MOVOU D0, (3*16)(oup) + + MOVQ $64, itr1 + LEAQ 64(inp), inp + SUBQ $64, inl + + JMP sealSSE128SealHash + +// ---------------------------------------------------------------------------- +// Special optimization for the last 192 bytes of plaintext +sealSSETail192: + // Need to encrypt up to 192 bytes - prepare three blocks, hash 192 or 256 bytes + MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2; MOVO D2, ctr2Store + +sealSSETail192LoopA: + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealSSETail192LoopB: + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Left; shiftC0Left; shiftD0Left + shiftB1Left; shiftC1Left; shiftD1Left + shiftB2Left; shiftC2Left; shiftD2Left + + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Right; shiftC0Right; shiftD0Right + shiftB1Right; shiftC1Right; shiftD1Right + shiftB2Right; shiftC2Right; shiftD2Right + + DECQ itr1 + JG sealSSETail192LoopA + + DECQ itr2 + JGE sealSSETail192LoopB + + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2 + PADDL state1Store, B0; PADDL state1Store, B1; PADDL state1Store, B2 + PADDL state2Store, C0; PADDL state2Store, C1; PADDL state2Store, C2 + PADDL ctr0Store, D0; PADDL ctr1Store, D1; PADDL ctr2Store, D2 + + MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3 + PXOR T0, A0; PXOR T1, B0; PXOR T2, C0; PXOR T3, D0 + MOVOU A0, (0*16)(oup); MOVOU B0, (1*16)(oup); MOVOU C0, (2*16)(oup); MOVOU D0, (3*16)(oup) + MOVOU (4*16)(inp), T0; MOVOU (5*16)(inp), T1; MOVOU (6*16)(inp), T2; MOVOU (7*16)(inp), T3 + PXOR T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1 + MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup) + + MOVO A2, A1 + MOVO B2, B1 + MOVO C2, C1 + MOVO D2, D1 + MOVQ $128, itr1 + LEAQ 128(inp), inp + SUBQ $128, inl + + JMP sealSSE128SealHash + +// ---------------------------------------------------------------------------- +// Special seal optimization for buffers smaller than 129 bytes +sealSSE128: + // For up to 128 bytes of ciphertext and 64 bytes for the poly key, we require to process three blocks + MOVOU ·chacha20Constants<>(SB), A0; MOVOU (1*16)(keyp), B0; MOVOU (2*16)(keyp), C0; MOVOU (3*16)(keyp), D0 + MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1 + MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2 + MOVO B0, T1; MOVO C0, T2; MOVO D1, T3 + MOVQ $10, itr2 + +sealSSE128InnerCipherLoop: + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Left; shiftB1Left; shiftB2Left + shiftC0Left; shiftC1Left; shiftC2Left + shiftD0Left; shiftD1Left; shiftD2Left + chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0) + shiftB0Right; shiftB1Right; shiftB2Right + shiftC0Right; shiftC1Right; shiftC2Right + shiftD0Right; shiftD1Right; shiftD2Right + DECQ itr2 + JNE sealSSE128InnerCipherLoop + + // A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded + PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2 + PADDL T1, B0; PADDL T1, B1; PADDL T1, B2 + PADDL T2, C1; PADDL T2, C2 + PADDL T3, D1; PADDL ·sseIncMask<>(SB), T3; PADDL T3, D2 + PAND ·polyClampMask<>(SB), A0 + MOVOU A0, rStore + MOVOU B0, sStore + + // Hash + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + XORQ itr1, itr1 + +sealSSE128SealHash: + // itr1 holds the number of bytes encrypted but not yet hashed + CMPQ itr1, $16 + JB sealSSE128Seal + polyAdd(0(oup)) + polyMul + + SUBQ $16, itr1 + ADDQ $16, oup + + JMP sealSSE128SealHash + +sealSSE128Seal: + CMPQ inl, $16 + JB sealSSETail + SUBQ $16, inl + + // Load for decryption + MOVOU (inp), T0 + PXOR T0, A1 + MOVOU A1, (oup) + LEAQ (1*16)(inp), inp + LEAQ (1*16)(oup), oup + + // Extract for hashing + MOVQ A1, t0 + PSRLDQ $8, A1 + MOVQ A1, t1 + ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2 + polyMul + + // Shift the stream "left" + MOVO B1, A1 + MOVO C1, B1 + MOVO D1, C1 + MOVO A2, D1 + MOVO B2, A2 + MOVO C2, B2 + MOVO D2, C2 + JMP sealSSE128Seal + +sealSSETail: + TESTQ inl, inl + JE sealSSEFinalize + + // We can only load the PT one byte at a time to avoid read after end of buffer + MOVQ inl, itr2 + SHLQ $4, itr2 + LEAQ ·andMask<>(SB), t0 + MOVQ inl, itr1 + LEAQ -1(inp)(inl*1), inp + XORQ t2, t2 + XORQ t3, t3 + XORQ AX, AX + +sealSSETailLoadLoop: + SHLQ $8, t2, t3 + SHLQ $8, t2 + MOVB (inp), AX + XORQ AX, t2 + LEAQ -1(inp), inp + DECQ itr1 + JNE sealSSETailLoadLoop + MOVQ t2, 0+tmpStore + MOVQ t3, 8+tmpStore + PXOR 0+tmpStore, A1 + MOVOU A1, (oup) + MOVOU -16(t0)(itr2*1), T0 + PAND T0, A1 + MOVQ A1, t0 + PSRLDQ $8, A1 + MOVQ A1, t1 + ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2 + polyMul + + ADDQ inl, oup + +sealSSEFinalize: + // Hash in the buffer lengths + ADDQ ad_len+80(FP), acc0 + ADCQ src_len+56(FP), acc1 + ADCQ $1, acc2 + polyMul + + // Final reduce + MOVQ acc0, t0 + MOVQ acc1, t1 + MOVQ acc2, t2 + SUBQ $-5, acc0 + SBBQ $-1, acc1 + SBBQ $3, acc2 + CMOVQCS t0, acc0 + CMOVQCS t1, acc1 + CMOVQCS t2, acc2 + + // Add in the "s" part of the key + ADDQ 0+sStore, acc0 + ADCQ 8+sStore, acc1 + + // Finally store the tag at the end of the message + MOVQ acc0, (0*8)(oup) + MOVQ acc1, (1*8)(oup) + RET + +// ---------------------------------------------------------------------------- +// ------------------------- AVX2 Code ---------------------------------------- +chacha20Poly1305Seal_AVX2: + VZEROUPPER + VMOVDQU ·chacha20Constants<>(SB), AA0 + BYTE $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x70; BYTE $0x10 // broadcasti128 16(r8), ymm14 + BYTE $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x20 // broadcasti128 32(r8), ymm12 + BYTE $0xc4; BYTE $0xc2; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x30 // broadcasti128 48(r8), ymm4 + VPADDD ·avx2InitMask<>(SB), DD0, DD0 + + // Special optimizations, for very short buffers + CMPQ inl, $192 + JBE seal192AVX2 // 33% faster + CMPQ inl, $320 + JBE seal320AVX2 // 17% faster + + // For the general key prepare the key first - as a byproduct we have 64 bytes of cipher stream + VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3; VMOVDQA BB0, state1StoreAVX2 + VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3; VMOVDQA CC0, state2StoreAVX2 + VPADDD ·avx2IncMask<>(SB), DD0, DD1; VMOVDQA DD0, ctr0StoreAVX2 + VPADDD ·avx2IncMask<>(SB), DD1, DD2; VMOVDQA DD1, ctr1StoreAVX2 + VPADDD ·avx2IncMask<>(SB), DD2, DD3; VMOVDQA DD2, ctr2StoreAVX2 + VMOVDQA DD3, ctr3StoreAVX2 + MOVQ $10, itr2 + +sealAVX2IntroLoop: + VMOVDQA CC3, tmpStoreAVX2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3) + VMOVDQA tmpStoreAVX2, CC3 + VMOVDQA CC1, tmpStoreAVX2 + chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1) + VMOVDQA tmpStoreAVX2, CC1 + + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0 + VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $12, DD1, DD1, DD1 + VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $12, DD2, DD2, DD2 + VPALIGNR $4, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $12, DD3, DD3, DD3 + + VMOVDQA CC3, tmpStoreAVX2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3) + VMOVDQA tmpStoreAVX2, CC3 + VMOVDQA CC1, tmpStoreAVX2 + chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1) + VMOVDQA tmpStoreAVX2, CC1 + + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0 + VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $4, DD1, DD1, DD1 + VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $4, DD2, DD2, DD2 + VPALIGNR $12, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $4, DD3, DD3, DD3 + DECQ itr2 + JNE sealAVX2IntroLoop + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3 + + VPERM2I128 $0x13, CC0, DD0, CC0 // Stream bytes 96 - 127 + VPERM2I128 $0x02, AA0, BB0, DD0 // The Poly1305 key + VPERM2I128 $0x13, AA0, BB0, AA0 // Stream bytes 64 - 95 + + // Clamp and store poly key + VPAND ·polyClampMask<>(SB), DD0, DD0 + VMOVDQA DD0, rsStoreAVX2 + + // Hash AD + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + + // Can store at least 320 bytes + VPXOR (0*32)(inp), AA0, AA0 + VPXOR (1*32)(inp), CC0, CC0 + VMOVDQU AA0, (0*32)(oup) + VMOVDQU CC0, (1*32)(oup) + + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + VPXOR (2*32)(inp), AA0, AA0; VPXOR (3*32)(inp), BB0, BB0; VPXOR (4*32)(inp), CC0, CC0; VPXOR (5*32)(inp), DD0, DD0 + VMOVDQU AA0, (2*32)(oup); VMOVDQU BB0, (3*32)(oup); VMOVDQU CC0, (4*32)(oup); VMOVDQU DD0, (5*32)(oup) + VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0 + VPXOR (6*32)(inp), AA0, AA0; VPXOR (7*32)(inp), BB0, BB0; VPXOR (8*32)(inp), CC0, CC0; VPXOR (9*32)(inp), DD0, DD0 + VMOVDQU AA0, (6*32)(oup); VMOVDQU BB0, (7*32)(oup); VMOVDQU CC0, (8*32)(oup); VMOVDQU DD0, (9*32)(oup) + + MOVQ $320, itr1 + SUBQ $320, inl + LEAQ 320(inp), inp + + VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, CC3, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, CC3, DD3, DD0 + CMPQ inl, $128 + JBE sealAVX2SealHash + + VPXOR (0*32)(inp), AA0, AA0; VPXOR (1*32)(inp), BB0, BB0; VPXOR (2*32)(inp), CC0, CC0; VPXOR (3*32)(inp), DD0, DD0 + VMOVDQU AA0, (10*32)(oup); VMOVDQU BB0, (11*32)(oup); VMOVDQU CC0, (12*32)(oup); VMOVDQU DD0, (13*32)(oup) + SUBQ $128, inl + LEAQ 128(inp), inp + + MOVQ $8, itr1 + MOVQ $2, itr2 + + CMPQ inl, $128 + JBE sealAVX2Tail128 + CMPQ inl, $256 + JBE sealAVX2Tail256 + CMPQ inl, $384 + JBE sealAVX2Tail384 + CMPQ inl, $512 + JBE sealAVX2Tail512 + + // We have 448 bytes to hash, but main loop hashes 512 bytes at a time - perform some rounds, before the main loop + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3 + VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2 + + VMOVDQA CC3, tmpStoreAVX2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3) + VMOVDQA tmpStoreAVX2, CC3 + VMOVDQA CC1, tmpStoreAVX2 + chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1) + VMOVDQA tmpStoreAVX2, CC1 + + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0 + VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $12, DD1, DD1, DD1 + VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $12, DD2, DD2, DD2 + VPALIGNR $4, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $12, DD3, DD3, DD3 + + VMOVDQA CC3, tmpStoreAVX2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3) + VMOVDQA tmpStoreAVX2, CC3 + VMOVDQA CC1, tmpStoreAVX2 + chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1) + VMOVDQA tmpStoreAVX2, CC1 + + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0 + VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $4, DD1, DD1, DD1 + VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $4, DD2, DD2, DD2 + VPALIGNR $12, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $4, DD3, DD3, DD3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + + SUBQ $16, oup // Adjust the pointer + MOVQ $9, itr1 + JMP sealAVX2InternalLoopStart + +sealAVX2MainLoop: + // Load state, increment counter blocks, store the incremented counters + VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3 + VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3 + VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2 + MOVQ $10, itr1 + +sealAVX2InternalLoop: + polyAdd(0*8(oup)) + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + polyMulStage1_AVX2 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + polyMulStage2_AVX2 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyMulStage3_AVX2 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulReduceStage + +sealAVX2InternalLoopStart: + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + polyAdd(2*8(oup)) + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + polyMulStage1_AVX2 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulStage2_AVX2 + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + polyMulStage3_AVX2 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + polyMulReduceStage + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyAdd(4*8(oup)) + LEAQ (6*8)(oup), oup + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulStage1_AVX2 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + polyMulStage2_AVX2 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + polyMulStage3_AVX2 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyMulReduceStage + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3 + DECQ itr1 + JNE sealAVX2InternalLoop + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3 + VMOVDQA CC3, tmpStoreAVX2 + + // We only hashed 480 of the 512 bytes available - hash the remaining 32 here + polyAdd(0*8(oup)) + polyMulAVX2 + LEAQ (4*8)(oup), oup + VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0 + VPXOR (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0 + VMOVDQU CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup) + VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0 + VPXOR (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0 + VMOVDQU AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup) + + // and here + polyAdd(-2*8(oup)) + polyMulAVX2 + VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0 + VPXOR (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0 + VMOVDQU AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup) + VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0 + VPXOR (12*32)(inp), AA0, AA0; VPXOR (13*32)(inp), BB0, BB0; VPXOR (14*32)(inp), CC0, CC0; VPXOR (15*32)(inp), DD0, DD0 + VMOVDQU AA0, (12*32)(oup); VMOVDQU BB0, (13*32)(oup); VMOVDQU CC0, (14*32)(oup); VMOVDQU DD0, (15*32)(oup) + LEAQ (32*16)(inp), inp + SUBQ $(32*16), inl + CMPQ inl, $512 + JG sealAVX2MainLoop + + // Tail can only hash 480 bytes + polyAdd(0*8(oup)) + polyMulAVX2 + polyAdd(2*8(oup)) + polyMulAVX2 + LEAQ 32(oup), oup + + MOVQ $10, itr1 + MOVQ $0, itr2 + CMPQ inl, $128 + JBE sealAVX2Tail128 + CMPQ inl, $256 + JBE sealAVX2Tail256 + CMPQ inl, $384 + JBE sealAVX2Tail384 + JMP sealAVX2Tail512 + +// ---------------------------------------------------------------------------- +// Special optimization for buffers smaller than 193 bytes +seal192AVX2: + // For up to 192 bytes of ciphertext and 64 bytes for the poly key, we process four blocks + VMOVDQA AA0, AA1 + VMOVDQA BB0, BB1 + VMOVDQA CC0, CC1 + VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA AA0, AA2 + VMOVDQA BB0, BB2 + VMOVDQA CC0, CC2 + VMOVDQA DD0, DD2 + VMOVDQA DD1, TT3 + MOVQ $10, itr2 + +sealAVX2192InnerCipherLoop: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1 + DECQ itr2 + JNE sealAVX2192InnerCipherLoop + VPADDD AA2, AA0, AA0; VPADDD AA2, AA1, AA1 + VPADDD BB2, BB0, BB0; VPADDD BB2, BB1, BB1 + VPADDD CC2, CC0, CC0; VPADDD CC2, CC1, CC1 + VPADDD DD2, DD0, DD0; VPADDD TT3, DD1, DD1 + VPERM2I128 $0x02, AA0, BB0, TT0 + + // Clamp and store poly key + VPAND ·polyClampMask<>(SB), TT0, TT0 + VMOVDQA TT0, rsStoreAVX2 + + // Stream for up to 192 bytes + VPERM2I128 $0x13, AA0, BB0, AA0 + VPERM2I128 $0x13, CC0, DD0, BB0 + VPERM2I128 $0x02, AA1, BB1, CC0 + VPERM2I128 $0x02, CC1, DD1, DD0 + VPERM2I128 $0x13, AA1, BB1, AA1 + VPERM2I128 $0x13, CC1, DD1, BB1 + +sealAVX2ShortSeal: + // Hash aad + MOVQ ad_len+80(FP), itr2 + CALL polyHashADInternal<>(SB) + XORQ itr1, itr1 + +sealAVX2SealHash: + // itr1 holds the number of bytes encrypted but not yet hashed + CMPQ itr1, $16 + JB sealAVX2ShortSealLoop + polyAdd(0(oup)) + polyMul + SUBQ $16, itr1 + ADDQ $16, oup + JMP sealAVX2SealHash + +sealAVX2ShortSealLoop: + CMPQ inl, $32 + JB sealAVX2ShortTail32 + SUBQ $32, inl + + // Load for encryption + VPXOR (inp), AA0, AA0 + VMOVDQU AA0, (oup) + LEAQ (1*32)(inp), inp + + // Now can hash + polyAdd(0*8(oup)) + polyMulAVX2 + polyAdd(2*8(oup)) + polyMulAVX2 + LEAQ (1*32)(oup), oup + + // Shift stream left + VMOVDQA BB0, AA0 + VMOVDQA CC0, BB0 + VMOVDQA DD0, CC0 + VMOVDQA AA1, DD0 + VMOVDQA BB1, AA1 + VMOVDQA CC1, BB1 + VMOVDQA DD1, CC1 + VMOVDQA AA2, DD1 + VMOVDQA BB2, AA2 + JMP sealAVX2ShortSealLoop + +sealAVX2ShortTail32: + CMPQ inl, $16 + VMOVDQA A0, A1 + JB sealAVX2ShortDone + + SUBQ $16, inl + + // Load for encryption + VPXOR (inp), A0, T0 + VMOVDQU T0, (oup) + LEAQ (1*16)(inp), inp + + // Hash + polyAdd(0*8(oup)) + polyMulAVX2 + LEAQ (1*16)(oup), oup + VPERM2I128 $0x11, AA0, AA0, AA0 + VMOVDQA A0, A1 + +sealAVX2ShortDone: + VZEROUPPER + JMP sealSSETail + +// ---------------------------------------------------------------------------- +// Special optimization for buffers smaller than 321 bytes +seal320AVX2: + // For up to 320 bytes of ciphertext and 64 bytes for the poly key, we process six blocks + VMOVDQA AA0, AA1; VMOVDQA BB0, BB1; VMOVDQA CC0, CC1; VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA AA0, AA2; VMOVDQA BB0, BB2; VMOVDQA CC0, CC2; VPADDD ·avx2IncMask<>(SB), DD1, DD2 + VMOVDQA BB0, TT1; VMOVDQA CC0, TT2; VMOVDQA DD0, TT3 + MOVQ $10, itr2 + +sealAVX2320InnerCipherLoop: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2 + DECQ itr2 + JNE sealAVX2320InnerCipherLoop + + VMOVDQA ·chacha20Constants<>(SB), TT0 + VPADDD TT0, AA0, AA0; VPADDD TT0, AA1, AA1; VPADDD TT0, AA2, AA2 + VPADDD TT1, BB0, BB0; VPADDD TT1, BB1, BB1; VPADDD TT1, BB2, BB2 + VPADDD TT2, CC0, CC0; VPADDD TT2, CC1, CC1; VPADDD TT2, CC2, CC2 + VMOVDQA ·avx2IncMask<>(SB), TT0 + VPADDD TT3, DD0, DD0; VPADDD TT0, TT3, TT3 + VPADDD TT3, DD1, DD1; VPADDD TT0, TT3, TT3 + VPADDD TT3, DD2, DD2 + + // Clamp and store poly key + VPERM2I128 $0x02, AA0, BB0, TT0 + VPAND ·polyClampMask<>(SB), TT0, TT0 + VMOVDQA TT0, rsStoreAVX2 + + // Stream for up to 320 bytes + VPERM2I128 $0x13, AA0, BB0, AA0 + VPERM2I128 $0x13, CC0, DD0, BB0 + VPERM2I128 $0x02, AA1, BB1, CC0 + VPERM2I128 $0x02, CC1, DD1, DD0 + VPERM2I128 $0x13, AA1, BB1, AA1 + VPERM2I128 $0x13, CC1, DD1, BB1 + VPERM2I128 $0x02, AA2, BB2, CC1 + VPERM2I128 $0x02, CC2, DD2, DD1 + VPERM2I128 $0x13, AA2, BB2, AA2 + VPERM2I128 $0x13, CC2, DD2, BB2 + JMP sealAVX2ShortSeal + +// ---------------------------------------------------------------------------- +// Special optimization for the last 128 bytes of ciphertext +sealAVX2Tail128: + // Need to decrypt up to 128 bytes - prepare two blocks + // If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed + // If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed + VMOVDQA ·chacha20Constants<>(SB), AA0 + VMOVDQA state1StoreAVX2, BB0 + VMOVDQA state2StoreAVX2, CC0 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0 + VMOVDQA DD0, DD1 + +sealAVX2Tail128LoopA: + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealAVX2Tail128LoopB: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0) + polyAdd(0(oup)) + polyMul + VPALIGNR $4, BB0, BB0, BB0 + VPALIGNR $8, CC0, CC0, CC0 + VPALIGNR $12, DD0, DD0, DD0 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0) + polyAdd(16(oup)) + polyMul + LEAQ 32(oup), oup + VPALIGNR $12, BB0, BB0, BB0 + VPALIGNR $8, CC0, CC0, CC0 + VPALIGNR $4, DD0, DD0, DD0 + DECQ itr1 + JG sealAVX2Tail128LoopA + DECQ itr2 + JGE sealAVX2Tail128LoopB + + VPADDD ·chacha20Constants<>(SB), AA0, AA1 + VPADDD state1StoreAVX2, BB0, BB1 + VPADDD state2StoreAVX2, CC0, CC1 + VPADDD DD1, DD0, DD1 + + VPERM2I128 $0x02, AA1, BB1, AA0 + VPERM2I128 $0x02, CC1, DD1, BB0 + VPERM2I128 $0x13, AA1, BB1, CC0 + VPERM2I128 $0x13, CC1, DD1, DD0 + JMP sealAVX2ShortSealLoop + +// ---------------------------------------------------------------------------- +// Special optimization for the last 256 bytes of ciphertext +sealAVX2Tail256: + // Need to decrypt up to 256 bytes - prepare two blocks + // If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed + // If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA ·chacha20Constants<>(SB), AA1 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA state1StoreAVX2, BB1 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA state2StoreAVX2, CC1 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD1 + VMOVDQA DD0, TT1 + VMOVDQA DD1, TT2 + +sealAVX2Tail256LoopA: + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealAVX2Tail256LoopB: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + polyAdd(0(oup)) + polyMul + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0) + polyAdd(16(oup)) + polyMul + LEAQ 32(oup), oup + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1 + DECQ itr1 + JG sealAVX2Tail256LoopA + DECQ itr2 + JGE sealAVX2Tail256LoopB + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1 + VPADDD TT1, DD0, DD0; VPADDD TT2, DD1, DD1 + VPERM2I128 $0x02, AA0, BB0, TT0 + VPERM2I128 $0x02, CC0, DD0, TT1 + VPERM2I128 $0x13, AA0, BB0, TT2 + VPERM2I128 $0x13, CC0, DD0, TT3 + VPXOR (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3 + VMOVDQU TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup) + MOVQ $128, itr1 + LEAQ 128(inp), inp + SUBQ $128, inl + VPERM2I128 $0x02, AA1, BB1, AA0 + VPERM2I128 $0x02, CC1, DD1, BB0 + VPERM2I128 $0x13, AA1, BB1, CC0 + VPERM2I128 $0x13, CC1, DD1, DD0 + + JMP sealAVX2SealHash + +// ---------------------------------------------------------------------------- +// Special optimization for the last 384 bytes of ciphertext +sealAVX2Tail384: + // Need to decrypt up to 384 bytes - prepare two blocks + // If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed + // If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2 + VMOVDQA DD0, TT1; VMOVDQA DD1, TT2; VMOVDQA DD2, TT3 + +sealAVX2Tail384LoopA: + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealAVX2Tail384LoopB: + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + polyAdd(0(oup)) + polyMul + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2 + chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0) + polyAdd(16(oup)) + polyMul + LEAQ 32(oup), oup + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2 + DECQ itr1 + JG sealAVX2Tail384LoopA + DECQ itr2 + JGE sealAVX2Tail384LoopB + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2 + VPADDD TT1, DD0, DD0; VPADDD TT2, DD1, DD1; VPADDD TT3, DD2, DD2 + VPERM2I128 $0x02, AA0, BB0, TT0 + VPERM2I128 $0x02, CC0, DD0, TT1 + VPERM2I128 $0x13, AA0, BB0, TT2 + VPERM2I128 $0x13, CC0, DD0, TT3 + VPXOR (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3 + VMOVDQU TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup) + VPERM2I128 $0x02, AA1, BB1, TT0 + VPERM2I128 $0x02, CC1, DD1, TT1 + VPERM2I128 $0x13, AA1, BB1, TT2 + VPERM2I128 $0x13, CC1, DD1, TT3 + VPXOR (4*32)(inp), TT0, TT0; VPXOR (5*32)(inp), TT1, TT1; VPXOR (6*32)(inp), TT2, TT2; VPXOR (7*32)(inp), TT3, TT3 + VMOVDQU TT0, (4*32)(oup); VMOVDQU TT1, (5*32)(oup); VMOVDQU TT2, (6*32)(oup); VMOVDQU TT3, (7*32)(oup) + MOVQ $256, itr1 + LEAQ 256(inp), inp + SUBQ $256, inl + VPERM2I128 $0x02, AA2, BB2, AA0 + VPERM2I128 $0x02, CC2, DD2, BB0 + VPERM2I128 $0x13, AA2, BB2, CC0 + VPERM2I128 $0x13, CC2, DD2, DD0 + + JMP sealAVX2SealHash + +// ---------------------------------------------------------------------------- +// Special optimization for the last 512 bytes of ciphertext +sealAVX2Tail512: + // Need to decrypt up to 512 bytes - prepare two blocks + // If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed + // If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed + VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3 + VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3 + VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3 + VMOVDQA ctr3StoreAVX2, DD0 + VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3 + VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2 + +sealAVX2Tail512LoopA: + polyAdd(0(oup)) + polyMul + LEAQ 16(oup), oup + +sealAVX2Tail512LoopB: + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + polyAdd(0*8(oup)) + polyMulAVX2 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + polyAdd(2*8(oup)) + polyMulAVX2 + LEAQ (4*8)(oup), oup + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPADDD BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3 + VPXOR AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3 + VPSHUFB ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3 + VPADDD DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3 + VPXOR CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3 + VMOVDQA CC3, tmpStoreAVX2 + VPSLLD $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0 + VPSLLD $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1 + VPSLLD $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2 + VPSLLD $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3 + VMOVDQA tmpStoreAVX2, CC3 + VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3 + VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3 + VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3 + + DECQ itr1 + JG sealAVX2Tail512LoopA + DECQ itr2 + JGE sealAVX2Tail512LoopB + + VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3 + VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3 + VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3 + VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3 + VMOVDQA CC3, tmpStoreAVX2 + VPERM2I128 $0x02, AA0, BB0, CC3 + VPXOR (0*32)(inp), CC3, CC3 + VMOVDQU CC3, (0*32)(oup) + VPERM2I128 $0x02, CC0, DD0, CC3 + VPXOR (1*32)(inp), CC3, CC3 + VMOVDQU CC3, (1*32)(oup) + VPERM2I128 $0x13, AA0, BB0, CC3 + VPXOR (2*32)(inp), CC3, CC3 + VMOVDQU CC3, (2*32)(oup) + VPERM2I128 $0x13, CC0, DD0, CC3 + VPXOR (3*32)(inp), CC3, CC3 + VMOVDQU CC3, (3*32)(oup) + + VPERM2I128 $0x02, AA1, BB1, AA0 + VPERM2I128 $0x02, CC1, DD1, BB0 + VPERM2I128 $0x13, AA1, BB1, CC0 + VPERM2I128 $0x13, CC1, DD1, DD0 + VPXOR (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0 + VMOVDQU AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup) + + VPERM2I128 $0x02, AA2, BB2, AA0 + VPERM2I128 $0x02, CC2, DD2, BB0 + VPERM2I128 $0x13, AA2, BB2, CC0 + VPERM2I128 $0x13, CC2, DD2, DD0 + VPXOR (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0 + VMOVDQU AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup) + + MOVQ $384, itr1 + LEAQ 384(inp), inp + SUBQ $384, inl + VPERM2I128 $0x02, AA3, BB3, AA0 + VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0 + VPERM2I128 $0x13, AA3, BB3, CC0 + VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0 + + JMP sealAVX2SealHash diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go new file mode 100644 index 000000000..6313898f0 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go @@ -0,0 +1,81 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package chacha20poly1305 + +import ( + "encoding/binary" + + "golang.org/x/crypto/chacha20" + "golang.org/x/crypto/internal/alias" + "golang.org/x/crypto/internal/poly1305" +) + +func writeWithPadding(p *poly1305.MAC, b []byte) { + p.Write(b) + if rem := len(b) % 16; rem != 0 { + var buf [16]byte + padLen := 16 - rem + p.Write(buf[:padLen]) + } +} + +func writeUint64(p *poly1305.MAC, n int) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(n)) + p.Write(buf[:]) +} + +func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte { + ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize) + ciphertext, tag := out[:len(plaintext)], out[len(plaintext):] + if alias.InexactOverlap(out, plaintext) { + panic("chacha20poly1305: invalid buffer overlap") + } + + var polyKey [32]byte + s, _ := chacha20.NewUnauthenticatedCipher(c.key[:], nonce) + s.XORKeyStream(polyKey[:], polyKey[:]) + s.SetCounter(1) // set the counter to 1, skipping 32 bytes + s.XORKeyStream(ciphertext, plaintext) + + p := poly1305.New(&polyKey) + writeWithPadding(p, additionalData) + writeWithPadding(p, ciphertext) + writeUint64(p, len(additionalData)) + writeUint64(p, len(plaintext)) + p.Sum(tag[:0]) + + return ret +} + +func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + tag := ciphertext[len(ciphertext)-16:] + ciphertext = ciphertext[:len(ciphertext)-16] + + var polyKey [32]byte + s, _ := chacha20.NewUnauthenticatedCipher(c.key[:], nonce) + s.XORKeyStream(polyKey[:], polyKey[:]) + s.SetCounter(1) // set the counter to 1, skipping 32 bytes + + p := poly1305.New(&polyKey) + writeWithPadding(p, additionalData) + writeWithPadding(p, ciphertext) + writeUint64(p, len(additionalData)) + writeUint64(p, len(ciphertext)) + + ret, out := sliceForAppend(dst, len(ciphertext)) + if alias.InexactOverlap(out, ciphertext) { + panic("chacha20poly1305: invalid buffer overlap") + } + if !p.Verify(tag) { + for i := range out { + out[i] = 0 + } + return nil, errOpen + } + + s.XORKeyStream(out, ciphertext) + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go new file mode 100644 index 000000000..34e6ab1df --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !amd64 || !gc || purego + +package chacha20poly1305 + +func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { + return c.sealGeneric(dst, nonce, plaintext, additionalData) +} + +func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + return c.openGeneric(dst, nonce, ciphertext, additionalData) +} diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go b/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go new file mode 100644 index 000000000..1cebfe946 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package chacha20poly1305 + +import ( + "crypto/cipher" + "errors" + + "golang.org/x/crypto/chacha20" +) + +type xchacha20poly1305 struct { + key [KeySize]byte +} + +// NewX returns a XChaCha20-Poly1305 AEAD that uses the given 256-bit key. +// +// XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce, +// suitable to be generated randomly without risk of collisions. It should be +// preferred when nonce uniqueness cannot be trivially ensured, or whenever +// nonces are randomly generated. +func NewX(key []byte) (cipher.AEAD, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20poly1305: bad key length") + } + ret := new(xchacha20poly1305) + copy(ret.key[:], key) + return ret, nil +} + +func (*xchacha20poly1305) NonceSize() int { + return NonceSizeX +} + +func (*xchacha20poly1305) Overhead() int { + return Overhead +} + +func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != NonceSizeX { + panic("chacha20poly1305: bad nonce length passed to Seal") + } + + // XChaCha20-Poly1305 technically supports a 64-bit counter, so there is no + // size limit. However, since we reuse the ChaCha20-Poly1305 implementation, + // the second half of the counter is not available. This is unlikely to be + // an issue because the cipher.AEAD API requires the entire message to be in + // memory, and the counter overflows at 256 GB. + if uint64(len(plaintext)) > (1<<38)-64 { + panic("chacha20poly1305: plaintext too large") + } + + c := new(chacha20poly1305) + hKey, _ := chacha20.HChaCha20(x.key[:], nonce[0:16]) + copy(c.key[:], hKey) + + // The first 4 bytes of the final nonce are unused counter space. + cNonce := make([]byte, NonceSize) + copy(cNonce[4:12], nonce[16:24]) + + return c.seal(dst, cNonce[:], plaintext, additionalData) +} + +func (x *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != NonceSizeX { + panic("chacha20poly1305: bad nonce length passed to Open") + } + if len(ciphertext) < 16 { + return nil, errOpen + } + if uint64(len(ciphertext)) > (1<<38)-48 { + panic("chacha20poly1305: ciphertext too large") + } + + c := new(chacha20poly1305) + hKey, _ := chacha20.HChaCha20(x.key[:], nonce[0:16]) + copy(c.key[:], hKey) + + // The first 4 bytes of the final nonce are unused counter space. + cNonce := make([]byte, NonceSize) + copy(cNonce[4:12], nonce[16:24]) + + return c.open(dst, cNonce[:], ciphertext, additionalData) +} diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go new file mode 100644 index 000000000..f4ded5fee --- /dev/null +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -0,0 +1,95 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation +// Function (HKDF) as defined in RFC 5869. +// +// HKDF is a cryptographic key derivation function (KDF) with the goal of +// expanding limited input keying material into one or more cryptographically +// strong secret keys. +package hkdf // import "golang.org/x/crypto/hkdf" + +import ( + "crypto/hmac" + "errors" + "hash" + "io" +) + +// Extract generates a pseudorandom key for use with Expand from an input secret +// and an optional independent salt. +// +// Only use this function if you need to reuse the extracted key with multiple +// Expand invocations and different context values. Most common scenarios, +// including the generation of multiple keys, should use New instead. +func Extract(hash func() hash.Hash, secret, salt []byte) []byte { + if salt == nil { + salt = make([]byte, hash().Size()) + } + extractor := hmac.New(hash, salt) + extractor.Write(secret) + return extractor.Sum(nil) +} + +type hkdf struct { + expander hash.Hash + size int + + info []byte + counter byte + + prev []byte + buf []byte +} + +func (f *hkdf) Read(p []byte) (int, error) { + // Check whether enough data can be generated + need := len(p) + remains := len(f.buf) + int(255-f.counter+1)*f.size + if remains < need { + return 0, errors.New("hkdf: entropy limit reached") + } + // Read any leftover from the buffer + n := copy(p, f.buf) + p = p[n:] + + // Fill the rest of the buffer + for len(p) > 0 { + if f.counter > 1 { + f.expander.Reset() + } + f.expander.Write(f.prev) + f.expander.Write(f.info) + f.expander.Write([]byte{f.counter}) + f.prev = f.expander.Sum(f.prev[:0]) + f.counter++ + + // Copy the new batch into p + f.buf = f.prev + n = copy(p, f.buf) + p = p[n:] + } + // Save leftovers for next run + f.buf = f.buf[n:] + + return need, nil +} + +// Expand returns a Reader, from which keys can be read, using the given +// pseudorandom key and optional context info, skipping the extraction step. +// +// The pseudorandomKey should have been generated by Extract, or be a uniformly +// random or pseudorandom cryptographically strong key. See RFC 5869, Section +// 3.3. Most common scenarios will want to use New instead. +func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { + expander := hmac.New(hash, pseudorandomKey) + return &hkdf{expander, expander.Size(), info, 1, nil, nil} +} + +// New returns a Reader, from which keys can be read, using the given hash, +// secret, salt and context info. Salt and info can be nil. +func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { + prk := Extract(hash, secret, salt) + return Expand(hash, prk, info) +} diff --git a/vendor/golang.org/x/crypto/internal/alias/alias.go b/vendor/golang.org/x/crypto/internal/alias/alias.go new file mode 100644 index 000000000..551ff0c35 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/alias/alias.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego + +// Package alias implements memory aliasing tests. +package alias + +import "unsafe" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/vendor/golang.org/x/crypto/internal/alias/alias_purego.go b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go new file mode 100644 index 000000000..6fe61b5c6 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go @@ -0,0 +1,34 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build purego + +// Package alias implements memory aliasing tests. +package alias + +// This is the Google App Engine standard variant based on reflect +// because the unsafe package and cgo are disallowed. + +import "reflect" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() && + reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer() +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go new file mode 100644 index 000000000..333da285b --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego + +package poly1305 + +type mac struct{ macGeneric } diff --git a/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go b/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go new file mode 100644 index 000000000..4aaea810a --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go @@ -0,0 +1,99 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package poly1305 implements Poly1305 one-time message authentication code as +// specified in https://cr.yp.to/mac/poly1305-20050329.pdf. +// +// Poly1305 is a fast, one-time authentication function. It is infeasible for an +// attacker to generate an authenticator for a message without the key. However, a +// key must only be used for a single message. Authenticating two different +// messages with the same key allows an attacker to forge authenticators for other +// messages with the same key. +// +// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was +// used with a fixed key in order to generate one-time keys from an nonce. +// However, in this package AES isn't used and the one-time key is specified +// directly. +package poly1305 + +import "crypto/subtle" + +// TagSize is the size, in bytes, of a poly1305 authenticator. +const TagSize = 16 + +// Sum generates an authenticator for msg using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + h := New(key) + h.Write(m) + h.Sum(out[:0]) +} + +// Verify returns true if mac is a valid authenticator for m with the given key. +func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { + var tmp [16]byte + Sum(&tmp, m, key) + return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 +} + +// New returns a new MAC computing an authentication +// tag of all data written to it with the given key. +// This allows writing the message progressively instead +// of passing it as a single slice. Common users should use +// the Sum function instead. +// +// The key must be unique for each message, as authenticating +// two different messages with the same key allows an attacker +// to forge messages at will. +func New(key *[32]byte) *MAC { + m := &MAC{} + initialize(key, &m.macState) + return m +} + +// MAC is an io.Writer computing an authentication tag +// of the data written to it. +// +// MAC cannot be used like common hash.Hash implementations, +// because using a poly1305 key twice breaks its security. +// Therefore writing data to a running MAC after calling +// Sum or Verify causes it to panic. +type MAC struct { + mac // platform-dependent implementation + + finalized bool +} + +// Size returns the number of bytes Sum will return. +func (h *MAC) Size() int { return TagSize } + +// Write adds more data to the running message authentication code. +// It never returns an error. +// +// It must not be called after the first call of Sum or Verify. +func (h *MAC) Write(p []byte) (n int, err error) { + if h.finalized { + panic("poly1305: write to MAC after Sum or Verify") + } + return h.mac.Write(p) +} + +// Sum computes the authenticator of all data written to the +// message authentication code. +func (h *MAC) Sum(b []byte) []byte { + var mac [TagSize]byte + h.mac.Sum(&mac) + h.finalized = true + return append(b, mac[:]...) +} + +// Verify returns whether the authenticator of all data written to +// the message authentication code matches the expected value. +func (h *MAC) Verify(expected []byte) bool { + var mac [TagSize]byte + h.mac.Sum(&mac) + h.finalized = true + return subtle.ConstantTimeCompare(expected, mac[:]) == 1 +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go new file mode 100644 index 000000000..164cd47d3 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go @@ -0,0 +1,47 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package poly1305 + +//go:noescape +func update(state *macState, msg []byte) + +// mac is a wrapper for macGeneric that redirects calls that would have gone to +// updateGeneric to update. +// +// Its Write and Sum methods are otherwise identical to the macGeneric ones, but +// using function pointers would carry a major performance cost. +type mac struct{ macGeneric } + +func (h *mac) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + update(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + update(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +func (h *mac) Sum(out *[16]byte) { + state := h.macState + if h.offset > 0 { + update(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s new file mode 100644 index 000000000..e0d3c6475 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -0,0 +1,108 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#include "textflag.h" + +#define POLY1305_ADD(msg, h0, h1, h2) \ + ADDQ 0(msg), h0; \ + ADCQ 8(msg), h1; \ + ADCQ $1, h2; \ + LEAQ 16(msg), msg + +#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \ + MOVQ r0, AX; \ + MULQ h0; \ + MOVQ AX, t0; \ + MOVQ DX, t1; \ + MOVQ r0, AX; \ + MULQ h1; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ r0, t2; \ + IMULQ h2, t2; \ + ADDQ DX, t2; \ + \ + MOVQ r1, AX; \ + MULQ h0; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ DX, h0; \ + MOVQ r1, t3; \ + IMULQ h2, t3; \ + MOVQ r1, AX; \ + MULQ h1; \ + ADDQ AX, t2; \ + ADCQ DX, t3; \ + ADDQ h0, t2; \ + ADCQ $0, t3; \ + \ + MOVQ t0, h0; \ + MOVQ t1, h1; \ + MOVQ t2, h2; \ + ANDQ $3, h2; \ + MOVQ t2, t0; \ + ANDQ $0xFFFFFFFFFFFFFFFC, t0; \ + ADDQ t0, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2; \ + SHRQ $2, t3, t2; \ + SHRQ $2, t3; \ + ADDQ t2, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2 + +// func update(state *[7]uint64, msg []byte) +TEXT ·update(SB), $0-32 + MOVQ state+0(FP), DI + MOVQ msg_base+8(FP), SI + MOVQ msg_len+16(FP), R15 + + MOVQ 0(DI), R8 // h0 + MOVQ 8(DI), R9 // h1 + MOVQ 16(DI), R10 // h2 + MOVQ 24(DI), R11 // r0 + MOVQ 32(DI), R12 // r1 + + CMPQ R15, $16 + JB bytes_between_0_and_15 + +loop: + POLY1305_ADD(SI, R8, R9, R10) + +multiply: + POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) + SUBQ $16, R15 + CMPQ R15, $16 + JAE loop + +bytes_between_0_and_15: + TESTQ R15, R15 + JZ done + MOVQ $1, BX + XORQ CX, CX + XORQ R13, R13 + ADDQ R15, SI + +flush_buffer: + SHLQ $8, BX, CX + SHLQ $8, BX + MOVB -1(SI), R13 + XORQ R13, BX + DECQ SI + DECQ R15 + JNZ flush_buffer + + ADDQ BX, R8 + ADCQ CX, R9 + ADCQ $0, R10 + MOVQ $16, R15 + JMP multiply + +done: + MOVQ R8, 0(DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + RET diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go new file mode 100644 index 000000000..ec2202bd7 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go @@ -0,0 +1,312 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file provides the generic implementation of Sum and MAC. Other files +// might provide optimized assembly implementations of some of this code. + +package poly1305 + +import ( + "encoding/binary" + "math/bits" +) + +// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag +// for a 64 bytes message is approximately +// +// s + m[0:16] * râ´ + m[16:32] * r³ + m[32:48] * r² + m[48:64] * r mod 2¹³Ⱐ- 5 +// +// for some secret r and s. It can be computed sequentially like +// +// for len(msg) > 0: +// h += read(msg, 16) +// h *= r +// h %= 2¹³Ⱐ- 5 +// return h + s +// +// All the complexity is about doing performant constant-time math on numbers +// larger than any available numeric type. + +func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { + h := newMACGeneric(key) + h.Write(msg) + h.Sum(out) +} + +func newMACGeneric(key *[32]byte) macGeneric { + m := macGeneric{} + initialize(key, &m.macState) + return m +} + +// macState holds numbers in saturated 64-bit little-endian limbs. That is, +// the value of [x0, x1, x2] is x[0] + x[1] * 2â¶â´ + x[2] * 2¹²â¸. +type macState struct { + // h is the main accumulator. It is to be interpreted modulo 2¹³Ⱐ- 5, but + // can grow larger during and after rounds. It must, however, remain below + // 2 * (2¹³Ⱐ- 5). + h [3]uint64 + // r and s are the private key components. + r [2]uint64 + s [2]uint64 +} + +type macGeneric struct { + macState + + buffer [TagSize]byte + offset int +} + +// Write splits the incoming message into TagSize chunks, and passes them to +// update. It buffers incomplete chunks. +func (h *macGeneric) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + updateGeneric(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + updateGeneric(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +// Sum flushes the last incomplete chunk from the buffer, if any, and generates +// the MAC output. It does not modify its state, in order to allow for multiple +// calls to Sum, even if no Write is allowed after Sum. +func (h *macGeneric) Sum(out *[TagSize]byte) { + state := h.macState + if h.offset > 0 { + updateGeneric(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} + +// [rMask0, rMask1] is the specified Poly1305 clamping mask in little-endian. It +// clears some bits of the secret coefficient to make it possible to implement +// multiplication more efficiently. +const ( + rMask0 = 0x0FFFFFFC0FFFFFFF + rMask1 = 0x0FFFFFFC0FFFFFFC +) + +// initialize loads the 256-bit key into the two 128-bit secret values r and s. +func initialize(key *[32]byte, m *macState) { + m.r[0] = binary.LittleEndian.Uint64(key[0:8]) & rMask0 + m.r[1] = binary.LittleEndian.Uint64(key[8:16]) & rMask1 + m.s[0] = binary.LittleEndian.Uint64(key[16:24]) + m.s[1] = binary.LittleEndian.Uint64(key[24:32]) +} + +// uint128 holds a 128-bit number as two 64-bit limbs, for use with the +// bits.Mul64 and bits.Add64 intrinsics. +type uint128 struct { + lo, hi uint64 +} + +func mul64(a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + return uint128{lo, hi} +} + +func add128(a, b uint128) uint128 { + lo, c := bits.Add64(a.lo, b.lo, 0) + hi, c := bits.Add64(a.hi, b.hi, c) + if c != 0 { + panic("poly1305: unexpected overflow") + } + return uint128{lo, hi} +} + +func shiftRightBy2(a uint128) uint128 { + a.lo = a.lo>>2 | (a.hi&3)<<62 + a.hi = a.hi >> 2 + return a +} + +// updateGeneric absorbs msg into the state.h accumulator. For each chunk m of +// 128 bits of message, it computes +// +// hâ‚Š = (h + m) * r mod 2¹³Ⱐ- 5 +// +// If the msg length is not a multiple of TagSize, it assumes the last +// incomplete chunk is the final one. +func updateGeneric(state *macState, msg []byte) { + h0, h1, h2 := state.h[0], state.h[1], state.h[2] + r0, r1 := state.r[0], state.r[1] + + for len(msg) > 0 { + var c uint64 + + // For the first step, h + m, we use a chain of bits.Add64 intrinsics. + // The resulting value of h might exceed 2¹³Ⱐ- 5, but will be partially + // reduced at the end of the multiplication below. + // + // The spec requires us to set a bit just above the message size, not to + // hide leading zeroes. For full chunks, that's 1 << 128, so we can just + // add 1 to the most significant (2¹²â¸) limb, h2. + if len(msg) >= TagSize { + h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) + h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) + h2 += c + 1 + + msg = msg[TagSize:] + } else { + var buf [TagSize]byte + copy(buf[:], msg) + buf[len(msg)] = 1 + + h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) + h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) + h2 += c + + msg = nil + } + + // Multiplication of big number limbs is similar to elementary school + // columnar multiplication. Instead of digits, there are 64-bit limbs. + // + // We are multiplying a 3 limbs number, h, by a 2 limbs number, r. + // + // h2 h1 h0 x + // r1 r0 = + // ---------------- + // h2r0 h1r0 h0r0 <-- individual 128-bit products + // + h2r1 h1r1 h0r1 + // ------------------------ + // m3 m2 m1 m0 <-- result in 128-bit overlapping limbs + // ------------------------ + // m3.hi m2.hi m1.hi m0.hi <-- carry propagation + // + m3.lo m2.lo m1.lo m0.lo + // ------------------------------- + // t4 t3 t2 t1 t0 <-- final result in 64-bit limbs + // + // The main difference from pen-and-paper multiplication is that we do + // carry propagation in a separate step, as if we wrote two digit sums + // at first (the 128-bit limbs), and then carried the tens all at once. + + h0r0 := mul64(h0, r0) + h1r0 := mul64(h1, r0) + h2r0 := mul64(h2, r0) + h0r1 := mul64(h0, r1) + h1r1 := mul64(h1, r1) + h2r1 := mul64(h2, r1) + + // Since h2 is known to be at most 7 (5 + 1 + 1), and r0 and r1 have their + // top 4 bits cleared by rMask{0,1}, we know that their product is not going + // to overflow 64 bits, so we can ignore the high part of the products. + // + // This also means that the product doesn't have a fifth limb (t4). + if h2r0.hi != 0 { + panic("poly1305: unexpected overflow") + } + if h2r1.hi != 0 { + panic("poly1305: unexpected overflow") + } + + m0 := h0r0 + m1 := add128(h1r0, h0r1) // These two additions don't overflow thanks again + m2 := add128(h2r0, h1r1) // to the 4 masked bits at the top of r0 and r1. + m3 := h2r1 + + t0 := m0.lo + t1, c := bits.Add64(m1.lo, m0.hi, 0) + t2, c := bits.Add64(m2.lo, m1.hi, c) + t3, _ := bits.Add64(m3.lo, m2.hi, c) + + // Now we have the result as 4 64-bit limbs, and we need to reduce it + // modulo 2¹³Ⱐ- 5. The special shape of this Crandall prime lets us do + // a cheap partial reduction according to the reduction identity + // + // c * 2¹³Ⱐ+ n = c * 5 + n mod 2¹³Ⱐ- 5 + // + // because 2¹³Ⱐ= 5 mod 2¹³Ⱐ- 5. Partial reduction since the result is + // likely to be larger than 2¹³Ⱐ- 5, but still small enough to fit the + // assumptions we make about h in the rest of the code. + // + // See also https://speakerdeck.com/gtank/engineering-prime-numbers?slide=23 + + // We split the final result at the 2¹³Ⱐmark into h and cc, the carry. + // Note that the carry bits are effectively shifted left by 2, in other + // words, cc = c * 4 for the c in the reduction identity. + h0, h1, h2 = t0, t1, t2&maskLow2Bits + cc := uint128{t2 & maskNotLow2Bits, t3} + + // To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c. + + h0, c = bits.Add64(h0, cc.lo, 0) + h1, c = bits.Add64(h1, cc.hi, c) + h2 += c + + cc = shiftRightBy2(cc) + + h0, c = bits.Add64(h0, cc.lo, 0) + h1, c = bits.Add64(h1, cc.hi, c) + h2 += c + + // h2 is at most 3 + 1 + 1 = 5, making the whole of h at most + // + // 5 * 2¹²⸠+ (2¹²⸠- 1) = 6 * 2¹²⸠- 1 + } + + state.h[0], state.h[1], state.h[2] = h0, h1, h2 +} + +const ( + maskLow2Bits uint64 = 0x0000000000000003 + maskNotLow2Bits uint64 = ^maskLow2Bits +) + +// select64 returns x if v == 1 and y if v == 0, in constant time. +func select64(v, x, y uint64) uint64 { return ^(v-1)&x | (v-1)&y } + +// [p0, p1, p2] is 2¹³Ⱐ- 5 in little endian order. +const ( + p0 = 0xFFFFFFFFFFFFFFFB + p1 = 0xFFFFFFFFFFFFFFFF + p2 = 0x0000000000000003 +) + +// finalize completes the modular reduction of h and computes +// +// out = h + s mod 2¹²⸠+func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { + h0, h1, h2 := h[0], h[1], h[2] + + // After the partial reduction in updateGeneric, h might be more than + // 2¹³Ⱐ- 5, but will be less than 2 * (2¹³Ⱐ- 5). To complete the reduction + // in constant time, we compute t = h - (2¹³Ⱐ- 5), and select h as the + // result if the subtraction underflows, and t otherwise. + + hMinusP0, b := bits.Sub64(h0, p0, 0) + hMinusP1, b := bits.Sub64(h1, p1, b) + _, b = bits.Sub64(h2, p2, b) + + // h = h if h < p else h - p + h0 = select64(b, h0, hMinusP0) + h1 = select64(b, h1, hMinusP1) + + // Finally, we compute the last Poly1305 step + // + // tag = h + s mod 2¹²⸠+ // + // by just doing a wide addition with the 128 low bits of h and discarding + // the overflow. + h0, c := bits.Add64(h0, s[0], 0) + h1, _ = bits.Add64(h1, s[1], c) + + binary.LittleEndian.PutUint64(out[0:8], h0) + binary.LittleEndian.PutUint64(out[8:16], h1) +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go new file mode 100644 index 000000000..4aec4874b --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package poly1305 + +//go:noescape +func update(state *macState, msg []byte) + +// mac is a wrapper for macGeneric that redirects calls that would have gone to +// updateGeneric to update. +// +// Its Write and Sum methods are otherwise identical to the macGeneric ones, but +// using function pointers would carry a major performance cost. +type mac struct{ macGeneric } + +func (h *mac) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + update(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + update(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +func (h *mac) Sum(out *[16]byte) { + state := h.macState + if h.offset > 0 { + update(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s new file mode 100644 index 000000000..b3c1699bf --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s @@ -0,0 +1,179 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#include "textflag.h" + +// This was ported from the amd64 implementation. + +#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ + MOVD (msg), t0; \ + MOVD 8(msg), t1; \ + MOVD $1, t2; \ + ADDC t0, h0, h0; \ + ADDE t1, h1, h1; \ + ADDE t2, h2; \ + ADD $16, msg + +#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \ + MULLD r0, h0, t0; \ + MULHDU r0, h0, t1; \ + MULLD r0, h1, t4; \ + MULHDU r0, h1, t5; \ + ADDC t4, t1, t1; \ + MULLD r0, h2, t2; \ + MULHDU r1, h0, t4; \ + MULLD r1, h0, h0; \ + ADDE t5, t2, t2; \ + ADDC h0, t1, t1; \ + MULLD h2, r1, t3; \ + ADDZE t4, h0; \ + MULHDU r1, h1, t5; \ + MULLD r1, h1, t4; \ + ADDC t4, t2, t2; \ + ADDE t5, t3, t3; \ + ADDC h0, t2, t2; \ + MOVD $-4, t4; \ + ADDZE t3; \ + RLDICL $0, t2, $62, h2; \ + AND t2, t4, h0; \ + ADDC t0, h0, h0; \ + ADDE t3, t1, h1; \ + SLD $62, t3, t4; \ + SRD $2, t2; \ + ADDZE h2; \ + OR t4, t2, t2; \ + SRD $2, t3; \ + ADDC t2, h0, h0; \ + ADDE t3, h1, h1; \ + ADDZE h2 + +DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF +DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC +GLOBL ·poly1305Mask<>(SB), RODATA, $16 + +// func update(state *[7]uint64, msg []byte) +TEXT ·update(SB), $0-32 + MOVD state+0(FP), R3 + MOVD msg_base+8(FP), R4 + MOVD msg_len+16(FP), R5 + + MOVD 0(R3), R8 // h0 + MOVD 8(R3), R9 // h1 + MOVD 16(R3), R10 // h2 + MOVD 24(R3), R11 // r0 + MOVD 32(R3), R12 // r1 + + CMP R5, $16 + BLT bytes_between_0_and_15 + +loop: + POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22) + + PCALIGN $16 +multiply: + POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21) + ADD $-16, R5 + CMP R5, $16 + BGE loop + +bytes_between_0_and_15: + CMP R5, $0 + BEQ done + MOVD $0, R16 // h0 + MOVD $0, R17 // h1 + +flush_buffer: + CMP R5, $8 + BLE just1 + + MOVD $8, R21 + SUB R21, R5, R21 + + // Greater than 8 -- load the rightmost remaining bytes in msg + // and put into R17 (h1) + MOVD (R4)(R21), R17 + MOVD $16, R22 + + // Find the offset to those bytes + SUB R5, R22, R22 + SLD $3, R22 + + // Shift to get only the bytes in msg + SRD R22, R17, R17 + + // Put 1 at high end + MOVD $1, R23 + SLD $3, R21 + SLD R21, R23, R23 + OR R23, R17, R17 + + // Remainder is 8 + MOVD $8, R5 + +just1: + CMP R5, $8 + BLT less8 + + // Exactly 8 + MOVD (R4), R16 + + CMP R17, $0 + + // Check if we've already set R17; if not + // set 1 to indicate end of msg. + BNE carry + MOVD $1, R17 + BR carry + +less8: + MOVD $0, R16 // h0 + MOVD $0, R22 // shift count + CMP R5, $4 + BLT less4 + MOVWZ (R4), R16 + ADD $4, R4 + ADD $-4, R5 + MOVD $32, R22 + +less4: + CMP R5, $2 + BLT less2 + MOVHZ (R4), R21 + SLD R22, R21, R21 + OR R16, R21, R16 + ADD $16, R22 + ADD $-2, R5 + ADD $2, R4 + +less2: + CMP R5, $0 + BEQ insert1 + MOVBZ (R4), R21 + SLD R22, R21, R21 + OR R16, R21, R16 + ADD $8, R22 + +insert1: + // Insert 1 at end of msg + MOVD $1, R21 + SLD R22, R21, R21 + OR R16, R21, R16 + +carry: + // Add new values to h0, h1, h2 + ADDC R16, R8 + ADDE R17, R9 + ADDZE R10, R10 + MOVD $16, R5 + ADD R5, R4 + BR multiply + +done: + // Save h0, h1, h2 in state + MOVD R8, 0(R3) + MOVD R9, 8(R3) + MOVD R10, 16(R3) + RET diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go new file mode 100644 index 000000000..e1d033a49 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go @@ -0,0 +1,76 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +package poly1305 + +import ( + "golang.org/x/sys/cpu" +) + +// updateVX is an assembly implementation of Poly1305 that uses vector +// instructions. It must only be called if the vector facility (vx) is +// available. +// +//go:noescape +func updateVX(state *macState, msg []byte) + +// mac is a replacement for macGeneric that uses a larger buffer and redirects +// calls that would have gone to updateGeneric to updateVX if the vector +// facility is installed. +// +// A larger buffer is required for good performance because the vector +// implementation has a higher fixed cost per call than the generic +// implementation. +type mac struct { + macState + + buffer [16 * TagSize]byte // size must be a multiple of block size (16) + offset int +} + +func (h *mac) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < len(h.buffer) { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + if cpu.S390X.HasVX { + updateVX(&h.macState, h.buffer[:]) + } else { + updateGeneric(&h.macState, h.buffer[:]) + } + } + + tail := len(p) % len(h.buffer) // number of bytes to copy into buffer + body := len(p) - tail // number of bytes to process now + if body > 0 { + if cpu.S390X.HasVX { + updateVX(&h.macState, p[:body]) + } else { + updateGeneric(&h.macState, p[:body]) + } + } + h.offset = copy(h.buffer[:], p[body:]) // copy tail bytes - can be 0 + return nn, nil +} + +func (h *mac) Sum(out *[TagSize]byte) { + state := h.macState + remainder := h.buffer[:h.offset] + + // Use the generic implementation if we have 2 or fewer blocks left + // to sum. The vector implementation has a higher startup time. + if cpu.S390X.HasVX && len(remainder) > 2*TagSize { + updateVX(&state, remainder) + } else if len(remainder) > 0 { + updateGeneric(&state, remainder) + } + finalize(out, &state.h, &state.s) +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s new file mode 100644 index 000000000..0fe3a7c21 --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s @@ -0,0 +1,503 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#include "textflag.h" + +// This implementation of Poly1305 uses the vector facility (vx) +// to process up to 2 blocks (32 bytes) per iteration using an +// algorithm based on the one described in: +// +// NEON crypto, Daniel J. Bernstein & Peter Schwabe +// https://cryptojedi.org/papers/neoncrypto-20120320.pdf +// +// This algorithm uses 5 26-bit limbs to represent a 130-bit +// value. These limbs are, for the most part, zero extended and +// placed into 64-bit vector register elements. Each vector +// register is 128-bits wide and so holds 2 of these elements. +// Using 26-bit limbs allows us plenty of headroom to accommodate +// accumulations before and after multiplication without +// overflowing either 32-bits (before multiplication) or 64-bits +// (after multiplication). +// +// In order to parallelise the operations required to calculate +// the sum we use two separate accumulators and then sum those +// in an extra final step. For compatibility with the generic +// implementation we perform this summation at the end of every +// updateVX call. +// +// To use two accumulators we must multiply the message blocks +// by r² rather than r. Only the final message block should be +// multiplied by r. +// +// Example: +// +// We want to calculate the sum (h) for a 64 byte message (m): +// +// h = m[0:16]râ´ + m[16:32]r³ + m[32:48]r² + m[48:64]r +// +// To do this we split the calculation into the even indices +// and odd indices of the message. These form our SIMD 'lanes': +// +// h = m[ 0:16]râ´ + m[32:48]r² + <- lane 0 +// m[16:32]r³ + m[48:64]r <- lane 1 +// +// To calculate this iteratively we refactor so that both lanes +// are written in terms of r² and r: +// +// h = (m[ 0:16]r² + m[32:48])r² + <- lane 0 +// (m[16:32]r² + m[48:64])r <- lane 1 +// ^ ^ +// | coefficients for second iteration +// coefficients for first iteration +// +// So in this case we would have two iterations. In the first +// both lanes are multiplied by r². In the second only the +// first lane is multiplied by r² and the second lane is +// instead multiplied by r. This gives use the odd and even +// powers of r that we need from the original equation. +// +// Notation: +// +// h - accumulator +// r - key +// m - message +// +// [a, b] - SIMD register holding two 64-bit values +// [a, b, c, d] - SIMD register holding four 32-bit values +// xáµ¢[n] - limb n of variable x with bit width i +// +// Limbs are expressed in little endian order, so for 26-bit +// limbs x₂₆[4] will be the most significant limb and x₂₆[0] +// will be the least significant limb. + +// masking constants +#define MOD24 V0 // [0x0000000000ffffff, 0x0000000000ffffff] - mask low 24-bits +#define MOD26 V1 // [0x0000000003ffffff, 0x0000000003ffffff] - mask low 26-bits + +// expansion constants (see EXPAND macro) +#define EX0 V2 +#define EX1 V3 +#define EX2 V4 + +// key (r², r or 1 depending on context) +#define R_0 V5 +#define R_1 V6 +#define R_2 V7 +#define R_3 V8 +#define R_4 V9 + +// precalculated coefficients (5r², 5r or 0 depending on context) +#define R5_1 V10 +#define R5_2 V11 +#define R5_3 V12 +#define R5_4 V13 + +// message block (m) +#define M_0 V14 +#define M_1 V15 +#define M_2 V16 +#define M_3 V17 +#define M_4 V18 + +// accumulator (h) +#define H_0 V19 +#define H_1 V20 +#define H_2 V21 +#define H_3 V22 +#define H_4 V23 + +// temporary registers (for short-lived values) +#define T_0 V24 +#define T_1 V25 +#define T_2 V26 +#define T_3 V27 +#define T_4 V28 + +GLOBL ·constants<>(SB), RODATA, $0x30 +// EX0 +DATA ·constants<>+0x00(SB)/8, $0x0006050403020100 +DATA ·constants<>+0x08(SB)/8, $0x1016151413121110 +// EX1 +DATA ·constants<>+0x10(SB)/8, $0x060c0b0a09080706 +DATA ·constants<>+0x18(SB)/8, $0x161c1b1a19181716 +// EX2 +DATA ·constants<>+0x20(SB)/8, $0x0d0d0d0d0d0f0e0d +DATA ·constants<>+0x28(SB)/8, $0x1d1d1d1d1d1f1e1d + +// MULTIPLY multiplies each lane of f and g, partially reduced +// modulo 2¹³Ⱐ- 5. The result, h, consists of partial products +// in each lane that need to be reduced further to produce the +// final result. +// +// hâ‚₃₀ = (fâ‚₃₀gâ‚₃₀) % 2¹³Ⱐ+ (5fâ‚₃₀gâ‚₃₀) / 2¹³Ⱐ+// +// Note that the multiplication by 5 of the high bits is +// achieved by precalculating the multiplication of four of the +// g coefficients by 5. These are g51-g54. +#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \ + VMLOF f0, g0, h0 \ + VMLOF f0, g3, h3 \ + VMLOF f0, g1, h1 \ + VMLOF f0, g4, h4 \ + VMLOF f0, g2, h2 \ + VMLOF f1, g54, T_0 \ + VMLOF f1, g2, T_3 \ + VMLOF f1, g0, T_1 \ + VMLOF f1, g3, T_4 \ + VMLOF f1, g1, T_2 \ + VMALOF f2, g53, h0, h0 \ + VMALOF f2, g1, h3, h3 \ + VMALOF f2, g54, h1, h1 \ + VMALOF f2, g2, h4, h4 \ + VMALOF f2, g0, h2, h2 \ + VMALOF f3, g52, T_0, T_0 \ + VMALOF f3, g0, T_3, T_3 \ + VMALOF f3, g53, T_1, T_1 \ + VMALOF f3, g1, T_4, T_4 \ + VMALOF f3, g54, T_2, T_2 \ + VMALOF f4, g51, h0, h0 \ + VMALOF f4, g54, h3, h3 \ + VMALOF f4, g52, h1, h1 \ + VMALOF f4, g0, h4, h4 \ + VMALOF f4, g53, h2, h2 \ + VAG T_0, h0, h0 \ + VAG T_3, h3, h3 \ + VAG T_1, h1, h1 \ + VAG T_4, h4, h4 \ + VAG T_2, h2, h2 + +// REDUCE performs the following carry operations in four +// stages, as specified in Bernstein & Schwabe: +// +// 1: h₂₆[0]->h₂₆[1] h₂₆[3]->h₂₆[4] +// 2: h₂₆[1]->h₂₆[2] h₂₆[4]->h₂₆[0] +// 3: h₂₆[0]->h₂₆[1] h₂₆[2]->h₂₆[3] +// 4: h₂₆[3]->h₂₆[4] +// +// The result is that all of the limbs are limited to 26-bits +// except for h₂₆[1] and h₂₆[4] which are limited to 27-bits. +// +// Note that although each limb is aligned at 26-bit intervals +// they may contain values that exceed 2²ⶠ- 1, hence the need +// to carry the excess bits in each limb. +#define REDUCE(h0, h1, h2, h3, h4) \ + VESRLG $26, h0, T_0 \ + VESRLG $26, h3, T_1 \ + VN MOD26, h0, h0 \ + VN MOD26, h3, h3 \ + VAG T_0, h1, h1 \ + VAG T_1, h4, h4 \ + VESRLG $26, h1, T_2 \ + VESRLG $26, h4, T_3 \ + VN MOD26, h1, h1 \ + VN MOD26, h4, h4 \ + VESLG $2, T_3, T_4 \ + VAG T_3, T_4, T_4 \ + VAG T_2, h2, h2 \ + VAG T_4, h0, h0 \ + VESRLG $26, h2, T_0 \ + VESRLG $26, h0, T_1 \ + VN MOD26, h2, h2 \ + VN MOD26, h0, h0 \ + VAG T_0, h3, h3 \ + VAG T_1, h1, h1 \ + VESRLG $26, h3, T_2 \ + VN MOD26, h3, h3 \ + VAG T_2, h4, h4 + +// EXPAND splits the 128-bit little-endian values in0 and in1 +// into 26-bit big-endian limbs and places the results into +// the first and second lane of d₂₆[0:4] respectively. +// +// The EX0, EX1 and EX2 constants are arrays of byte indices +// for permutation. The permutation both reverses the bytes +// in the input and ensures the bytes are copied into the +// destination limb ready to be shifted into their final +// position. +#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \ + VPERM in0, in1, EX0, d0 \ + VPERM in0, in1, EX1, d2 \ + VPERM in0, in1, EX2, d4 \ + VESRLG $26, d0, d1 \ + VESRLG $30, d2, d3 \ + VESRLG $4, d2, d2 \ + VN MOD26, d0, d0 \ // [in0₂₆[0], in1₂₆[0]] + VN MOD26, d3, d3 \ // [in0₂₆[3], in1₂₆[3]] + VN MOD26, d1, d1 \ // [in0₂₆[1], in1₂₆[1]] + VN MOD24, d4, d4 \ // [in0₂₆[4], in1₂₆[4]] + VN MOD26, d2, d2 // [in0₂₆[2], in1₂₆[2]] + +// func updateVX(state *macState, msg []byte) +TEXT ·updateVX(SB), NOSPLIT, $0 + MOVD state+0(FP), R1 + LMG msg+8(FP), R2, R3 // R2=msg_base, R3=msg_len + + // load EX0, EX1 and EX2 + MOVD $·constants<>(SB), R5 + VLM (R5), EX0, EX2 + + // generate masks + VGMG $(64-24), $63, MOD24 // [0x00ffffff, 0x00ffffff] + VGMG $(64-26), $63, MOD26 // [0x03ffffff, 0x03ffffff] + + // load h (accumulator) and r (key) from state + VZERO T_1 // [0, 0] + VL 0(R1), T_0 // [h₆₄[0], h₆₄[1]] + VLEG $0, 16(R1), T_1 // [h₆₄[2], 0] + VL 24(R1), T_2 // [r₆₄[0], r₆₄[1]] + VPDI $0, T_0, T_2, T_3 // [h₆₄[0], r₆₄[0]] + VPDI $5, T_0, T_2, T_4 // [h₆₄[1], r₆₄[1]] + + // unpack h and r into 26-bit limbs + // note: h₆₄[2] may have the low 3 bits set, so h₂₆[4] is a 27-bit value + VN MOD26, T_3, H_0 // [h₂₆[0], r₂₆[0]] + VZERO H_1 // [0, 0] + VZERO H_3 // [0, 0] + VGMG $(64-12-14), $(63-12), T_0 // [0x03fff000, 0x03fff000] - 26-bit mask with low 12 bits masked out + VESLG $24, T_1, T_1 // [h₆₄[2]<<24, 0] + VERIMG $-26&63, T_3, MOD26, H_1 // [h₂₆[1], r₂₆[1]] + VESRLG $+52&63, T_3, H_2 // [h₂₆[2], r₂₆[2]] - low 12 bits only + VERIMG $-14&63, T_4, MOD26, H_3 // [h₂₆[1], r₂₆[1]] + VESRLG $40, T_4, H_4 // [h₂₆[4], r₂₆[4]] - low 24 bits only + VERIMG $+12&63, T_4, T_0, H_2 // [h₂₆[2], r₂₆[2]] - complete + VO T_1, H_4, H_4 // [h₂₆[4], r₂₆[4]] - complete + + // replicate r across all 4 vector elements + VREPF $3, H_0, R_0 // [r₂₆[0], r₂₆[0], r₂₆[0], r₂₆[0]] + VREPF $3, H_1, R_1 // [r₂₆[1], r₂₆[1], r₂₆[1], r₂₆[1]] + VREPF $3, H_2, R_2 // [r₂₆[2], r₂₆[2], r₂₆[2], r₂₆[2]] + VREPF $3, H_3, R_3 // [r₂₆[3], r₂₆[3], r₂₆[3], r₂₆[3]] + VREPF $3, H_4, R_4 // [r₂₆[4], r₂₆[4], r₂₆[4], r₂₆[4]] + + // zero out lane 1 of h + VLEIG $1, $0, H_0 // [h₂₆[0], 0] + VLEIG $1, $0, H_1 // [h₂₆[1], 0] + VLEIG $1, $0, H_2 // [h₂₆[2], 0] + VLEIG $1, $0, H_3 // [h₂₆[3], 0] + VLEIG $1, $0, H_4 // [h₂₆[4], 0] + + // calculate 5r (ignore least significant limb) + VREPIF $5, T_0 + VMLF T_0, R_1, R5_1 // [5r₂₆[1], 5r₂₆[1], 5r₂₆[1], 5r₂₆[1]] + VMLF T_0, R_2, R5_2 // [5r₂₆[2], 5r₂₆[2], 5r₂₆[2], 5r₂₆[2]] + VMLF T_0, R_3, R5_3 // [5r₂₆[3], 5r₂₆[3], 5r₂₆[3], 5r₂₆[3]] + VMLF T_0, R_4, R5_4 // [5r₂₆[4], 5r₂₆[4], 5r₂₆[4], 5r₂₆[4]] + + // skip r² calculation if we are only calculating one block + CMPBLE R3, $16, skip + + // calculate r² + MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, M_0, M_1, M_2, M_3, M_4) + REDUCE(M_0, M_1, M_2, M_3, M_4) + VGBM $0x0f0f, T_0 + VERIMG $0, M_0, T_0, R_0 // [r₂₆[0], r²₂₆[0], r₂₆[0], r²₂₆[0]] + VERIMG $0, M_1, T_0, R_1 // [r₂₆[1], r²₂₆[1], r₂₆[1], r²₂₆[1]] + VERIMG $0, M_2, T_0, R_2 // [r₂₆[2], r²₂₆[2], r₂₆[2], r²₂₆[2]] + VERIMG $0, M_3, T_0, R_3 // [r₂₆[3], r²₂₆[3], r₂₆[3], r²₂₆[3]] + VERIMG $0, M_4, T_0, R_4 // [r₂₆[4], r²₂₆[4], r₂₆[4], r²₂₆[4]] + + // calculate 5r² (ignore least significant limb) + VREPIF $5, T_0 + VMLF T_0, R_1, R5_1 // [5r₂₆[1], 5r²₂₆[1], 5r₂₆[1], 5r²₂₆[1]] + VMLF T_0, R_2, R5_2 // [5r₂₆[2], 5r²₂₆[2], 5r₂₆[2], 5r²₂₆[2]] + VMLF T_0, R_3, R5_3 // [5r₂₆[3], 5r²₂₆[3], 5r₂₆[3], 5r²₂₆[3]] + VMLF T_0, R_4, R5_4 // [5r₂₆[4], 5r²₂₆[4], 5r₂₆[4], 5r²₂₆[4]] + +loop: + CMPBLE R3, $32, b2 // 2 or fewer blocks remaining, need to change key coefficients + + // load next 2 blocks from message + VLM (R2), T_0, T_1 + + // update message slice + SUB $32, R3 + MOVD $32(R2), R2 + + // unpack message blocks into 26-bit big-endian limbs + EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4) + + // add 2¹²⸠to each message block value + VLEIB $4, $1, M_4 + VLEIB $12, $1, M_4 + +multiply: + // accumulate the incoming message + VAG H_0, M_0, M_0 + VAG H_3, M_3, M_3 + VAG H_1, M_1, M_1 + VAG H_4, M_4, M_4 + VAG H_2, M_2, M_2 + + // multiply the accumulator by the key coefficient + MULTIPLY(M_0, M_1, M_2, M_3, M_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4) + + // carry and partially reduce the partial products + REDUCE(H_0, H_1, H_2, H_3, H_4) + + CMPBNE R3, $0, loop + +finish: + // sum lane 0 and lane 1 and put the result in lane 1 + VZERO T_0 + VSUMQG H_0, T_0, H_0 + VSUMQG H_3, T_0, H_3 + VSUMQG H_1, T_0, H_1 + VSUMQG H_4, T_0, H_4 + VSUMQG H_2, T_0, H_2 + + // reduce again after summation + // TODO(mundaym): there might be a more efficient way to do this + // now that we only have 1 active lane. For example, we could + // simultaneously pack the values as we reduce them. + REDUCE(H_0, H_1, H_2, H_3, H_4) + + // carry h[1] through to h[4] so that only h[4] can exceed 2²ⶠ- 1 + // TODO(mundaym): in testing this final carry was unnecessary. + // Needs a proof before it can be removed though. + VESRLG $26, H_1, T_1 + VN MOD26, H_1, H_1 + VAQ T_1, H_2, H_2 + VESRLG $26, H_2, T_2 + VN MOD26, H_2, H_2 + VAQ T_2, H_3, H_3 + VESRLG $26, H_3, T_3 + VN MOD26, H_3, H_3 + VAQ T_3, H_4, H_4 + + // h is now < 2(2¹³Ⱐ- 5) + // Pack each lane in h₂₆[0:4] into hâ‚₂₈[0:1]. + VESLG $26, H_1, H_1 + VESLG $26, H_3, H_3 + VO H_0, H_1, H_0 + VO H_2, H_3, H_2 + VESLG $4, H_2, H_2 + VLEIB $7, $48, H_1 + VSLB H_1, H_2, H_2 + VO H_0, H_2, H_0 + VLEIB $7, $104, H_1 + VSLB H_1, H_4, H_3 + VO H_3, H_0, H_0 + VLEIB $7, $24, H_1 + VSRLB H_1, H_4, H_1 + + // update state + VSTEG $1, H_0, 0(R1) + VSTEG $0, H_0, 8(R1) + VSTEG $1, H_1, 16(R1) + RET + +b2: // 2 or fewer blocks remaining + CMPBLE R3, $16, b1 + + // Load the 2 remaining blocks (17-32 bytes remaining). + MOVD $-17(R3), R0 // index of final byte to load modulo 16 + VL (R2), T_0 // load full 16 byte block + VLL R0, 16(R2), T_1 // load final (possibly partial) block and pad with zeros to 16 bytes + + // The Poly1305 algorithm requires that a 1 bit be appended to + // each message block. If the final block is less than 16 bytes + // long then it is easiest to insert the 1 before the message + // block is split into 26-bit limbs. If, on the other hand, the + // final message block is 16 bytes long then we append the 1 bit + // after expansion as normal. + MOVBZ $1, R0 + MOVD $-16(R3), R3 // index of byte in last block to insert 1 at (could be 16) + CMPBEQ R3, $16, 2(PC) // skip the insertion if the final block is 16 bytes long + VLVGB R3, R0, T_1 // insert 1 into the byte at index R3 + + // Split both blocks into 26-bit limbs in the appropriate lanes. + EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4) + + // Append a 1 byte to the end of the second to last block. + VLEIB $4, $1, M_4 + + // Append a 1 byte to the end of the last block only if it is a + // full 16 byte block. + CMPBNE R3, $16, 2(PC) + VLEIB $12, $1, M_4 + + // Finally, set up the coefficients for the final multiplication. + // We have previously saved r and 5r in the 32-bit even indexes + // of the R_[0-4] and R5_[1-4] coefficient registers. + // + // We want lane 0 to be multiplied by r² so that can be kept the + // same. We want lane 1 to be multiplied by r so we need to move + // the saved r value into the 32-bit odd index in lane 1 by + // rotating the 64-bit lane by 32. + VGBM $0x00ff, T_0 // [0, 0xffffffffffffffff] - mask lane 1 only + VERIMG $32, R_0, T_0, R_0 // [_, r²₂₆[0], _, r₂₆[0]] + VERIMG $32, R_1, T_0, R_1 // [_, r²₂₆[1], _, r₂₆[1]] + VERIMG $32, R_2, T_0, R_2 // [_, r²₂₆[2], _, r₂₆[2]] + VERIMG $32, R_3, T_0, R_3 // [_, r²₂₆[3], _, r₂₆[3]] + VERIMG $32, R_4, T_0, R_4 // [_, r²₂₆[4], _, r₂₆[4]] + VERIMG $32, R5_1, T_0, R5_1 // [_, 5r²₂₆[1], _, 5r₂₆[1]] + VERIMG $32, R5_2, T_0, R5_2 // [_, 5r²₂₆[2], _, 5r₂₆[2]] + VERIMG $32, R5_3, T_0, R5_3 // [_, 5r²₂₆[3], _, 5r₂₆[3]] + VERIMG $32, R5_4, T_0, R5_4 // [_, 5r²₂₆[4], _, 5r₂₆[4]] + + MOVD $0, R3 + BR multiply + +skip: + CMPBEQ R3, $0, finish + +b1: // 1 block remaining + + // Load the final block (1-16 bytes). This will be placed into + // lane 0. + MOVD $-1(R3), R0 + VLL R0, (R2), T_0 // pad to 16 bytes with zeros + + // The Poly1305 algorithm requires that a 1 bit be appended to + // each message block. If the final block is less than 16 bytes + // long then it is easiest to insert the 1 before the message + // block is split into 26-bit limbs. If, on the other hand, the + // final message block is 16 bytes long then we append the 1 bit + // after expansion as normal. + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, T_0 + + // Set the message block in lane 1 to the value 0 so that it + // can be accumulated without affecting the final result. + VZERO T_1 + + // Split the final message block into 26-bit limbs in lane 0. + // Lane 1 will be contain 0. + EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4) + + // Append a 1 byte to the end of the last block only if it is a + // full 16 byte block. + CMPBNE R3, $16, 2(PC) + VLEIB $4, $1, M_4 + + // We have previously saved r and 5r in the 32-bit even indexes + // of the R_[0-4] and R5_[1-4] coefficient registers. + // + // We want lane 0 to be multiplied by r so we need to move the + // saved r value into the 32-bit odd index in lane 0. We want + // lane 1 to be set to the value 1. This makes multiplication + // a no-op. We do this by setting lane 1 in every register to 0 + // and then just setting the 32-bit index 3 in R_0 to 1. + VZERO T_0 + MOVD $0, R0 + MOVD $0x10111213, R12 + VLVGP R12, R0, T_1 // [_, 0x10111213, _, 0x00000000] + VPERM T_0, R_0, T_1, R_0 // [_, r₂₆[0], _, 0] + VPERM T_0, R_1, T_1, R_1 // [_, r₂₆[1], _, 0] + VPERM T_0, R_2, T_1, R_2 // [_, r₂₆[2], _, 0] + VPERM T_0, R_3, T_1, R_3 // [_, r₂₆[3], _, 0] + VPERM T_0, R_4, T_1, R_4 // [_, r₂₆[4], _, 0] + VPERM T_0, R5_1, T_1, R5_1 // [_, 5r₂₆[1], _, 0] + VPERM T_0, R5_2, T_1, R5_2 // [_, 5r₂₆[2], _, 0] + VPERM T_0, R5_3, T_1, R5_3 // [_, 5r₂₆[3], _, 0] + VPERM T_0, R5_4, T_1, R5_4 // [_, 5r₂₆[4], _, 0] + + // Set the value of lane 1 to be 1. + VLEIF $3, $1, R_0 // [_, r₂₆[0], _, 1] + + MOVD $0, R3 + BR multiply diff --git a/vendor/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/exp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/exp/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/exp/rand/exp.go b/vendor/golang.org/x/exp/rand/exp.go new file mode 100644 index 000000000..083867276 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/exp.go @@ -0,0 +1,221 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Exponential distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + re = 7.69711747013104972 +) + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1). +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func (r *Rand) ExpFloat64() float64 { + for { + j := r.Uint32() + i := j & 0xFF + x := float64(j) * float64(we[i]) + if j < ke[i] { + return x + } + if i == 0 { + return re - math.Log(r.Float64()) + } + if fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) { + return x + } + } +} + +var ke = [256]uint32{ + 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990, + 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8, + 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78, + 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651, + 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca, + 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8, + 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea, + 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba, + 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed, + 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662, + 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3, + 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace, + 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6, + 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7, + 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415, + 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4, + 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36, + 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46, + 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac, + 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245, + 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52, + 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06, + 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0, + 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9, + 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76, + 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516, + 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289, + 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed, + 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb, + 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e, + 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a, + 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1, + 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b, + 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621, + 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d, + 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3, + 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73, + 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88, + 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a, + 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb, + 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176, + 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be, + 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192, + 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed, + 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936, + 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b, + 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4, + 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1, + 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482, + 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023, + 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d, + 0xe6da6ecf, +} +var we = [256]float32{ + 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11, + 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11, + 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11, + 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11, + 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10, + 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10, + 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10, + 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10, + 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10, + 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10, + 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10, + 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10, + 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10, + 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10, + 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10, + 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10, + 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10, + 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10, + 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10, + 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10, + 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10, + 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10, + 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10, + 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10, + 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10, + 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10, + 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10, + 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10, + 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10, + 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10, + 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10, + 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10, + 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10, + 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10, + 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10, + 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10, + 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10, + 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10, + 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10, + 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10, + 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10, + 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10, + 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10, + 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10, + 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10, + 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10, + 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10, + 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10, + 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10, + 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10, + 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10, + 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10, + 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10, + 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10, + 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10, + 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10, + 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10, + 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10, + 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10, + 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09, + 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09, + 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09, + 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09, + 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09, +} +var fe = [256]float32{ + 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933, + 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686, + 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665, + 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967, + 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896, + 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092, + 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386, + 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495, + 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752, + 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325, + 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955, + 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694, + 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218, + 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763, + 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044, + 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796, + 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408, + 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928, + 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393, + 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625, + 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107, + 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878, + 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438, + 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682, + 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852, + 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479, + 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354, + 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494, + 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119, + 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624, + 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574, + 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672, + 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763, + 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816, + 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919, + 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274, + 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195, + 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106, + 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434, + 0.062193416, 0.060783047, 0.059384305, 0.057997175, + 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236, + 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623, + 0.043502413, 0.042254124, 0.041017443, 0.039792392, + 0.038578995, 0.037377283, 0.036187284, 0.035009038, + 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566, + 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421, + 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867, + 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392, + 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414, + 0.008780315, 0.007963077, 0.0071633533, 0.006381906, + 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648, + 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693, + 0.00045413437, +} diff --git a/vendor/golang.org/x/exp/rand/normal.go b/vendor/golang.org/x/exp/rand/normal.go new file mode 100644 index 000000000..b66da3a81 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/normal.go @@ -0,0 +1,156 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Normal distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + rn = 3.442619855899 +) + +func absInt32(i int32) uint32 { + if i < 0 { + return uint32(-i) + } + return uint32(i) +} + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1). +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func (r *Rand) NormFloat64() float64 { + for { + j := int32(r.Uint32()) // Possibly negative + i := j & 0x7F + x := float64(j) * float64(wn[i]) + if absInt32(j) < kn[i] { + // This case should be hit better than 99% of the time. + return x + } + + if i == 0 { + // This extra work is only required for the base strip. + for { + x = -math.Log(r.Float64()) * (1.0 / rn) + y := -math.Log(r.Float64()) + if y+y >= x*x { + break + } + } + if j > 0 { + return rn + x + } + return -rn - x + } + if fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) { + return x + } + } +} + +var kn = [128]uint32{ + 0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2, + 0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d, + 0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7, + 0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883, + 0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30, + 0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa, + 0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d, + 0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18, + 0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924, + 0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a, + 0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4, + 0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62, + 0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e, + 0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473, + 0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd, + 0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568, + 0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08, + 0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc, + 0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94, + 0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb, + 0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075, + 0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba, + 0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded, + 0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72, + 0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a, + 0x7ba90bdc, 0x7a722176, 0x77d664e5, +} +var wn = [128]float32{ + 1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10, + 2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10, + 2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10, + 3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10, + 3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10, + 4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10, + 4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10, + 4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10, + 5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10, + 5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10, + 5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10, + 5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10, + 6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10, + 6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10, + 6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10, + 6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10, + 7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10, + 7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10, + 7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10, + 7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10, + 8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10, + 8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10, + 8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10, + 9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10, + 9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10, + 9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09, + 1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09, + 1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09, + 1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09, + 1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09, + 1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09, + 1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09, +} +var fn = [128]float32{ + 1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303, + 0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177, + 0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569, + 0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277, + 0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434, + 0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903, + 0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055, + 0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766, + 0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872, + 0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642, + 0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446, + 0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685, + 0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484, + 0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807, + 0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239, + 0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877, + 0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499, + 0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037, + 0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265, + 0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602, + 0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467, + 0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058, + 0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863, + 0.040742867, 0.03688439, 0.033087887, 0.029356318, + 0.025693292, 0.022103304, 0.018592102, 0.015167298, + 0.011839478, 0.008624485, 0.005548995, 0.0026696292, +} diff --git a/vendor/golang.org/x/exp/rand/rand.go b/vendor/golang.org/x/exp/rand/rand.go new file mode 100644 index 000000000..ee6161bc6 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/rand.go @@ -0,0 +1,372 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rand implements pseudo-random number generators. +// +// Random numbers are generated by a Source. Top-level functions, such as +// Float64 and Int, use a default shared Source that produces a deterministic +// sequence of values each time a program is run. Use the Seed function to +// initialize the default Source if different behavior is required for each run. +// The default Source, a LockedSource, is safe for concurrent use by multiple +// goroutines, but Sources created by NewSource are not. However, Sources are small +// and it is reasonable to have a separate Source for each goroutine, seeded +// differently, to avoid locking. +// +// For random numbers suitable for security-sensitive work, see the crypto/rand +// package. +package rand + +import "sync" + +// A Source represents a source of uniformly-distributed +// pseudo-random int64 values in the range [0, 1<<64). +type Source interface { + Uint64() uint64 + Seed(seed uint64) +} + +// NewSource returns a new pseudo-random Source seeded with the given value. +func NewSource(seed uint64) Source { + var rng PCGSource + rng.Seed(seed) + return &rng +} + +// A Rand is a source of random numbers. +type Rand struct { + src Source + + // readVal contains remainder of 64-bit integer used for bytes + // generation during most recent Read call. + // It is saved so next Read call can start where the previous + // one finished. + readVal uint64 + // readPos indicates the number of low-order bytes of readVal + // that are still valid. + readPos int8 +} + +// New returns a new Rand that uses random values from src +// to generate other random values. +func New(src Source) *Rand { + return &Rand{src: src} +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +// Seed should not be called concurrently with any other Rand method. +func (r *Rand) Seed(seed uint64) { + if lk, ok := r.src.(*LockedSource); ok { + lk.seedPos(seed, &r.readPos) + return + } + + r.src.Seed(seed) + r.readPos = 0 +} + +// Uint64 returns a pseudo-random 64-bit integer as a uint64. +func (r *Rand) Uint64() uint64 { return r.src.Uint64() } + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +func (r *Rand) Int63() int64 { return int64(r.src.Uint64() &^ (1 << 63)) } + +// Uint32 returns a pseudo-random 32-bit value as a uint32. +func (r *Rand) Uint32() uint32 { return uint32(r.Uint64() >> 32) } + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32. +func (r *Rand) Int31() int32 { return int32(r.Uint64() >> 33) } + +// Int returns a non-negative pseudo-random int. +func (r *Rand) Int() int { + u := uint(r.Uint64()) + return int(u << 1 >> 1) // clear sign bit. +} + +const maxUint64 = (1 << 64) - 1 + +// Uint64n returns, as a uint64, a pseudo-random number in [0,n). +// It is guaranteed more uniform than taking a Source value mod n +// for any n that is not a power of 2. +func (r *Rand) Uint64n(n uint64) uint64 { + if n&(n-1) == 0 { // n is power of two, can mask + if n == 0 { + panic("invalid argument to Uint64n") + } + return r.Uint64() & (n - 1) + } + // If n does not divide v, to avoid bias we must not use + // a v that is within maxUint64%n of the top of the range. + v := r.Uint64() + if v > maxUint64-n { // Fast check. + ceiling := maxUint64 - maxUint64%n + for v >= ceiling { + v = r.Uint64() + } + } + + return v % n +} + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Int63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int63n") + } + return int64(r.Uint64n(uint64(n))) +} + +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Int31n(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int31n") + } + // TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines. + return int32(r.Uint64n(uint64(n))) +} + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } + // TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines. + return int(r.Uint64n(uint64(n))) +} + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0). +func (r *Rand) Float64() float64 { + // There is one bug in the value stream: r.Int63() may be so close + // to 1<<63 that the division rounds up to 1.0, and we've guaranteed + // that the result is always less than 1.0. + // + // We tried to fix this by mapping 1.0 back to 0.0, but since float64 + // values near 0 are much denser than near 1, mapping 1 to 0 caused + // a theoretically significant overshoot in the probability of returning 0. + // Instead of that, if we round up to 1, just try again. + // Getting 1 only happens 1/2âµÂ³ of the time, so most clients + // will not observe it anyway. +again: + f := float64(r.Uint64n(1<<53)) / (1 << 53) + if f == 1.0 { + goto again // resample; this branch is taken O(never) + } + return f +} + +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0). +func (r *Rand) Float32() float32 { + // We do not want to return 1.0. + // This only happens 1/2²ⴠof the time (plus the 1/2âµÂ³ of the time in Float64). +again: + f := float32(r.Float64()) + if f == 1 { + goto again // resample; this branch is taken O(very rarely) + } + return f +} + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n). +func (r *Rand) Perm(n int) []int { + m := make([]int, n) + // In the following loop, the iteration when i=0 always swaps m[0] with m[0]. + // A change to remove this useless iteration is to assign 1 to i in the init + // statement. But Perm also effects r. Making this change will affect + // the final state of r. So this change can't be made for compatibility + // reasons for Go 1. + for i := 0; i < n; i++ { + j := r.Intn(i + 1) + m[i] = m[j] + m[j] = i + } + return m +} + +// Shuffle pseudo-randomizes the order of elements. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func (r *Rand) Shuffle(n int, swap func(i, j int)) { + if n < 0 { + panic("invalid argument to Shuffle") + } + + // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + // Shuffle really ought not be called with n that doesn't fit in 32 bits. + // Not only will it take a very long time, but with 2³¹! possible permutations, + // there's no way that any PRNG can have a big enough internal state to + // generate even a minuscule percentage of the possible permutations. + // Nevertheless, the right API signature accepts an int n, so handle it as best we can. + i := n - 1 + for ; i > 1<<31-1-1; i-- { + j := int(r.Int63n(int64(i + 1))) + swap(i, j) + } + for ; i > 0; i-- { + j := int(r.Int31n(int32(i + 1))) + swap(i, j) + } +} + +// Read generates len(p) random bytes and writes them into p. It +// always returns len(p) and a nil error. +// Read should not be called concurrently with any other Rand method unless +// the underlying source is a LockedSource. +func (r *Rand) Read(p []byte) (n int, err error) { + if lk, ok := r.src.(*LockedSource); ok { + return lk.Read(p, &r.readVal, &r.readPos) + } + return read(p, r.src, &r.readVal, &r.readPos) +} + +func read(p []byte, src Source, readVal *uint64, readPos *int8) (n int, err error) { + pos := *readPos + val := *readVal + rng, _ := src.(*PCGSource) + for n = 0; n < len(p); n++ { + if pos == 0 { + if rng != nil { + val = rng.Uint64() + } else { + val = src.Uint64() + } + pos = 8 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + *readPos = pos + *readVal = val + return +} + +/* + * Top-level convenience functions + */ + +var globalRand = New(&LockedSource{src: *NewSource(1).(*PCGSource)}) + +// Type assert that globalRand's source is a LockedSource whose src is a PCGSource. +var _ PCGSource = globalRand.src.(*LockedSource).src + +// Seed uses the provided seed value to initialize the default Source to a +// deterministic state. If Seed is not called, the generator behaves as +// if seeded by Seed(1). +// Seed, unlike the Rand.Seed method, is safe for concurrent use. +func Seed(seed uint64) { globalRand.Seed(seed) } + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. +func Int63() int64 { return globalRand.Int63() } + +// Uint32 returns a pseudo-random 32-bit value as a uint32 +// from the default Source. +func Uint32() uint32 { return globalRand.Uint32() } + +// Uint64 returns a pseudo-random 64-bit value as a uint64 +// from the default Source. +func Uint64() uint64 { return globalRand.Uint64() } + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32 +// from the default Source. +func Int31() int32 { return globalRand.Int31() } + +// Int returns a non-negative pseudo-random int from the default Source. +func Int() int { return globalRand.Int() } + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Int63n(n int64) int64 { return globalRand.Int63n(n) } + +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Int31n(n int32) int32 { return globalRand.Int31n(n) } + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Intn(n int) int { return globalRand.Intn(n) } + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) +// from the default Source. +func Float64() float64 { return globalRand.Float64() } + +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) +// from the default Source. +func Float32() float32 { return globalRand.Float32() } + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n) +// from the default Source. +func Perm(n int) []int { return globalRand.Perm(n) } + +// Shuffle pseudo-randomizes the order of elements using the default Source. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func Shuffle(n int, swap func(i, j int)) { globalRand.Shuffle(n, swap) } + +// Read generates len(p) random bytes from the default Source and +// writes them into p. It always returns len(p) and a nil error. +// Read, unlike the Rand.Read method, is safe for concurrent use. +func Read(p []byte) (n int, err error) { return globalRand.Read(p) } + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1) +// from the default Source. +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func NormFloat64() float64 { return globalRand.NormFloat64() } + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func ExpFloat64() float64 { return globalRand.ExpFloat64() } + +// LockedSource is an implementation of Source that is concurrency-safe. +// A Rand using a LockedSource is safe for concurrent use. +// +// The zero value of LockedSource is valid, but should be seeded before use. +type LockedSource struct { + lk sync.Mutex + src PCGSource +} + +func (s *LockedSource) Uint64() (n uint64) { + s.lk.Lock() + n = s.src.Uint64() + s.lk.Unlock() + return +} + +func (s *LockedSource) Seed(seed uint64) { + s.lk.Lock() + s.src.Seed(seed) + s.lk.Unlock() +} + +// seedPos implements Seed for a LockedSource without a race condiiton. +func (s *LockedSource) seedPos(seed uint64, readPos *int8) { + s.lk.Lock() + s.src.Seed(seed) + *readPos = 0 + s.lk.Unlock() +} + +// Read implements Read for a LockedSource. +func (s *LockedSource) Read(p []byte, readVal *uint64, readPos *int8) (n int, err error) { + s.lk.Lock() + n, err = read(p, &s.src, readVal, readPos) + s.lk.Unlock() + return +} diff --git a/vendor/golang.org/x/exp/rand/rng.go b/vendor/golang.org/x/exp/rand/rng.go new file mode 100644 index 000000000..9b79108c7 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/rng.go @@ -0,0 +1,91 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "encoding/binary" + "io" + "math/bits" +) + +// PCGSource is an implementation of a 64-bit permuted congruential +// generator as defined in +// +// PCG: A Family of Simple Fast Space-Efficient Statistically Good +// Algorithms for Random Number Generation +// Melissa E. O’Neill, Harvey Mudd College +// http://www.pcg-random.org/pdf/toms-oneill-pcg-family-v1.02.pdf +// +// The generator here is the congruential generator PCG XSL RR 128/64 (LCG) +// as found in the software available at http://www.pcg-random.org/. +// It has period 2^128 with 128 bits of state, producing 64-bit values. +// Is state is represented by two uint64 words. +type PCGSource struct { + low uint64 + high uint64 +} + +const ( + maxUint32 = (1 << 32) - 1 + + multiplier = 47026247687942121848144207491837523525 + mulHigh = multiplier >> 64 + mulLow = multiplier & maxUint64 + + increment = 117397592171526113268558934119004209487 + incHigh = increment >> 64 + incLow = increment & maxUint64 + + // TODO: Use these? + initializer = 245720598905631564143578724636268694099 + initHigh = initializer >> 64 + initLow = initializer & maxUint64 +) + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +func (pcg *PCGSource) Seed(seed uint64) { + pcg.low = seed + pcg.high = seed // TODO: What is right? +} + +// Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64. +func (pcg *PCGSource) Uint64() uint64 { + pcg.multiply() + pcg.add() + // XOR high and low 64 bits together and rotate right by high 6 bits of state. + return bits.RotateLeft64(pcg.high^pcg.low, -int(pcg.high>>58)) +} + +func (pcg *PCGSource) add() { + var carry uint64 + pcg.low, carry = bits.Add64(pcg.low, incLow, 0) + pcg.high, _ = bits.Add64(pcg.high, incHigh, carry) +} + +func (pcg *PCGSource) multiply() { + hi, lo := bits.Mul64(pcg.low, mulLow) + hi += pcg.high * mulLow + hi += pcg.low * mulHigh + pcg.low = lo + pcg.high = hi +} + +// MarshalBinary returns the binary representation of the current state of the generator. +func (pcg *PCGSource) MarshalBinary() ([]byte, error) { + var buf [16]byte + binary.BigEndian.PutUint64(buf[:8], pcg.high) + binary.BigEndian.PutUint64(buf[8:], pcg.low) + return buf[:], nil +} + +// UnmarshalBinary sets the state of the generator to the state represented in data. +func (pcg *PCGSource) UnmarshalBinary(data []byte) error { + if len(data) < 16 { + return io.ErrUnexpectedEOF + } + pcg.low = binary.BigEndian.Uint64(data[8:]) + pcg.high = binary.BigEndian.Uint64(data[:8]) + return nil +} diff --git a/vendor/golang.org/x/exp/rand/zipf.go b/vendor/golang.org/x/exp/rand/zipf.go new file mode 100644 index 000000000..f04c814eb --- /dev/null +++ b/vendor/golang.org/x/exp/rand/zipf.go @@ -0,0 +1,77 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// W.Hormann, G.Derflinger: +// "Rejection-Inversion to Generate Variates +// from Monotone Discrete Distributions" +// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz + +package rand + +import "math" + +// A Zipf generates Zipf distributed variates. +type Zipf struct { + r *Rand + imax float64 + v float64 + q float64 + s float64 + oneminusQ float64 + oneminusQinv float64 + hxm float64 + hx0minusHxm float64 +} + +func (z *Zipf) h(x float64) float64 { + return math.Exp(z.oneminusQ*math.Log(z.v+x)) * z.oneminusQinv +} + +func (z *Zipf) hinv(x float64) float64 { + return math.Exp(z.oneminusQinv*math.Log(z.oneminusQ*x)) - z.v +} + +// NewZipf returns a Zipf variate generator. +// The generator generates values k ∈ [0, imax] +// such that P(k) is proportional to (v + k) ** (-s). +// Requirements: s > 1 and v >= 1. +func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf { + z := new(Zipf) + if s <= 1.0 || v < 1 { + return nil + } + z.r = r + z.imax = float64(imax) + z.v = v + z.q = s + z.oneminusQ = 1.0 - z.q + z.oneminusQinv = 1.0 / z.oneminusQ + z.hxm = z.h(z.imax + 0.5) + z.hx0minusHxm = z.h(0.5) - math.Exp(math.Log(z.v)*(-z.q)) - z.hxm + z.s = 1 - z.hinv(z.h(1.5)-math.Exp(-z.q*math.Log(z.v+1.0))) + return z +} + +// Uint64 returns a value drawn from the Zipf distribution described +// by the Zipf object. +func (z *Zipf) Uint64() uint64 { + if z == nil { + panic("rand: nil Zipf") + } + k := 0.0 + + for { + r := z.r.Float64() // r on [0,1] + ur := z.hxm + r*z.hx0minusHxm + x := z.hinv(ur) + k = math.Floor(x + 0.5) + if k-x <= z.s { + break + } + if ur >= z.h(k+0.5)-math.Exp(-math.Log(k+z.v)*z.q) { + break + } + } + return uint64(k) +} diff --git a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go new file mode 100644 index 000000000..150f887e7 --- /dev/null +++ b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go @@ -0,0 +1,78 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package lazyregexp is a thin wrapper over regexp, allowing the use of global +// regexp variables without forcing them to be compiled at init. +package lazyregexp + +import ( + "os" + "regexp" + "strings" + "sync" +) + +// Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be +// compiled the first time it is needed. +type Regexp struct { + str string + once sync.Once + rx *regexp.Regexp +} + +func (r *Regexp) re() *regexp.Regexp { + r.once.Do(r.build) + return r.rx +} + +func (r *Regexp) build() { + r.rx = regexp.MustCompile(r.str) + r.str = "" +} + +func (r *Regexp) FindSubmatch(s []byte) [][]byte { + return r.re().FindSubmatch(s) +} + +func (r *Regexp) FindStringSubmatch(s string) []string { + return r.re().FindStringSubmatch(s) +} + +func (r *Regexp) FindStringSubmatchIndex(s string) []int { + return r.re().FindStringSubmatchIndex(s) +} + +func (r *Regexp) ReplaceAllString(src, repl string) string { + return r.re().ReplaceAllString(src, repl) +} + +func (r *Regexp) FindString(s string) string { + return r.re().FindString(s) +} + +func (r *Regexp) FindAllString(s string, n int) []string { + return r.re().FindAllString(s, n) +} + +func (r *Regexp) MatchString(s string) bool { + return r.re().MatchString(s) +} + +func (r *Regexp) SubexpNames() []string { + return r.re().SubexpNames() +} + +var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") + +// New creates a new lazy regexp, delaying the compiling work until it is first +// needed. If the code is being run as part of tests, the regexp compiling will +// happen immediately. +func New(str string) *Regexp { + lr := &Regexp{str: str} + if inTest { + // In tests, always compile the regexps early. + lr.re() + } + return lr +} diff --git a/vendor/golang.org/x/mod/modfile/print.go b/vendor/golang.org/x/mod/modfile/print.go new file mode 100644 index 000000000..2a0123d4b --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/print.go @@ -0,0 +1,184 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Module file printer. + +package modfile + +import ( + "bytes" + "fmt" + "strings" +) + +// Format returns a go.mod file as a byte slice, formatted in standard style. +func Format(f *FileSyntax) []byte { + pr := &printer{} + pr.file(f) + + // remove trailing blank lines + b := pr.Bytes() + for len(b) > 0 && b[len(b)-1] == '\n' && (len(b) == 1 || b[len(b)-2] == '\n') { + b = b[:len(b)-1] + } + return b +} + +// A printer collects the state during printing of a file or expression. +type printer struct { + bytes.Buffer // output buffer + comment []Comment // pending end-of-line comments + margin int // left margin (indent), a number of tabs +} + +// printf prints to the buffer. +func (p *printer) printf(format string, args ...interface{}) { + fmt.Fprintf(p, format, args...) +} + +// indent returns the position on the current line, in bytes, 0-indexed. +func (p *printer) indent() int { + b := p.Bytes() + n := 0 + for n < len(b) && b[len(b)-1-n] != '\n' { + n++ + } + return n +} + +// newline ends the current line, flushing end-of-line comments. +func (p *printer) newline() { + if len(p.comment) > 0 { + p.printf(" ") + for i, com := range p.comment { + if i > 0 { + p.trim() + p.printf("\n") + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + } + p.printf("%s", strings.TrimSpace(com.Token)) + } + p.comment = p.comment[:0] + } + + p.trim() + if b := p.Bytes(); len(b) == 0 || (len(b) >= 2 && b[len(b)-1] == '\n' && b[len(b)-2] == '\n') { + // skip the blank line at top of file or after a blank line + } else { + p.printf("\n") + } + for i := 0; i < p.margin; i++ { + p.printf("\t") + } +} + +// trim removes trailing spaces and tabs from the current line. +func (p *printer) trim() { + // Remove trailing spaces and tabs from line we're about to end. + b := p.Bytes() + n := len(b) + for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') { + n-- + } + p.Truncate(n) +} + +// file formats the given file into the print buffer. +func (p *printer) file(f *FileSyntax) { + for _, com := range f.Before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + for i, stmt := range f.Stmt { + switch x := stmt.(type) { + case *CommentBlock: + // comments already handled + p.expr(x) + + default: + p.expr(x) + p.newline() + } + + for _, com := range stmt.Comment().After { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + if i+1 < len(f.Stmt) { + p.newline() + } + } +} + +func (p *printer) expr(x Expr) { + // Emit line-comments preceding this expression. + if before := x.Comment().Before; len(before) > 0 { + // Want to print a line comment. + // Line comments must be at the current margin. + p.trim() + if p.indent() > 0 { + // There's other text on the line. Start a new line. + p.printf("\n") + } + // Re-indent to margin. + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + for _, com := range before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + } + + switch x := x.(type) { + default: + panic(fmt.Errorf("printer: unexpected type %T", x)) + + case *CommentBlock: + // done + + case *LParen: + p.printf("(") + case *RParen: + p.printf(")") + + case *Line: + p.tokens(x.Token) + + case *LineBlock: + p.tokens(x.Token) + p.printf(" ") + p.expr(&x.LParen) + p.margin++ + for _, l := range x.Line { + p.newline() + p.expr(l) + } + p.margin-- + p.newline() + p.expr(&x.RParen) + } + + // Queue end-of-line comments for printing when we + // reach the end of the line. + p.comment = append(p.comment, x.Comment().Suffix...) +} + +func (p *printer) tokens(tokens []string) { + sep := "" + for _, t := range tokens { + if t == "," || t == ")" || t == "]" || t == "}" { + sep = "" + } + p.printf("%s%s", sep, t) + sep = " " + if t == "(" || t == "[" || t == "{" { + sep = "" + } + } +} diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go new file mode 100644 index 000000000..5b5bb5e11 --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -0,0 +1,958 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modfile + +import ( + "bytes" + "errors" + "fmt" + "os" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// A Position describes an arbitrary source position in a file, including the +// file, line, column, and byte offset. +type Position struct { + Line int // line in input (starting at 1) + LineRune int // rune in line (starting at 1) + Byte int // byte in input (starting at 0) +} + +// add returns the position at the end of s, assuming it starts at p. +func (p Position) add(s string) Position { + p.Byte += len(s) + if n := strings.Count(s, "\n"); n > 0 { + p.Line += n + s = s[strings.LastIndex(s, "\n")+1:] + p.LineRune = 1 + } + p.LineRune += utf8.RuneCountInString(s) + return p +} + +// An Expr represents an input element. +type Expr interface { + // Span returns the start and end position of the expression, + // excluding leading or trailing comments. + Span() (start, end Position) + + // Comment returns the comments attached to the expression. + // This method would normally be named 'Comments' but that + // would interfere with embedding a type of the same name. + Comment() *Comments +} + +// A Comment represents a single // comment. +type Comment struct { + Start Position + Token string // without trailing newline + Suffix bool // an end of line (not whole line) comment +} + +// Comments collects the comments associated with an expression. +type Comments struct { + Before []Comment // whole-line comments before this expression + Suffix []Comment // end-of-line comments after this expression + + // For top-level expressions only, After lists whole-line + // comments following the expression. + After []Comment +} + +// Comment returns the receiver. This isn't useful by itself, but +// a [Comments] struct is embedded into all the expression +// implementation types, and this gives each of those a Comment +// method to satisfy the Expr interface. +func (c *Comments) Comment() *Comments { + return c +} + +// A FileSyntax represents an entire go.mod file. +type FileSyntax struct { + Name string // file path + Comments + Stmt []Expr +} + +func (x *FileSyntax) Span() (start, end Position) { + if len(x.Stmt) == 0 { + return + } + start, _ = x.Stmt[0].Span() + _, end = x.Stmt[len(x.Stmt)-1].Span() + return start, end +} + +// addLine adds a line containing the given tokens to the file. +// +// If the first token of the hint matches the first token of the +// line, the new line is added at the end of the block containing hint, +// extracting hint into a new block if it is not yet in one. +// +// If the hint is non-nil buts its first token does not match, +// the new line is added after the block containing hint +// (or hint itself, if not in a block). +// +// If no hint is provided, addLine appends the line to the end of +// the last block with a matching first token, +// or to the end of the file if no such block exists. +func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line { + if hint == nil { + // If no hint given, add to the last statement of the given type. + Loop: + for i := len(x.Stmt) - 1; i >= 0; i-- { + stmt := x.Stmt[i] + switch stmt := stmt.(type) { + case *Line: + if stmt.Token != nil && stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + case *LineBlock: + if stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + } + } + } + + newLineAfter := func(i int) *Line { + new := &Line{Token: tokens} + if i == len(x.Stmt) { + x.Stmt = append(x.Stmt, new) + } else { + x.Stmt = append(x.Stmt, nil) + copy(x.Stmt[i+2:], x.Stmt[i+1:]) + x.Stmt[i+1] = new + } + return new + } + + if hint != nil { + for i, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt == hint { + if stmt.Token == nil || stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Convert line to line block. + stmt.InBlock = true + block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}} + stmt.Token = stmt.Token[1:] + x.Stmt[i] = block + new := &Line{Token: tokens[1:], InBlock: true} + block.Line = append(block.Line, new) + return new + } + + case *LineBlock: + if stmt == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line = append(stmt.Line, new) + return new + } + + for j, line := range stmt.Line { + if line == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Add new line after hint within the block. + stmt.Line = append(stmt.Line, nil) + copy(stmt.Line[j+2:], stmt.Line[j+1:]) + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line[j+1] = new + return new + } + } + } + } + } + + new := &Line{Token: tokens} + x.Stmt = append(x.Stmt, new) + return new +} + +func (x *FileSyntax) updateLine(line *Line, tokens ...string) { + if line.InBlock { + tokens = tokens[1:] + } + line.Token = tokens +} + +// markRemoved modifies line so that it (and its end-of-line comment, if any) +// will be dropped by (*FileSyntax).Cleanup. +func (line *Line) markRemoved() { + line.Token = nil + line.Comments.Suffix = nil +} + +// Cleanup cleans up the file syntax x after any edit operations. +// To avoid quadratic behavior, (*Line).markRemoved marks the line as dead +// by setting line.Token = nil but does not remove it from the slice +// in which it appears. After edits have all been indicated, +// calling Cleanup cleans out the dead lines. +func (x *FileSyntax) Cleanup() { + w := 0 + for _, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt.Token == nil { + continue + } + case *LineBlock: + ww := 0 + for _, line := range stmt.Line { + if line.Token != nil { + stmt.Line[ww] = line + ww++ + } + } + if ww == 0 { + continue + } + if ww == 1 { + // Collapse block into single line. + line := &Line{ + Comments: Comments{ + Before: commentsAdd(stmt.Before, stmt.Line[0].Before), + Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix), + After: commentsAdd(stmt.Line[0].After, stmt.After), + }, + Token: stringsAdd(stmt.Token, stmt.Line[0].Token), + } + x.Stmt[w] = line + w++ + continue + } + stmt.Line = stmt.Line[:ww] + } + x.Stmt[w] = stmt + w++ + } + x.Stmt = x.Stmt[:w] +} + +func commentsAdd(x, y []Comment) []Comment { + return append(x[:len(x):len(x)], y...) +} + +func stringsAdd(x, y []string) []string { + return append(x[:len(x):len(x)], y...) +} + +// A CommentBlock represents a top-level block of comments separate +// from any rule. +type CommentBlock struct { + Comments + Start Position +} + +func (x *CommentBlock) Span() (start, end Position) { + return x.Start, x.Start +} + +// A Line is a single line of tokens. +type Line struct { + Comments + Start Position + Token []string + InBlock bool + End Position +} + +func (x *Line) Span() (start, end Position) { + return x.Start, x.End +} + +// A LineBlock is a factored block of lines, like +// +// require ( +// "x" +// "y" +// ) +type LineBlock struct { + Comments + Start Position + LParen LParen + Token []string + Line []*Line + RParen RParen +} + +func (x *LineBlock) Span() (start, end Position) { + return x.Start, x.RParen.Pos.add(")") +} + +// An LParen represents the beginning of a parenthesized line block. +// It is a place to store suffix comments. +type LParen struct { + Comments + Pos Position +} + +func (x *LParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An RParen represents the end of a parenthesized line block. +// It is a place to store whole-line (before) comments. +type RParen struct { + Comments + Pos Position +} + +func (x *RParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An input represents a single input file being parsed. +type input struct { + // Lexing state. + filename string // name of input file, for errors + complete []byte // entire input + remaining []byte // remaining input + tokenStart []byte // token being scanned to end of input + token token // next token to be returned by lex, peek + pos Position // current input position + comments []Comment // accumulated comments + + // Parser state. + file *FileSyntax // returned top-level syntax tree + parseErrors ErrorList // errors encountered during parsing + + // Comment assignment state. + pre []Expr // all expressions, in preorder traversal + post []Expr // all expressions, in postorder traversal +} + +func newInput(filename string, data []byte) *input { + return &input{ + filename: filename, + complete: data, + remaining: data, + pos: Position{Line: 1, LineRune: 1, Byte: 0}, + } +} + +// parse parses the input file. +func parse(file string, data []byte) (f *FileSyntax, err error) { + // The parser panics for both routine errors like syntax errors + // and for programmer bugs like array index errors. + // Turn both into error returns. Catching bug panics is + // especially important when processing many files. + in := newInput(file, data) + defer func() { + if e := recover(); e != nil && e != &in.parseErrors { + in.parseErrors = append(in.parseErrors, Error{ + Filename: in.filename, + Pos: in.pos, + Err: fmt.Errorf("internal error: %v", e), + }) + } + if err == nil && len(in.parseErrors) > 0 { + err = in.parseErrors + } + }() + + // Prime the lexer by reading in the first token. It will be available + // in the next peek() or lex() call. + in.readToken() + + // Invoke the parser. + in.parseFile() + if len(in.parseErrors) > 0 { + return nil, in.parseErrors + } + in.file.Name = in.filename + + // Assign comments to nearby syntax. + in.assignComments() + + return in.file, nil +} + +// Error is called to report an error. +// Error does not return: it panics. +func (in *input) Error(s string) { + in.parseErrors = append(in.parseErrors, Error{ + Filename: in.filename, + Pos: in.pos, + Err: errors.New(s), + }) + panic(&in.parseErrors) +} + +// eof reports whether the input has reached end of file. +func (in *input) eof() bool { + return len(in.remaining) == 0 +} + +// peekRune returns the next rune in the input without consuming it. +func (in *input) peekRune() int { + if len(in.remaining) == 0 { + return 0 + } + r, _ := utf8.DecodeRune(in.remaining) + return int(r) +} + +// peekPrefix reports whether the remaining input begins with the given prefix. +func (in *input) peekPrefix(prefix string) bool { + // This is like bytes.HasPrefix(in.remaining, []byte(prefix)) + // but without the allocation of the []byte copy of prefix. + for i := 0; i < len(prefix); i++ { + if i >= len(in.remaining) || in.remaining[i] != prefix[i] { + return false + } + } + return true +} + +// readRune consumes and returns the next rune in the input. +func (in *input) readRune() int { + if len(in.remaining) == 0 { + in.Error("internal lexer error: readRune at EOF") + } + r, size := utf8.DecodeRune(in.remaining) + in.remaining = in.remaining[size:] + if r == '\n' { + in.pos.Line++ + in.pos.LineRune = 1 + } else { + in.pos.LineRune++ + } + in.pos.Byte += size + return int(r) +} + +type token struct { + kind tokenKind + pos Position + endPos Position + text string +} + +type tokenKind int + +const ( + _EOF tokenKind = -(iota + 1) + _EOLCOMMENT + _IDENT + _STRING + _COMMENT + + // newlines and punctuation tokens are allowed as ASCII codes. +) + +func (k tokenKind) isComment() bool { + return k == _COMMENT || k == _EOLCOMMENT +} + +// isEOL returns whether a token terminates a line. +func (k tokenKind) isEOL() bool { + return k == _EOF || k == _EOLCOMMENT || k == '\n' +} + +// startToken marks the beginning of the next input token. +// It must be followed by a call to endToken, once the token's text has +// been consumed using readRune. +func (in *input) startToken() { + in.tokenStart = in.remaining + in.token.text = "" + in.token.pos = in.pos +} + +// endToken marks the end of an input token. +// It records the actual token string in tok.text. +// A single trailing newline (LF or CRLF) will be removed from comment tokens. +func (in *input) endToken(kind tokenKind) { + in.token.kind = kind + text := string(in.tokenStart[:len(in.tokenStart)-len(in.remaining)]) + if kind.isComment() { + if strings.HasSuffix(text, "\r\n") { + text = text[:len(text)-2] + } else { + text = strings.TrimSuffix(text, "\n") + } + } + in.token.text = text + in.token.endPos = in.pos +} + +// peek returns the kind of the next token returned by lex. +func (in *input) peek() tokenKind { + return in.token.kind +} + +// lex is called from the parser to obtain the next input token. +func (in *input) lex() token { + tok := in.token + in.readToken() + return tok +} + +// readToken lexes the next token from the text and stores it in in.token. +func (in *input) readToken() { + // Skip past spaces, stopping at non-space or EOF. + for !in.eof() { + c := in.peekRune() + if c == ' ' || c == '\t' || c == '\r' { + in.readRune() + continue + } + + // Comment runs to end of line. + if in.peekPrefix("//") { + in.startToken() + + // Is this comment the only thing on its line? + // Find the last \n before this // and see if it's all + // spaces from there to here. + i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n")) + suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0 + in.readRune() + in.readRune() + + // Consume comment. + for len(in.remaining) > 0 && in.readRune() != '\n' { + } + + // If we are at top level (not in a statement), hand the comment to + // the parser as a _COMMENT token. The grammar is written + // to handle top-level comments itself. + if !suffix { + in.endToken(_COMMENT) + return + } + + // Otherwise, save comment for later attachment to syntax tree. + in.endToken(_EOLCOMMENT) + in.comments = append(in.comments, Comment{in.token.pos, in.token.text, suffix}) + return + } + + if in.peekPrefix("/*") { + in.Error("mod files must use // comments (not /* */ comments)") + } + + // Found non-space non-comment. + break + } + + // Found the beginning of the next token. + in.startToken() + + // End of file. + if in.eof() { + in.endToken(_EOF) + return + } + + // Punctuation tokens. + switch c := in.peekRune(); c { + case '\n', '(', ')', '[', ']', '{', '}', ',': + in.readRune() + in.endToken(tokenKind(c)) + return + + case '"', '`': // quoted string + quote := c + in.readRune() + for { + if in.eof() { + in.pos = in.token.pos + in.Error("unexpected EOF in string") + } + if in.peekRune() == '\n' { + in.Error("unexpected newline in string") + } + c := in.readRune() + if c == quote { + break + } + if c == '\\' && quote != '`' { + if in.eof() { + in.pos = in.token.pos + in.Error("unexpected EOF in string") + } + in.readRune() + } + } + in.endToken(_STRING) + return + } + + // Checked all punctuation. Must be identifier token. + if c := in.peekRune(); !isIdent(c) { + in.Error(fmt.Sprintf("unexpected input character %#q", c)) + } + + // Scan over identifier. + for isIdent(in.peekRune()) { + if in.peekPrefix("//") { + break + } + if in.peekPrefix("/*") { + in.Error("mod files must use // comments (not /* */ comments)") + } + in.readRune() + } + in.endToken(_IDENT) +} + +// isIdent reports whether c is an identifier rune. +// We treat most printable runes as identifier runes, except for a handful of +// ASCII punctuation characters. +func isIdent(c int) bool { + switch r := rune(c); r { + case ' ', '(', ')', '[', ']', '{', '}', ',': + return false + default: + return !unicode.IsSpace(r) && unicode.IsPrint(r) + } +} + +// Comment assignment. +// We build two lists of all subexpressions, preorder and postorder. +// The preorder list is ordered by start location, with outer expressions first. +// The postorder list is ordered by end location, with outer expressions last. +// We use the preorder list to assign each whole-line comment to the syntax +// immediately following it, and we use the postorder list to assign each +// end-of-line comment to the syntax immediately preceding it. + +// order walks the expression adding it and its subexpressions to the +// preorder and postorder lists. +func (in *input) order(x Expr) { + if x != nil { + in.pre = append(in.pre, x) + } + switch x := x.(type) { + default: + panic(fmt.Errorf("order: unexpected type %T", x)) + case nil: + // nothing + case *LParen, *RParen: + // nothing + case *CommentBlock: + // nothing + case *Line: + // nothing + case *FileSyntax: + for _, stmt := range x.Stmt { + in.order(stmt) + } + case *LineBlock: + in.order(&x.LParen) + for _, l := range x.Line { + in.order(l) + } + in.order(&x.RParen) + } + if x != nil { + in.post = append(in.post, x) + } +} + +// assignComments attaches comments to nearby syntax. +func (in *input) assignComments() { + const debug = false + + // Generate preorder and postorder lists. + in.order(in.file) + + // Split into whole-line comments and suffix comments. + var line, suffix []Comment + for _, com := range in.comments { + if com.Suffix { + suffix = append(suffix, com) + } else { + line = append(line, com) + } + } + + if debug { + for _, c := range line { + fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign line comments to syntax immediately following. + for _, x := range in.pre { + start, _ := x.Span() + if debug { + fmt.Fprintf(os.Stderr, "pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte) + } + xcom := x.Comment() + for len(line) > 0 && start.Byte >= line[0].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte) + } + xcom.Before = append(xcom.Before, line[0]) + line = line[1:] + } + } + + // Remaining line comments go at end of file. + in.file.After = append(in.file.After, line...) + + if debug { + for _, c := range suffix { + fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign suffix comments to syntax immediately before. + for i := len(in.post) - 1; i >= 0; i-- { + x := in.post[i] + + start, end := x.Span() + if debug { + fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte) + } + + // Do not assign suffix comments to end of line block or whole file. + // Instead assign them to the last element inside. + switch x.(type) { + case *FileSyntax: + continue + } + + // Do not assign suffix comments to something that starts + // on an earlier line, so that in + // + // x ( y + // z ) // comment + // + // we assign the comment to z and not to x ( ... ). + if start.Line != end.Line { + continue + } + xcom := x.Comment() + for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte) + } + xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) + suffix = suffix[:len(suffix)-1] + } + } + + // We assigned suffix comments in reverse. + // If multiple suffix comments were appended to the same + // expression node, they are now in reverse. Fix that. + for _, x := range in.post { + reverseComments(x.Comment().Suffix) + } + + // Remaining suffix comments go at beginning of file. + in.file.Before = append(in.file.Before, suffix...) +} + +// reverseComments reverses the []Comment list. +func reverseComments(list []Comment) { + for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] + } +} + +func (in *input) parseFile() { + in.file = new(FileSyntax) + var cb *CommentBlock + for { + switch in.peek() { + case '\n': + in.lex() + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + cb = nil + } + case _COMMENT: + tok := in.lex() + if cb == nil { + cb = &CommentBlock{Start: tok.pos} + } + com := cb.Comment() + com.Before = append(com.Before, Comment{Start: tok.pos, Token: tok.text}) + case _EOF: + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + } + return + default: + in.parseStmt() + if cb != nil { + in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before + cb = nil + } + } + } +} + +func (in *input) parseStmt() { + tok := in.lex() + start := tok.pos + end := tok.endPos + tokens := []string{tok.text} + for { + tok := in.lex() + switch { + case tok.kind.isEOL(): + in.file.Stmt = append(in.file.Stmt, &Line{ + Start: start, + Token: tokens, + End: end, + }) + return + + case tok.kind == '(': + if next := in.peek(); next.isEOL() { + // Start of block: no more tokens on this line. + in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, tokens, tok)) + return + } else if next == ')' { + rparen := in.lex() + if in.peek().isEOL() { + // Empty block. + in.lex() + in.file.Stmt = append(in.file.Stmt, &LineBlock{ + Start: start, + Token: tokens, + LParen: LParen{Pos: tok.pos}, + RParen: RParen{Pos: rparen.pos}, + }) + return + } + // '( )' in the middle of the line, not a block. + tokens = append(tokens, tok.text, rparen.text) + } else { + // '(' in the middle of the line, not a block. + tokens = append(tokens, tok.text) + } + + default: + tokens = append(tokens, tok.text) + end = tok.endPos + } + } +} + +func (in *input) parseLineBlock(start Position, token []string, lparen token) *LineBlock { + x := &LineBlock{ + Start: start, + Token: token, + LParen: LParen{Pos: lparen.pos}, + } + var comments []Comment + for { + switch in.peek() { + case _EOLCOMMENT: + // Suffix comment, will be attached later by assignComments. + in.lex() + case '\n': + // Blank line. Add an empty comment to preserve it. + in.lex() + if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" { + comments = append(comments, Comment{}) + } + case _COMMENT: + tok := in.lex() + comments = append(comments, Comment{Start: tok.pos, Token: tok.text}) + case _EOF: + in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) + case ')': + rparen := in.lex() + x.RParen.Before = comments + x.RParen.Pos = rparen.pos + if !in.peek().isEOL() { + in.Error("syntax error (expected newline after closing paren)") + } + in.lex() + return x + default: + l := in.parseLine() + x.Line = append(x.Line, l) + l.Comment().Before = comments + comments = nil + } + } +} + +func (in *input) parseLine() *Line { + tok := in.lex() + if tok.kind.isEOL() { + in.Error("internal parse error: parseLine at end of line") + } + start := tok.pos + end := tok.endPos + tokens := []string{tok.text} + for { + tok := in.lex() + if tok.kind.isEOL() { + return &Line{ + Start: start, + Token: tokens, + End: end, + InBlock: true, + } + } + tokens = append(tokens, tok.text) + end = tok.endPos + } +} + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// ModulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +func ModulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go new file mode 100644 index 000000000..35fd1f534 --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -0,0 +1,1663 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package modfile implements a parser and formatter for go.mod files. +// +// The go.mod syntax is described in +// https://pkg.go.dev/cmd/go/#hdr-The_go_mod_file. +// +// The [Parse] and [ParseLax] functions both parse a go.mod file and return an +// abstract syntax tree. ParseLax ignores unknown statements and may be used to +// parse go.mod files that may have been developed with newer versions of Go. +// +// The [File] struct returned by Parse and ParseLax represent an abstract +// go.mod file. File has several methods like [File.AddNewRequire] and +// [File.DropReplace] that can be used to programmatically edit a file. +// +// The [Format] function formats a File back to a byte slice which can be +// written to a file. +package modfile + +import ( + "errors" + "fmt" + "path/filepath" + "sort" + "strconv" + "strings" + "unicode" + + "golang.org/x/mod/internal/lazyregexp" + "golang.org/x/mod/module" + "golang.org/x/mod/semver" +) + +// A File is the parsed, interpreted form of a go.mod file. +type File struct { + Module *Module + Go *Go + Toolchain *Toolchain + Require []*Require + Exclude []*Exclude + Replace []*Replace + Retract []*Retract + + Syntax *FileSyntax +} + +// A Module is the module statement. +type Module struct { + Mod module.Version + Deprecated string + Syntax *Line +} + +// A Go is the go statement. +type Go struct { + Version string // "1.23" + Syntax *Line +} + +// A Toolchain is the toolchain statement. +type Toolchain struct { + Name string // "go1.21rc1" + Syntax *Line +} + +// An Exclude is a single exclude statement. +type Exclude struct { + Mod module.Version + Syntax *Line +} + +// A Replace is a single replace statement. +type Replace struct { + Old module.Version + New module.Version + Syntax *Line +} + +// A Retract is a single retract statement. +type Retract struct { + VersionInterval + Rationale string + Syntax *Line +} + +// A VersionInterval represents a range of versions with upper and lower bounds. +// Intervals are closed: both bounds are included. When Low is equal to High, +// the interval may refer to a single version ('v1.2.3') or an interval +// ('[v1.2.3, v1.2.3]'); both have the same representation. +type VersionInterval struct { + Low, High string +} + +// A Require is a single require statement. +type Require struct { + Mod module.Version + Indirect bool // has "// indirect" comment + Syntax *Line +} + +func (r *Require) markRemoved() { + r.Syntax.markRemoved() + *r = Require{} +} + +func (r *Require) setVersion(v string) { + r.Mod.Version = v + + if line := r.Syntax; len(line.Token) > 0 { + if line.InBlock { + // If the line is preceded by an empty line, remove it; see + // https://golang.org/issue/33779. + if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 { + line.Comments.Before = line.Comments.Before[:0] + } + if len(line.Token) >= 2 { // example.com v1.2.3 + line.Token[1] = v + } + } else { + if len(line.Token) >= 3 { // require example.com v1.2.3 + line.Token[2] = v + } + } + } +} + +// setIndirect sets line to have (or not have) a "// indirect" comment. +func (r *Require) setIndirect(indirect bool) { + r.Indirect = indirect + line := r.Syntax + if isIndirect(line) == indirect { + return + } + if indirect { + // Adding comment. + if len(line.Suffix) == 0 { + // New comment. + line.Suffix = []Comment{{Token: "// indirect", Suffix: true}} + return + } + + com := &line.Suffix[0] + text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash))) + if text == "" { + // Empty comment. + com.Token = "// indirect" + return + } + + // Insert at beginning of existing comment. + com.Token = "// indirect; " + text + return + } + + // Removing comment. + f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + if f == "indirect" { + // Remove whole comment. + line.Suffix = nil + return + } + + // Remove comment prefix. + com := &line.Suffix[0] + i := strings.Index(com.Token, "indirect;") + com.Token = "//" + com.Token[i+len("indirect;"):] +} + +// isIndirect reports whether line has a "// indirect" comment, +// meaning it is in go.mod only for its effect on indirect dependencies, +// so that it can be dropped entirely once the effective version of the +// indirect dependency reaches the given minimum version. +func isIndirect(line *Line) bool { + if len(line.Suffix) == 0 { + return false + } + f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;") +} + +func (f *File) AddModuleStmt(path string) error { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + if f.Module == nil { + f.Module = &Module{ + Mod: module.Version{Path: path}, + Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)), + } + } else { + f.Module.Mod.Path = path + f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path)) + } + return nil +} + +func (f *File) AddComment(text string) { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{ + Comments: Comments{ + Before: []Comment{ + { + Token: text, + }, + }, + }, + }) +} + +type VersionFixer func(path, version string) (string, error) + +// errDontFix is returned by a VersionFixer to indicate the version should be +// left alone, even if it's not canonical. +var dontFixRetract VersionFixer = func(_, vers string) (string, error) { + return vers, nil +} + +// Parse parses and returns a go.mod file. +// +// file is the name of the file, used in positions and errors. +// +// data is the content of the file. +// +// fix is an optional function that canonicalizes module versions. +// If fix is nil, all module versions must be canonical ([module.CanonicalVersion] +// must return the same string). +func Parse(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, true) +} + +// ParseLax is like Parse but ignores unknown statements. +// It is used when parsing go.mod files other than the main module, +// under the theory that most statement types we add in the future will +// only apply in the main module, like exclude and replace, +// and so we get better gradual deployments if old go commands +// simply ignore those statements when found in go.mod files +// in dependencies. +func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, false) +} + +func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parsed *File, err error) { + fs, err := parse(file, data) + if err != nil { + return nil, err + } + f := &File{ + Syntax: fs, + } + var errs ErrorList + + // fix versions in retract directives after the file is parsed. + // We need the module path to fix versions, and it might be at the end. + defer func() { + oldLen := len(errs) + f.fixRetract(fix, &errs) + if len(errs) > oldLen { + parsed, err = nil, errs + } + }() + + for _, x := range fs.Stmt { + switch x := x.(type) { + case *Line: + f.add(&errs, nil, x, x.Token[0], x.Token[1:], fix, strict) + + case *LineBlock: + if len(x.Token) > 1 { + if strict { + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + } + continue + } + switch x.Token[0] { + default: + if strict { + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + } + continue + case "module", "require", "exclude", "replace", "retract": + for _, l := range x.Line { + f.add(&errs, x, l, x.Token[0], l.Token, fix, strict) + } + } + } + } + + if len(errs) > 0 { + return nil, errs + } + return f, nil +} + +var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`) +var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`) + +// Toolchains must be named beginning with `go1`, +// like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted. +var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`) + +func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { + // If strict is false, this module is a dependency. + // We ignore all unknown directives as well as main-module-only + // directives like replace and exclude. It will work better for + // forward compatibility if we can depend on modules that have unknown + // statements (presumed relevant only when acting as the main module) + // and simply ignore those statements. + if !strict { + switch verb { + case "go", "module", "retract", "require": + // want these even for dependency go.mods + default: + return + } + } + + wrapModPathError := func(modPath string, err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: line.Start, + ModPath: modPath, + Verb: verb, + Err: err, + }) + } + wrapError := func(err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: line.Start, + Err: err, + }) + } + errorf := func(format string, args ...interface{}) { + wrapError(fmt.Errorf(format, args...)) + } + + switch verb { + default: + errorf("unknown directive: %s", verb) + + case "go": + if f.Go != nil { + errorf("repeated go statement") + return + } + if len(args) != 1 { + errorf("go directive expects exactly one argument") + return + } else if !GoVersionRE.MatchString(args[0]) { + fixed := false + if !strict { + if m := laxGoVersionRE.FindStringSubmatch(args[0]); m != nil { + args[0] = m[1] + fixed = true + } + } + if !fixed { + errorf("invalid go version '%s': must match format 1.23.0", args[0]) + return + } + } + + f.Go = &Go{Syntax: line} + f.Go.Version = args[0] + + case "toolchain": + if f.Toolchain != nil { + errorf("repeated toolchain statement") + return + } + if len(args) != 1 { + errorf("toolchain directive expects exactly one argument") + return + } else if strict && !ToolchainRE.MatchString(args[0]) { + errorf("invalid toolchain version '%s': must match format go1.23.0 or local", args[0]) + return + } + f.Toolchain = &Toolchain{Syntax: line} + f.Toolchain.Name = args[0] + + case "module": + if f.Module != nil { + errorf("repeated module statement") + return + } + deprecated := parseDeprecation(block, line) + f.Module = &Module{ + Syntax: line, + Deprecated: deprecated, + } + if len(args) != 1 { + errorf("usage: module module/path") + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + f.Module.Mod = module.Version{Path: s} + + case "require", "exclude": + if len(args) != 2 { + errorf("usage: %s module/path v1.2.3", verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + v, err := parseVersion(verb, s, &args[1], fix) + if err != nil { + wrapError(err) + return + } + pathMajor, err := modulePathMajor(s) + if err != nil { + wrapError(err) + return + } + if err := module.CheckPathMajor(v, pathMajor); err != nil { + wrapModPathError(s, err) + return + } + if verb == "require" { + f.Require = append(f.Require, &Require{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + Indirect: isIndirect(line), + }) + } else { + f.Exclude = append(f.Exclude, &Exclude{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + }) + } + + case "replace": + replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix) + if wrappederr != nil { + *errs = append(*errs, *wrappederr) + return + } + f.Replace = append(f.Replace, replace) + + case "retract": + rationale := parseDirectiveComment(block, line) + vi, err := parseVersionInterval(verb, "", &args, dontFixRetract) + if err != nil { + if strict { + wrapError(err) + return + } else { + // Only report errors parsing intervals in the main module. We may + // support additional syntax in the future, such as open and half-open + // intervals. Those can't be supported now, because they break the + // go.mod parser, even in lax mode. + return + } + } + if len(args) > 0 && strict { + // In the future, there may be additional information after the version. + errorf("unexpected token after version: %q", args[0]) + return + } + retract := &Retract{ + VersionInterval: vi, + Rationale: rationale, + Syntax: line, + } + f.Retract = append(f.Retract, retract) + } +} + +func parseReplace(filename string, line *Line, verb string, args []string, fix VersionFixer) (*Replace, *Error) { + wrapModPathError := func(modPath string, err error) *Error { + return &Error{ + Filename: filename, + Pos: line.Start, + ModPath: modPath, + Verb: verb, + Err: err, + } + } + wrapError := func(err error) *Error { + return &Error{ + Filename: filename, + Pos: line.Start, + Err: err, + } + } + errorf := func(format string, args ...interface{}) *Error { + return wrapError(fmt.Errorf(format, args...)) + } + + arrow := 2 + if len(args) >= 2 && args[1] == "=>" { + arrow = 1 + } + if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" { + return nil, errorf("usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", verb, verb) + } + s, err := parseString(&args[0]) + if err != nil { + return nil, errorf("invalid quoted string: %v", err) + } + pathMajor, err := modulePathMajor(s) + if err != nil { + return nil, wrapModPathError(s, err) + + } + var v string + if arrow == 2 { + v, err = parseVersion(verb, s, &args[1], fix) + if err != nil { + return nil, wrapError(err) + } + if err := module.CheckPathMajor(v, pathMajor); err != nil { + return nil, wrapModPathError(s, err) + } + } + ns, err := parseString(&args[arrow+1]) + if err != nil { + return nil, errorf("invalid quoted string: %v", err) + } + nv := "" + if len(args) == arrow+2 { + if !IsDirectoryPath(ns) { + if strings.Contains(ns, "@") { + return nil, errorf("replacement module must match format 'path version', not 'path@version'") + } + return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)") + } + if filepath.Separator == '/' && strings.Contains(ns, `\`) { + return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)") + } + } + if len(args) == arrow+3 { + nv, err = parseVersion(verb, ns, &args[arrow+2], fix) + if err != nil { + return nil, wrapError(err) + } + if IsDirectoryPath(ns) { + return nil, errorf("replacement module directory path %q cannot have version", ns) + } + } + return &Replace{ + Old: module.Version{Path: s, Version: v}, + New: module.Version{Path: ns, Version: nv}, + Syntax: line, + }, nil +} + +// fixRetract applies fix to each retract directive in f, appending any errors +// to errs. +// +// Most versions are fixed as we parse the file, but for retract directives, +// the relevant module path is the one specified with the module directive, +// and that might appear at the end of the file (or not at all). +func (f *File) fixRetract(fix VersionFixer, errs *ErrorList) { + if fix == nil { + return + } + path := "" + if f.Module != nil { + path = f.Module.Mod.Path + } + var r *Retract + wrapError := func(err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: r.Syntax.Start, + Err: err, + }) + } + + for _, r = range f.Retract { + if path == "" { + wrapError(errors.New("no module directive found, so retract cannot be used")) + return // only print the first one of these + } + + args := r.Syntax.Token + if args[0] == "retract" { + args = args[1:] + } + vi, err := parseVersionInterval("retract", path, &args, fix) + if err != nil { + wrapError(err) + } + r.VersionInterval = vi + } +} + +func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, fix VersionFixer) { + wrapError := func(err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: line.Start, + Err: err, + }) + } + errorf := func(format string, args ...interface{}) { + wrapError(fmt.Errorf(format, args...)) + } + + switch verb { + default: + errorf("unknown directive: %s", verb) + + case "go": + if f.Go != nil { + errorf("repeated go statement") + return + } + if len(args) != 1 { + errorf("go directive expects exactly one argument") + return + } else if !GoVersionRE.MatchString(args[0]) { + errorf("invalid go version '%s': must match format 1.23", args[0]) + return + } + + f.Go = &Go{Syntax: line} + f.Go.Version = args[0] + + case "toolchain": + if f.Toolchain != nil { + errorf("repeated toolchain statement") + return + } + if len(args) != 1 { + errorf("toolchain directive expects exactly one argument") + return + } else if !ToolchainRE.MatchString(args[0]) { + errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0]) + return + } + + f.Toolchain = &Toolchain{Syntax: line} + f.Toolchain.Name = args[0] + + case "use": + if len(args) != 1 { + errorf("usage: %s local/dir", verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + f.Use = append(f.Use, &Use{ + Path: s, + Syntax: line, + }) + + case "replace": + replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix) + if wrappederr != nil { + *errs = append(*errs, *wrappederr) + return + } + f.Replace = append(f.Replace, replace) + } +} + +// IsDirectoryPath reports whether the given path should be interpreted as a directory path. +// Just like on the go command line, relative paths starting with a '.' or '..' path component +// and rooted paths are directory paths; the rest are module paths. +func IsDirectoryPath(ns string) bool { + // Because go.mod files can move from one system to another, + // we check all known path syntaxes, both Unix and Windows. + return ns == "." || strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, `.\`) || + ns == ".." || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, `..\`) || + strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `\`) || + len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' +} + +// MustQuote reports whether s must be quoted in order to appear as +// a single token in a go.mod line. +func MustQuote(s string) bool { + for _, r := range s { + switch r { + case ' ', '"', '\'', '`': + return true + + case '(', ')', '[', ']', '{', '}', ',': + if len(s) > 1 { + return true + } + + default: + if !unicode.IsPrint(r) { + return true + } + } + } + return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*") +} + +// AutoQuote returns s or, if quoting is required for s to appear in a go.mod, +// the quotation of s. +func AutoQuote(s string) string { + if MustQuote(s) { + return strconv.Quote(s) + } + return s +} + +func parseVersionInterval(verb string, path string, args *[]string, fix VersionFixer) (VersionInterval, error) { + toks := *args + if len(toks) == 0 || toks[0] == "(" { + return VersionInterval{}, fmt.Errorf("expected '[' or version") + } + if toks[0] != "[" { + v, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + *args = toks[1:] + return VersionInterval{Low: v, High: v}, nil + } + toks = toks[1:] + + if len(toks) == 0 { + return VersionInterval{}, fmt.Errorf("expected version after '['") + } + low, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + toks = toks[1:] + + if len(toks) == 0 || toks[0] != "," { + return VersionInterval{}, fmt.Errorf("expected ',' after version") + } + toks = toks[1:] + + if len(toks) == 0 { + return VersionInterval{}, fmt.Errorf("expected version after ','") + } + high, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + toks = toks[1:] + + if len(toks) == 0 || toks[0] != "]" { + return VersionInterval{}, fmt.Errorf("expected ']' after version") + } + toks = toks[1:] + + *args = toks + return VersionInterval{Low: low, High: high}, nil +} + +func parseString(s *string) (string, error) { + t := *s + if strings.HasPrefix(t, `"`) { + var err error + if t, err = strconv.Unquote(t); err != nil { + return "", err + } + } else if strings.ContainsAny(t, "\"'`") { + // Other quotes are reserved both for possible future expansion + // and to avoid confusion. For example if someone types 'x' + // we want that to be a syntax error and not a literal x in literal quotation marks. + return "", fmt.Errorf("unquoted string cannot contain quote") + } + *s = AutoQuote(t) + return t, nil +} + +var deprecatedRE = lazyregexp.New(`(?s)(?:^|\n\n)Deprecated: *(.*?)(?:$|\n\n)`) + +// parseDeprecation extracts the text of comments on a "module" directive and +// extracts a deprecation message from that. +// +// A deprecation message is contained in a paragraph within a block of comments +// that starts with "Deprecated:" (case sensitive). The message runs until the +// end of the paragraph and does not include the "Deprecated:" prefix. If the +// comment block has multiple paragraphs that start with "Deprecated:", +// parseDeprecation returns the message from the first. +func parseDeprecation(block *LineBlock, line *Line) string { + text := parseDirectiveComment(block, line) + m := deprecatedRE.FindStringSubmatch(text) + if m == nil { + return "" + } + return m[1] +} + +// parseDirectiveComment extracts the text of comments on a directive. +// If the directive's line does not have comments and is part of a block that +// does have comments, the block's comments are used. +func parseDirectiveComment(block *LineBlock, line *Line) string { + comments := line.Comment() + if block != nil && len(comments.Before) == 0 && len(comments.Suffix) == 0 { + comments = block.Comment() + } + groups := [][]Comment{comments.Before, comments.Suffix} + var lines []string + for _, g := range groups { + for _, c := range g { + if !strings.HasPrefix(c.Token, "//") { + continue // blank line + } + lines = append(lines, strings.TrimSpace(strings.TrimPrefix(c.Token, "//"))) + } + } + return strings.Join(lines, "\n") +} + +type ErrorList []Error + +func (e ErrorList) Error() string { + errStrs := make([]string, len(e)) + for i, err := range e { + errStrs[i] = err.Error() + } + return strings.Join(errStrs, "\n") +} + +type Error struct { + Filename string + Pos Position + Verb string + ModPath string + Err error +} + +func (e *Error) Error() string { + var pos string + if e.Pos.LineRune > 1 { + // Don't print LineRune if it's 1 (beginning of line). + // It's always 1 except in scanner errors, which are rare. + pos = fmt.Sprintf("%s:%d:%d: ", e.Filename, e.Pos.Line, e.Pos.LineRune) + } else if e.Pos.Line > 0 { + pos = fmt.Sprintf("%s:%d: ", e.Filename, e.Pos.Line) + } else if e.Filename != "" { + pos = fmt.Sprintf("%s: ", e.Filename) + } + + var directive string + if e.ModPath != "" { + directive = fmt.Sprintf("%s %s: ", e.Verb, e.ModPath) + } else if e.Verb != "" { + directive = fmt.Sprintf("%s: ", e.Verb) + } + + return pos + directive + e.Err.Error() +} + +func (e *Error) Unwrap() error { return e.Err } + +func parseVersion(verb string, path string, s *string, fix VersionFixer) (string, error) { + t, err := parseString(s) + if err != nil { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: &module.InvalidVersionError{ + Version: *s, + Err: err, + }, + } + } + if fix != nil { + fixed, err := fix(path, t) + if err != nil { + if err, ok := err.(*module.ModuleError); ok { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: err.Err, + } + } + return "", err + } + t = fixed + } else { + cv := module.CanonicalVersion(t) + if cv == "" { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: &module.InvalidVersionError{ + Version: t, + Err: errors.New("must be of the form v1.2.3"), + }, + } + } + t = cv + } + *s = t + return *s, nil +} + +func modulePathMajor(path string) (string, error) { + _, major, ok := module.SplitPathVersion(path) + if !ok { + return "", fmt.Errorf("invalid module path") + } + return major, nil +} + +func (f *File) Format() ([]byte, error) { + return Format(f.Syntax), nil +} + +// Cleanup cleans up the file f after any edit operations. +// To avoid quadratic behavior, modifications like [File.DropRequire] +// clear the entry but do not remove it from the slice. +// Cleanup cleans out all the cleared entries. +func (f *File) Cleanup() { + w := 0 + for _, r := range f.Require { + if r.Mod.Path != "" { + f.Require[w] = r + w++ + } + } + f.Require = f.Require[:w] + + w = 0 + for _, x := range f.Exclude { + if x.Mod.Path != "" { + f.Exclude[w] = x + w++ + } + } + f.Exclude = f.Exclude[:w] + + w = 0 + for _, r := range f.Replace { + if r.Old.Path != "" { + f.Replace[w] = r + w++ + } + } + f.Replace = f.Replace[:w] + + w = 0 + for _, r := range f.Retract { + if r.Low != "" || r.High != "" { + f.Retract[w] = r + w++ + } + } + f.Retract = f.Retract[:w] + + f.Syntax.Cleanup() +} + +func (f *File) AddGoStmt(version string) error { + if !GoVersionRE.MatchString(version) { + return fmt.Errorf("invalid language version %q", version) + } + if f.Go == nil { + var hint Expr + if f.Module != nil && f.Module.Syntax != nil { + hint = f.Module.Syntax + } + f.Go = &Go{ + Version: version, + Syntax: f.Syntax.addLine(hint, "go", version), + } + } else { + f.Go.Version = version + f.Syntax.updateLine(f.Go.Syntax, "go", version) + } + return nil +} + +// DropGoStmt deletes the go statement from the file. +func (f *File) DropGoStmt() { + if f.Go != nil { + f.Go.Syntax.markRemoved() + f.Go = nil + } +} + +// DropToolchainStmt deletes the toolchain statement from the file. +func (f *File) DropToolchainStmt() { + if f.Toolchain != nil { + f.Toolchain.Syntax.markRemoved() + f.Toolchain = nil + } +} + +func (f *File) AddToolchainStmt(name string) error { + if !ToolchainRE.MatchString(name) { + return fmt.Errorf("invalid toolchain name %q", name) + } + if f.Toolchain == nil { + var hint Expr + if f.Go != nil && f.Go.Syntax != nil { + hint = f.Go.Syntax + } else if f.Module != nil && f.Module.Syntax != nil { + hint = f.Module.Syntax + } + f.Toolchain = &Toolchain{ + Name: name, + Syntax: f.Syntax.addLine(hint, "toolchain", name), + } + } else { + f.Toolchain.Name = name + f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name) + } + return nil +} + +// AddRequire sets the first require line for path to version vers, +// preserving any existing comments for that line and removing all +// other lines for path. +// +// If no line currently exists for path, AddRequire adds a new line +// at the end of the last require block. +func (f *File) AddRequire(path, vers string) error { + need := true + for _, r := range f.Require { + if r.Mod.Path == path { + if need { + r.Mod.Version = vers + f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers) + need = false + } else { + r.Syntax.markRemoved() + *r = Require{} + } + } + } + + if need { + f.AddNewRequire(path, vers, false) + } + return nil +} + +// AddNewRequire adds a new require line for path at version vers at the end of +// the last require block, regardless of any existing require lines for path. +func (f *File) AddNewRequire(path, vers string, indirect bool) { + line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers) + r := &Require{ + Mod: module.Version{Path: path, Version: vers}, + Syntax: line, + } + r.setIndirect(indirect) + f.Require = append(f.Require, r) +} + +// SetRequire updates the requirements of f to contain exactly req, preserving +// the existing block structure and line comment contents (except for 'indirect' +// markings) for the first requirement on each named module path. +// +// The Syntax field is ignored for the requirements in req. +// +// Any requirements not already present in the file are added to the block +// containing the last require line. +// +// The requirements in req must specify at most one distinct version for each +// module path. +// +// If any existing requirements may be removed, the caller should call +// [File.Cleanup] after all edits are complete. +func (f *File) SetRequire(req []*Require) { + type elem struct { + version string + indirect bool + } + need := make(map[string]elem) + for _, r := range req { + if prev, dup := need[r.Mod.Path]; dup && prev.version != r.Mod.Version { + panic(fmt.Errorf("SetRequire called with conflicting versions for path %s (%s and %s)", r.Mod.Path, prev.version, r.Mod.Version)) + } + need[r.Mod.Path] = elem{r.Mod.Version, r.Indirect} + } + + // Update or delete the existing Require entries to preserve + // only the first for each module path in req. + for _, r := range f.Require { + e, ok := need[r.Mod.Path] + if ok { + r.setVersion(e.version) + r.setIndirect(e.indirect) + } else { + r.markRemoved() + } + delete(need, r.Mod.Path) + } + + // Add new entries in the last block of the file for any paths that weren't + // already present. + // + // This step is nondeterministic, but the final result will be deterministic + // because we will sort the block. + for path, e := range need { + f.AddNewRequire(path, e.version, e.indirect) + } + + f.SortBlocks() +} + +// SetRequireSeparateIndirect updates the requirements of f to contain the given +// requirements. Comment contents (except for 'indirect' markings) are retained +// from the first existing requirement for each module path. Like SetRequire, +// SetRequireSeparateIndirect adds requirements for new paths in req, +// updates the version and "// indirect" comment on existing requirements, +// and deletes requirements on paths not in req. Existing duplicate requirements +// are deleted. +// +// As its name suggests, SetRequireSeparateIndirect puts direct and indirect +// requirements into two separate blocks, one containing only direct +// requirements, and the other containing only indirect requirements. +// SetRequireSeparateIndirect may move requirements between these two blocks +// when their indirect markings change. However, SetRequireSeparateIndirect +// won't move requirements from other blocks, especially blocks with comments. +// +// If the file initially has one uncommented block of requirements, +// SetRequireSeparateIndirect will split it into a direct-only and indirect-only +// block. This aids in the transition to separate blocks. +func (f *File) SetRequireSeparateIndirect(req []*Require) { + // hasComments returns whether a line or block has comments + // other than "indirect". + hasComments := func(c Comments) bool { + return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 || + (len(c.Suffix) == 1 && + strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect") + } + + // moveReq adds r to block. If r was in another block, moveReq deletes + // it from that block and transfers its comments. + moveReq := func(r *Require, block *LineBlock) { + var line *Line + if r.Syntax == nil { + line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}} + r.Syntax = line + if r.Indirect { + r.setIndirect(true) + } + } else { + line = new(Line) + *line = *r.Syntax + if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" { + line.Token = line.Token[1:] + } + r.Syntax.Token = nil // Cleanup will delete the old line. + r.Syntax = line + } + line.InBlock = true + block.Line = append(block.Line, line) + } + + // Examine existing require lines and blocks. + var ( + // We may insert new requirements into the last uncommented + // direct-only and indirect-only blocks. We may also move requirements + // to the opposite block if their indirect markings change. + lastDirectIndex = -1 + lastIndirectIndex = -1 + + // If there are no direct-only or indirect-only blocks, a new block may + // be inserted after the last require line or block. + lastRequireIndex = -1 + + // If there's only one require line or block, and it's uncommented, + // we'll move its requirements to the direct-only or indirect-only blocks. + requireLineOrBlockCount = 0 + + // Track the block each requirement belongs to (if any) so we can + // move them later. + lineToBlock = make(map[*Line]*LineBlock) + ) + for i, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *Line: + if len(stmt.Token) == 0 || stmt.Token[0] != "require" { + continue + } + lastRequireIndex = i + requireLineOrBlockCount++ + if !hasComments(stmt.Comments) { + if isIndirect(stmt) { + lastIndirectIndex = i + } else { + lastDirectIndex = i + } + } + + case *LineBlock: + if len(stmt.Token) == 0 || stmt.Token[0] != "require" { + continue + } + lastRequireIndex = i + requireLineOrBlockCount++ + allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) + allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) + for _, line := range stmt.Line { + lineToBlock[line] = stmt + if hasComments(line.Comments) { + allDirect = false + allIndirect = false + } else if isIndirect(line) { + allDirect = false + } else { + allIndirect = false + } + } + if allDirect { + lastDirectIndex = i + } + if allIndirect { + lastIndirectIndex = i + } + } + } + + oneFlatUncommentedBlock := requireLineOrBlockCount == 1 && + !hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment()) + + // Create direct and indirect blocks if needed. Convert lines into blocks + // if needed. If we end up with an empty block or a one-line block, + // Cleanup will delete it or convert it to a line later. + insertBlock := func(i int) *LineBlock { + block := &LineBlock{Token: []string{"require"}} + f.Syntax.Stmt = append(f.Syntax.Stmt, nil) + copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) + f.Syntax.Stmt[i] = block + return block + } + + ensureBlock := func(i int) *LineBlock { + switch stmt := f.Syntax.Stmt[i].(type) { + case *LineBlock: + return stmt + case *Line: + block := &LineBlock{ + Token: []string{"require"}, + Line: []*Line{stmt}, + } + stmt.Token = stmt.Token[1:] // remove "require" + stmt.InBlock = true + f.Syntax.Stmt[i] = block + return block + default: + panic(fmt.Sprintf("unexpected statement: %v", stmt)) + } + } + + var lastDirectBlock *LineBlock + if lastDirectIndex < 0 { + if lastIndirectIndex >= 0 { + lastDirectIndex = lastIndirectIndex + lastIndirectIndex++ + } else if lastRequireIndex >= 0 { + lastDirectIndex = lastRequireIndex + 1 + } else { + lastDirectIndex = len(f.Syntax.Stmt) + } + lastDirectBlock = insertBlock(lastDirectIndex) + } else { + lastDirectBlock = ensureBlock(lastDirectIndex) + } + + var lastIndirectBlock *LineBlock + if lastIndirectIndex < 0 { + lastIndirectIndex = lastDirectIndex + 1 + lastIndirectBlock = insertBlock(lastIndirectIndex) + } else { + lastIndirectBlock = ensureBlock(lastIndirectIndex) + } + + // Delete requirements we don't want anymore. + // Update versions and indirect comments on requirements we want to keep. + // If a requirement is in last{Direct,Indirect}Block with the wrong + // indirect marking after this, or if the requirement is in an single + // uncommented mixed block (oneFlatUncommentedBlock), move it to the + // correct block. + // + // Some blocks may be empty after this. Cleanup will remove them. + need := make(map[string]*Require) + for _, r := range req { + need[r.Mod.Path] = r + } + have := make(map[string]*Require) + for _, r := range f.Require { + path := r.Mod.Path + if need[path] == nil || have[path] != nil { + // Requirement not needed, or duplicate requirement. Delete. + r.markRemoved() + continue + } + have[r.Mod.Path] = r + r.setVersion(need[path].Mod.Version) + r.setIndirect(need[path].Indirect) + if need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) { + moveReq(r, lastIndirectBlock) + } else if !need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) { + moveReq(r, lastDirectBlock) + } + } + + // Add new requirements. + for path, r := range need { + if have[path] == nil { + if r.Indirect { + moveReq(r, lastIndirectBlock) + } else { + moveReq(r, lastDirectBlock) + } + f.Require = append(f.Require, r) + } + } + + f.SortBlocks() +} + +func (f *File) DropRequire(path string) error { + for _, r := range f.Require { + if r.Mod.Path == path { + r.Syntax.markRemoved() + *r = Require{} + } + } + return nil +} + +// AddExclude adds a exclude statement to the mod file. Errors if the provided +// version is not a canonical version string +func (f *File) AddExclude(path, vers string) error { + if err := checkCanonicalVersion(path, vers); err != nil { + return err + } + + var hint *Line + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + return nil + } + if x.Mod.Path == path { + hint = x.Syntax + } + } + + f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)}) + return nil +} + +func (f *File) DropExclude(path, vers string) error { + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + x.Syntax.markRemoved() + *x = Exclude{} + } + } + return nil +} + +func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { + return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers) +} + +func addReplace(syntax *FileSyntax, replace *[]*Replace, oldPath, oldVers, newPath, newVers string) error { + need := true + old := module.Version{Path: oldPath, Version: oldVers} + new := module.Version{Path: newPath, Version: newVers} + tokens := []string{"replace", AutoQuote(oldPath)} + if oldVers != "" { + tokens = append(tokens, oldVers) + } + tokens = append(tokens, "=>", AutoQuote(newPath)) + if newVers != "" { + tokens = append(tokens, newVers) + } + + var hint *Line + for _, r := range *replace { + if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { + if need { + // Found replacement for old; update to use new. + r.New = new + syntax.updateLine(r.Syntax, tokens...) + need = false + continue + } + // Already added; delete other replacements for same. + r.Syntax.markRemoved() + *r = Replace{} + } + if r.Old.Path == oldPath { + hint = r.Syntax + } + } + if need { + *replace = append(*replace, &Replace{Old: old, New: new, Syntax: syntax.addLine(hint, tokens...)}) + } + return nil +} + +func (f *File) DropReplace(oldPath, oldVers string) error { + for _, r := range f.Replace { + if r.Old.Path == oldPath && r.Old.Version == oldVers { + r.Syntax.markRemoved() + *r = Replace{} + } + } + return nil +} + +// AddRetract adds a retract statement to the mod file. Errors if the provided +// version interval does not consist of canonical version strings +func (f *File) AddRetract(vi VersionInterval, rationale string) error { + var path string + if f.Module != nil { + path = f.Module.Mod.Path + } + if err := checkCanonicalVersion(path, vi.High); err != nil { + return err + } + if err := checkCanonicalVersion(path, vi.Low); err != nil { + return err + } + + r := &Retract{ + VersionInterval: vi, + } + if vi.Low == vi.High { + r.Syntax = f.Syntax.addLine(nil, "retract", AutoQuote(vi.Low)) + } else { + r.Syntax = f.Syntax.addLine(nil, "retract", "[", AutoQuote(vi.Low), ",", AutoQuote(vi.High), "]") + } + if rationale != "" { + for _, line := range strings.Split(rationale, "\n") { + com := Comment{Token: "// " + line} + r.Syntax.Comment().Before = append(r.Syntax.Comment().Before, com) + } + } + return nil +} + +func (f *File) DropRetract(vi VersionInterval) error { + for _, r := range f.Retract { + if r.VersionInterval == vi { + r.Syntax.markRemoved() + *r = Retract{} + } + } + return nil +} + +func (f *File) SortBlocks() { + f.removeDups() // otherwise sorting is unsafe + + // semanticSortForExcludeVersionV is the Go version (plus leading "v") at which + // lines in exclude blocks start to use semantic sort instead of lexicographic sort. + // See go.dev/issue/60028. + const semanticSortForExcludeVersionV = "v1.21" + useSemanticSortForExclude := f.Go != nil && semver.Compare("v"+f.Go.Version, semanticSortForExcludeVersionV) >= 0 + + for _, stmt := range f.Syntax.Stmt { + block, ok := stmt.(*LineBlock) + if !ok { + continue + } + less := lineLess + if block.Token[0] == "exclude" && useSemanticSortForExclude { + less = lineExcludeLess + } else if block.Token[0] == "retract" { + less = lineRetractLess + } + sort.SliceStable(block.Line, func(i, j int) bool { + return less(block.Line[i], block.Line[j]) + }) + } +} + +// removeDups removes duplicate exclude and replace directives. +// +// Earlier exclude directives take priority. +// +// Later replace directives take priority. +// +// require directives are not de-duplicated. That's left up to higher-level +// logic (MVS). +// +// retract directives are not de-duplicated since comments are +// meaningful, and versions may be retracted multiple times. +func (f *File) removeDups() { + removeDups(f.Syntax, &f.Exclude, &f.Replace) +} + +func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace) { + kill := make(map[*Line]bool) + + // Remove duplicate excludes. + if exclude != nil { + haveExclude := make(map[module.Version]bool) + for _, x := range *exclude { + if haveExclude[x.Mod] { + kill[x.Syntax] = true + continue + } + haveExclude[x.Mod] = true + } + var excl []*Exclude + for _, x := range *exclude { + if !kill[x.Syntax] { + excl = append(excl, x) + } + } + *exclude = excl + } + + // Remove duplicate replacements. + // Later replacements take priority over earlier ones. + haveReplace := make(map[module.Version]bool) + for i := len(*replace) - 1; i >= 0; i-- { + x := (*replace)[i] + if haveReplace[x.Old] { + kill[x.Syntax] = true + continue + } + haveReplace[x.Old] = true + } + var repl []*Replace + for _, x := range *replace { + if !kill[x.Syntax] { + repl = append(repl, x) + } + } + *replace = repl + + // Duplicate require and retract directives are not removed. + + // Drop killed statements from the syntax tree. + var stmts []Expr + for _, stmt := range syntax.Stmt { + switch stmt := stmt.(type) { + case *Line: + if kill[stmt] { + continue + } + case *LineBlock: + var lines []*Line + for _, line := range stmt.Line { + if !kill[line] { + lines = append(lines, line) + } + } + stmt.Line = lines + if len(lines) == 0 { + continue + } + } + stmts = append(stmts, stmt) + } + syntax.Stmt = stmts +} + +// lineLess returns whether li should be sorted before lj. It sorts +// lexicographically without assigning any special meaning to tokens. +func lineLess(li, lj *Line) bool { + for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { + if li.Token[k] != lj.Token[k] { + return li.Token[k] < lj.Token[k] + } + } + return len(li.Token) < len(lj.Token) +} + +// lineExcludeLess reports whether li should be sorted before lj for lines in +// an "exclude" block. +func lineExcludeLess(li, lj *Line) bool { + if len(li.Token) != 2 || len(lj.Token) != 2 { + // Not a known exclude specification. + // Fall back to sorting lexicographically. + return lineLess(li, lj) + } + // An exclude specification has two tokens: ModulePath and Version. + // Compare module path by string order and version by semver rules. + if pi, pj := li.Token[0], lj.Token[0]; pi != pj { + return pi < pj + } + return semver.Compare(li.Token[1], lj.Token[1]) < 0 +} + +// lineRetractLess returns whether li should be sorted before lj for lines in +// a "retract" block. It treats each line as a version interval. Single versions +// are compared as if they were intervals with the same low and high version. +// Intervals are sorted in descending order, first by low version, then by +// high version, using semver.Compare. +func lineRetractLess(li, lj *Line) bool { + interval := func(l *Line) VersionInterval { + if len(l.Token) == 1 { + return VersionInterval{Low: l.Token[0], High: l.Token[0]} + } else if len(l.Token) == 5 && l.Token[0] == "[" && l.Token[2] == "," && l.Token[4] == "]" { + return VersionInterval{Low: l.Token[1], High: l.Token[3]} + } else { + // Line in unknown format. Treat as an invalid version. + return VersionInterval{} + } + } + vii := interval(li) + vij := interval(lj) + if cmp := semver.Compare(vii.Low, vij.Low); cmp != 0 { + return cmp > 0 + } + return semver.Compare(vii.High, vij.High) > 0 +} + +// checkCanonicalVersion returns a non-nil error if vers is not a canonical +// version string or does not match the major version of path. +// +// If path is non-empty, the error text suggests a format with a major version +// corresponding to the path. +func checkCanonicalVersion(path, vers string) error { + _, pathMajor, pathMajorOk := module.SplitPathVersion(path) + + if vers == "" || vers != module.CanonicalVersion(vers) { + if pathMajor == "" { + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("must be of the form v1.2.3"), + } + } + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("must be of the form %s.2.3", module.PathMajorPrefix(pathMajor)), + } + } + + if pathMajorOk { + if err := module.CheckPathMajor(vers, pathMajor); err != nil { + if pathMajor == "" { + // In this context, the user probably wrote "v2.3.4" when they meant + // "v2.3.4+incompatible". Suggest that instead of "v0 or v1". + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("should be %s+incompatible (or module %s/%v)", vers, path, semver.Major(vers)), + } + } + return err + } + } + + return nil +} diff --git a/vendor/golang.org/x/mod/modfile/work.go b/vendor/golang.org/x/mod/modfile/work.go new file mode 100644 index 000000000..d7b99376e --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/work.go @@ -0,0 +1,285 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modfile + +import ( + "fmt" + "sort" + "strings" +) + +// A WorkFile is the parsed, interpreted form of a go.work file. +type WorkFile struct { + Go *Go + Toolchain *Toolchain + Use []*Use + Replace []*Replace + + Syntax *FileSyntax +} + +// A Use is a single directory statement. +type Use struct { + Path string // Use path of module. + ModulePath string // Module path in the comment. + Syntax *Line +} + +// ParseWork parses and returns a go.work file. +// +// file is the name of the file, used in positions and errors. +// +// data is the content of the file. +// +// fix is an optional function that canonicalizes module versions. +// If fix is nil, all module versions must be canonical ([module.CanonicalVersion] +// must return the same string). +func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) { + fs, err := parse(file, data) + if err != nil { + return nil, err + } + f := &WorkFile{ + Syntax: fs, + } + var errs ErrorList + + for _, x := range fs.Stmt { + switch x := x.(type) { + case *Line: + f.add(&errs, x, x.Token[0], x.Token[1:], fix) + + case *LineBlock: + if len(x.Token) > 1 { + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + continue + } + switch x.Token[0] { + default: + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + continue + case "use", "replace": + for _, l := range x.Line { + f.add(&errs, l, x.Token[0], l.Token, fix) + } + } + } + } + + if len(errs) > 0 { + return nil, errs + } + return f, nil +} + +// Cleanup cleans up the file f after any edit operations. +// To avoid quadratic behavior, modifications like [WorkFile.DropRequire] +// clear the entry but do not remove it from the slice. +// Cleanup cleans out all the cleared entries. +func (f *WorkFile) Cleanup() { + w := 0 + for _, r := range f.Use { + if r.Path != "" { + f.Use[w] = r + w++ + } + } + f.Use = f.Use[:w] + + w = 0 + for _, r := range f.Replace { + if r.Old.Path != "" { + f.Replace[w] = r + w++ + } + } + f.Replace = f.Replace[:w] + + f.Syntax.Cleanup() +} + +func (f *WorkFile) AddGoStmt(version string) error { + if !GoVersionRE.MatchString(version) { + return fmt.Errorf("invalid language version %q", version) + } + if f.Go == nil { + stmt := &Line{Token: []string{"go", version}} + f.Go = &Go{ + Version: version, + Syntax: stmt, + } + // Find the first non-comment-only block and add + // the go statement before it. That will keep file comments at the top. + i := 0 + for i = 0; i < len(f.Syntax.Stmt); i++ { + if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok { + break + } + } + f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...) + } else { + f.Go.Version = version + f.Syntax.updateLine(f.Go.Syntax, "go", version) + } + return nil +} + +func (f *WorkFile) AddToolchainStmt(name string) error { + if !ToolchainRE.MatchString(name) { + return fmt.Errorf("invalid toolchain name %q", name) + } + if f.Toolchain == nil { + stmt := &Line{Token: []string{"toolchain", name}} + f.Toolchain = &Toolchain{ + Name: name, + Syntax: stmt, + } + // Find the go line and add the toolchain line after it. + // Or else find the first non-comment-only block and add + // the toolchain line before it. That will keep file comments at the top. + i := 0 + for i = 0; i < len(f.Syntax.Stmt); i++ { + if line, ok := f.Syntax.Stmt[i].(*Line); ok && len(line.Token) > 0 && line.Token[0] == "go" { + i++ + goto Found + } + } + for i = 0; i < len(f.Syntax.Stmt); i++ { + if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok { + break + } + } + Found: + f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...) + } else { + f.Toolchain.Name = name + f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name) + } + return nil +} + +// DropGoStmt deletes the go statement from the file. +func (f *WorkFile) DropGoStmt() { + if f.Go != nil { + f.Go.Syntax.markRemoved() + f.Go = nil + } +} + +// DropToolchainStmt deletes the toolchain statement from the file. +func (f *WorkFile) DropToolchainStmt() { + if f.Toolchain != nil { + f.Toolchain.Syntax.markRemoved() + f.Toolchain = nil + } +} + +func (f *WorkFile) AddUse(diskPath, modulePath string) error { + need := true + for _, d := range f.Use { + if d.Path == diskPath { + if need { + d.ModulePath = modulePath + f.Syntax.updateLine(d.Syntax, "use", AutoQuote(diskPath)) + need = false + } else { + d.Syntax.markRemoved() + *d = Use{} + } + } + } + + if need { + f.AddNewUse(diskPath, modulePath) + } + return nil +} + +func (f *WorkFile) AddNewUse(diskPath, modulePath string) { + line := f.Syntax.addLine(nil, "use", AutoQuote(diskPath)) + f.Use = append(f.Use, &Use{Path: diskPath, ModulePath: modulePath, Syntax: line}) +} + +func (f *WorkFile) SetUse(dirs []*Use) { + need := make(map[string]string) + for _, d := range dirs { + need[d.Path] = d.ModulePath + } + + for _, d := range f.Use { + if modulePath, ok := need[d.Path]; ok { + d.ModulePath = modulePath + } else { + d.Syntax.markRemoved() + *d = Use{} + } + } + + // TODO(#45713): Add module path to comment. + + for diskPath, modulePath := range need { + f.AddNewUse(diskPath, modulePath) + } + f.SortBlocks() +} + +func (f *WorkFile) DropUse(path string) error { + for _, d := range f.Use { + if d.Path == path { + d.Syntax.markRemoved() + *d = Use{} + } + } + return nil +} + +func (f *WorkFile) AddReplace(oldPath, oldVers, newPath, newVers string) error { + return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers) +} + +func (f *WorkFile) DropReplace(oldPath, oldVers string) error { + for _, r := range f.Replace { + if r.Old.Path == oldPath && r.Old.Version == oldVers { + r.Syntax.markRemoved() + *r = Replace{} + } + } + return nil +} + +func (f *WorkFile) SortBlocks() { + f.removeDups() // otherwise sorting is unsafe + + for _, stmt := range f.Syntax.Stmt { + block, ok := stmt.(*LineBlock) + if !ok { + continue + } + sort.SliceStable(block.Line, func(i, j int) bool { + return lineLess(block.Line[i], block.Line[j]) + }) + } +} + +// removeDups removes duplicate replace directives. +// +// Later replace directives take priority. +// +// require directives are not de-duplicated. That's left up to higher-level +// logic (MVS). +// +// retract directives are not de-duplicated since comments are +// meaningful, and versions may be retracted multiple times. +func (f *WorkFile) removeDups() { + removeDups(f.Syntax, nil, &f.Replace) +} diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go new file mode 100644 index 000000000..2a364b229 --- /dev/null +++ b/vendor/golang.org/x/mod/module/module.go @@ -0,0 +1,841 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package module defines the module.Version type along with support code. +// +// The [module.Version] type is a simple Path, Version pair: +// +// type Version struct { +// Path string +// Version string +// } +// +// There are no restrictions imposed directly by use of this structure, +// but additional checking functions, most notably [Check], verify that +// a particular path, version pair is valid. +// +// # Escaped Paths +// +// Module paths appear as substrings of file system paths +// (in the download cache) and of web server URLs in the proxy protocol. +// In general we cannot rely on file systems to be case-sensitive, +// nor can we rely on web servers, since they read from file systems. +// That is, we cannot rely on the file system to keep rsc.io/QUOTE +// and rsc.io/quote separate. Windows and macOS don't. +// Instead, we must never require two different casings of a file path. +// Because we want the download cache to match the proxy protocol, +// and because we want the proxy protocol to be possible to serve +// from a tree of static files (which might be stored on a case-insensitive +// file system), the proxy protocol must never require two different casings +// of a URL path either. +// +// One possibility would be to make the escaped form be the lowercase +// hexadecimal encoding of the actual path bytes. This would avoid ever +// needing different casings of a file path, but it would be fairly illegible +// to most programmers when those paths appeared in the file system +// (including in file paths in compiler errors and stack traces) +// in web server logs, and so on. Instead, we want a safe escaped form that +// leaves most paths unaltered. +// +// The safe escaped form is to replace every uppercase letter +// with an exclamation mark followed by the letter's lowercase equivalent. +// +// For example, +// +// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. +// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy +// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. +// +// Import paths that avoid upper-case letters are left unchanged. +// Note that because import paths are ASCII-only and avoid various +// problematic punctuation (like : < and >), the escaped form is also ASCII-only +// and avoids the same problematic punctuation. +// +// Import paths have never allowed exclamation marks, so there is no +// need to define how to escape a literal !. +// +// # Unicode Restrictions +// +// Today, paths are disallowed from using Unicode. +// +// Although paths are currently disallowed from using Unicode, +// we would like at some point to allow Unicode letters as well, to assume that +// file systems and URLs are Unicode-safe (storing UTF-8), and apply +// the !-for-uppercase convention for escaping them in the file system. +// But there are at least two subtle considerations. +// +// First, note that not all case-fold equivalent distinct runes +// form an upper/lower pair. +// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) +// are three distinct runes that case-fold to each other. +// When we do add Unicode letters, we must not assume that upper/lower +// are the only case-equivalent pairs. +// Perhaps the Kelvin symbol would be disallowed entirely, for example. +// Or perhaps it would escape as "!!k", or perhaps as "(212A)". +// +// Second, it would be nice to allow Unicode marks as well as letters, +// but marks include combining marks, and then we must deal not +// only with case folding but also normalization: both U+00E9 ('é') +// and U+0065 U+0301 ('e' followed by combining acute accent) +// look the same on the page and are treated by some file systems +// as the same path. If we do allow Unicode marks in paths, there +// must be some kind of normalization to allow only one canonical +// encoding of any character used in an import path. +package module + +// IMPORTANT NOTE +// +// This file essentially defines the set of valid import paths for the go command. +// There are many subtle considerations, including Unicode ambiguity, +// security, network, and file system representations. +// +// This file also defines the set of valid module path and version combinations, +// another topic with many subtle considerations. +// +// Changes to the semantics in this file require approval from rsc. + +import ( + "errors" + "fmt" + "path" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/mod/semver" +) + +// A Version (for clients, a module.Version) is defined by a module path and version pair. +// These are stored in their plain (unescaped) form. +type Version struct { + // Path is a module path, like "golang.org/x/text" or "rsc.io/quote/v2". + Path string + + // Version is usually a semantic version in canonical form. + // There are three exceptions to this general rule. + // First, the top-level target of a build has no specific version + // and uses Version = "". + // Second, during MVS calculations the version "none" is used + // to represent the decision to take no version of a given module. + // Third, filesystem paths found in "replace" directives are + // represented by a path with an empty version. + Version string `json:",omitempty"` +} + +// String returns a representation of the Version suitable for logging +// (Path@Version, or just Path if Version is empty). +func (m Version) String() string { + if m.Version == "" { + return m.Path + } + return m.Path + "@" + m.Version +} + +// A ModuleError indicates an error specific to a module. +type ModuleError struct { + Path string + Version string + Err error +} + +// VersionError returns a [ModuleError] derived from a [Version] and error, +// or err itself if it is already such an error. +func VersionError(v Version, err error) error { + var mErr *ModuleError + if errors.As(err, &mErr) && mErr.Path == v.Path && mErr.Version == v.Version { + return err + } + return &ModuleError{ + Path: v.Path, + Version: v.Version, + Err: err, + } +} + +func (e *ModuleError) Error() string { + if v, ok := e.Err.(*InvalidVersionError); ok { + return fmt.Sprintf("%s@%s: invalid %s: %v", e.Path, v.Version, v.noun(), v.Err) + } + if e.Version != "" { + return fmt.Sprintf("%s@%s: %v", e.Path, e.Version, e.Err) + } + return fmt.Sprintf("module %s: %v", e.Path, e.Err) +} + +func (e *ModuleError) Unwrap() error { return e.Err } + +// An InvalidVersionError indicates an error specific to a version, with the +// module path unknown or specified externally. +// +// A [ModuleError] may wrap an InvalidVersionError, but an InvalidVersionError +// must not wrap a ModuleError. +type InvalidVersionError struct { + Version string + Pseudo bool + Err error +} + +// noun returns either "version" or "pseudo-version", depending on whether +// e.Version is a pseudo-version. +func (e *InvalidVersionError) noun() string { + if e.Pseudo { + return "pseudo-version" + } + return "version" +} + +func (e *InvalidVersionError) Error() string { + return fmt.Sprintf("%s %q invalid: %s", e.noun(), e.Version, e.Err) +} + +func (e *InvalidVersionError) Unwrap() error { return e.Err } + +// An InvalidPathError indicates a module, import, or file path doesn't +// satisfy all naming constraints. See [CheckPath], [CheckImportPath], +// and [CheckFilePath] for specific restrictions. +type InvalidPathError struct { + Kind string // "module", "import", or "file" + Path string + Err error +} + +func (e *InvalidPathError) Error() string { + return fmt.Sprintf("malformed %s path %q: %v", e.Kind, e.Path, e.Err) +} + +func (e *InvalidPathError) Unwrap() error { return e.Err } + +// Check checks that a given module path, version pair is valid. +// In addition to the path being a valid module path +// and the version being a valid semantic version, +// the two must correspond. +// For example, the path "yaml/v2" only corresponds to +// semantic versions beginning with "v2.". +func Check(path, version string) error { + if err := CheckPath(path); err != nil { + return err + } + if !semver.IsValid(version) { + return &ModuleError{ + Path: path, + Err: &InvalidVersionError{Version: version, Err: errors.New("not a semantic version")}, + } + } + _, pathMajor, _ := SplitPathVersion(path) + if err := CheckPathMajor(version, pathMajor); err != nil { + return &ModuleError{Path: path, Err: err} + } + return nil +} + +// firstPathOK reports whether r can appear in the first element of a module path. +// The first element of the path must be an LDH domain name, at least for now. +// To avoid case ambiguity, the domain name must be entirely lower case. +func firstPathOK(r rune) bool { + return r == '-' || r == '.' || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' +} + +// modPathOK reports whether r can appear in a module path element. +// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. +// +// This matches what "go get" has historically recognized in import paths, +// and avoids confusing sequences like '%20' or '+' that would change meaning +// if used in a URL. +// +// TODO(rsc): We would like to allow Unicode letters, but that requires additional +// care in the safe encoding (see "escaped paths" above). +func modPathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return false +} + +// importPathOK reports whether r can appear in a package import path element. +// +// Import paths are intermediate between module paths and file paths: we allow +// disallow characters that would be confusing or ambiguous as arguments to +// 'go get' (such as '@' and ' ' ), but allow certain characters that are +// otherwise-unambiguous on the command line and historically used for some +// binary names (such as '++' as a suffix for compiler binaries and wrappers). +func importPathOK(r rune) bool { + return modPathOK(r) || r == '+' +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "escaped paths" above. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + return strings.ContainsRune(allowed, r) + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// CheckPath checks that a module path is valid. +// A valid module path is a valid import path, as checked by [CheckImportPath], +// with three additional constraints. +// First, the leading path element (up to the first slash, if any), +// by convention a domain name, must contain only lower-case ASCII letters, +// ASCII digits, dots (U+002E), and dashes (U+002D); +// it must contain at least one dot and cannot start with a dash. +// Second, for a final path element of the form /vN, where N looks numeric +// (ASCII digits and dots) must not begin with a leading zero, must not be /v1, +// and must not contain any dots. For paths beginning with "gopkg.in/", +// this second requirement is replaced by a requirement that the path +// follow the gopkg.in server's conventions. +// Third, no path element may begin with a dot. +func CheckPath(path string) (err error) { + defer func() { + if err != nil { + err = &InvalidPathError{Kind: "module", Path: path, Err: err} + } + }() + + if err := checkPath(path, modulePath); err != nil { + return err + } + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if i == 0 { + return fmt.Errorf("leading slash") + } + if !strings.Contains(path[:i], ".") { + return fmt.Errorf("missing dot in first path element") + } + if path[0] == '-' { + return fmt.Errorf("leading dash in first path element") + } + for _, r := range path[:i] { + if !firstPathOK(r) { + return fmt.Errorf("invalid char %q in first path element", r) + } + } + if _, _, ok := SplitPathVersion(path); !ok { + return fmt.Errorf("invalid version") + } + return nil +} + +// CheckImportPath checks that an import path is valid. +// +// A valid import path consists of one or more valid path elements +// separated by slashes (U+002F). (It must not begin with nor end in a slash.) +// +// A valid path element is a non-empty string made up of +// ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. +// It must not end with a dot (U+002E), nor contain two dots in a row. +// +// The element prefix up to the first dot must not be a reserved file name +// on Windows, regardless of case (CON, com1, NuL, and so on). The element +// must not have a suffix of a tilde followed by one or more ASCII digits +// (to exclude paths elements that look like Windows short-names). +// +// CheckImportPath may be less restrictive in the future, but see the +// top-level package documentation for additional information about +// subtleties of Unicode. +func CheckImportPath(path string) error { + if err := checkPath(path, importPath); err != nil { + return &InvalidPathError{Kind: "import", Path: path, Err: err} + } + return nil +} + +// pathKind indicates what kind of path we're checking. Module paths, +// import paths, and file paths have different restrictions. +type pathKind int + +const ( + modulePath pathKind = iota + importPath + filePath +) + +// checkPath checks that a general path is valid. kind indicates what +// specific constraints should be applied. +// +// checkPath returns an error describing why the path is not valid. +// Because these checks apply to module, import, and file paths, +// and because other checks may be applied, the caller is expected to wrap +// this error with [InvalidPathError]. +func checkPath(path string, kind pathKind) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if path[0] == '-' && kind != filePath { + return fmt.Errorf("leading dash") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], kind); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], kind); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +func checkElem(elem string, kind pathKind) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && kind == modulePath { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + for _, r := range elem { + ok := false + switch kind { + case modulePath: + ok = modPathOK(r) + case importPath: + ok = importPathOK(r) + case filePath: + ok = fileNameOK(r) + default: + panic(fmt.Sprintf("internal error: invalid kind %v", kind)) + } + if !ok { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("%q disallowed as path element component on Windows", short) + } + } + + if kind == filePath { + // don't check for Windows short-names in file names. They're + // only an issue for import paths. + return nil + } + + // Reject path components that look like Windows short-names. + // Those usually end in a tilde followed by one or more ASCII digits. + if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 { + suffix := short[tilde+1:] + suffixIsDigits := true + for _, r := range suffix { + if r < '0' || r > '9' { + suffixIsDigits = false + break + } + } + if suffixIsDigits { + return fmt.Errorf("trailing tilde and digits in path element") + } + } + + return nil +} + +// CheckFilePath checks that a slash-separated file path is valid. +// The definition of a valid file path is the same as the definition +// of a valid import path except that the set of allowed characters is larger: +// all Unicode letters, ASCII digits, the ASCII space character (U+0020), +// and the ASCII punctuation characters +// “!#$%&()+,-.=@[]^_{}~â€. +// (The excluded punctuation characters, " * < > ? ` ' | / \ and :, +// have special meanings in certain shells or operating systems.) +// +// CheckFilePath may be less restrictive in the future, but see the +// top-level package documentation for additional information about +// subtleties of Unicode. +func CheckFilePath(path string) error { + if err := checkPath(path, filePath); err != nil { + return &InvalidPathError{Kind: "file", Path: path, Err: err} + } + return nil +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} + +// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path +// and version is either empty or "/vN" for N >= 2. +// As a special case, gopkg.in paths are recognized directly; +// they require ".vN" instead of "/vN", and for all N, not just N >= 2. +// SplitPathVersion returns with ok = false when presented with +// a path whose last path element does not satisfy the constraints +// applied by [CheckPath], such as "example.com/pkg/v1" or "example.com/pkg/v1.2". +func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { + if strings.HasPrefix(path, "gopkg.in/") { + return splitGopkgIn(path) + } + + i := len(path) + dot := false + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + if path[i-1] == '.' { + dot = true + } + i-- + } + if i <= 1 || i == len(path) || path[i-1] != 'v' || path[i-2] != '/' { + return path, "", true + } + prefix, pathMajor = path[:i-2], path[i-2:] + if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { + return path, "", false + } + return prefix, pathMajor, true +} + +// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. +func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return path, "", false + } + i := len(path) + if strings.HasSuffix(path, "-unstable") { + i -= len("-unstable") + } + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { + // All gopkg.in paths must end in vN for some N. + return path, "", false + } + prefix, pathMajor = path[:i-2], path[i-2:] + if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { + return path, "", false + } + return prefix, pathMajor, true +} + +// MatchPathMajor reports whether the semantic version v +// matches the path major version pathMajor. +// +// MatchPathMajor returns true if and only if [CheckPathMajor] returns nil. +func MatchPathMajor(v, pathMajor string) bool { + return CheckPathMajor(v, pathMajor) == nil +} + +// CheckPathMajor returns a non-nil error if the semantic version v +// does not match the path major version pathMajor. +func CheckPathMajor(v, pathMajor string) error { + // TODO(jayconrod): return errors or panic for invalid inputs. This function + // (and others) was covered by integration tests for cmd/go, and surrounding + // code protected against invalid inputs like non-canonical versions. + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { + // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. + // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. + return nil + } + m := semver.Major(v) + if pathMajor == "" { + if m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" { + return nil + } + pathMajor = "v0 or v1" + } else if pathMajor[0] == '/' || pathMajor[0] == '.' { + if m == pathMajor[1:] { + return nil + } + pathMajor = pathMajor[1:] + } + return &InvalidVersionError{ + Version: v, + Err: fmt.Errorf("should be %s, not %s", pathMajor, semver.Major(v)), + } +} + +// PathMajorPrefix returns the major-version tag prefix implied by pathMajor. +// An empty PathMajorPrefix allows either v0 or v1. +// +// Note that [MatchPathMajor] may accept some versions that do not actually begin +// with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1' +// pathMajor, even though that pathMajor implies 'v1' tagging. +func PathMajorPrefix(pathMajor string) string { + if pathMajor == "" { + return "" + } + if pathMajor[0] != '/' && pathMajor[0] != '.' { + panic("pathMajor suffix " + pathMajor + " passed to PathMajorPrefix lacks separator") + } + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + m := pathMajor[1:] + if m != semver.Major(m) { + panic("pathMajor suffix " + pathMajor + "passed to PathMajorPrefix is not a valid major version") + } + return m +} + +// CanonicalVersion returns the canonical form of the version string v. +// It is the same as [semver.Canonical] except that it preserves the special build suffix "+incompatible". +func CanonicalVersion(v string) string { + cv := semver.Canonical(v) + if semver.Build(v) == "+incompatible" { + cv += "+incompatible" + } + return cv +} + +// Sort sorts the list by Path, breaking ties by comparing [Version] fields. +// The Version fields are interpreted as semantic versions (using [semver.Compare]) +// optionally followed by a tie-breaking suffix introduced by a slash character, +// like in "v0.0.1/go.mod". +func Sort(list []Version) { + sort.Slice(list, func(i, j int) bool { + mi := list[i] + mj := list[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + // To help go.sum formatting, allow version/file. + // Compare semver prefix by semver rules, + // file by string order. + vi := mi.Version + vj := mj.Version + var fi, fj string + if k := strings.Index(vi, "/"); k >= 0 { + vi, fi = vi[:k], vi[k:] + } + if k := strings.Index(vj, "/"); k >= 0 { + vj, fj = vj[:k], vj[k:] + } + if vi != vj { + return semver.Compare(vi, vj) < 0 + } + return fi < fj + }) +} + +// EscapePath returns the escaped form of the given module path. +// It fails if the module path is invalid. +func EscapePath(path string) (escaped string, err error) { + if err := CheckPath(path); err != nil { + return "", err + } + + return escapeString(path) +} + +// EscapeVersion returns the escaped form of the given module version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func EscapeVersion(v string) (escaped string, err error) { + if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") { + return "", &InvalidVersionError{ + Version: v, + Err: fmt.Errorf("disallowed version string"), + } + } + return escapeString(v) +} + +func escapeString(s string) (escaped string, err error) { + haveUpper := false + for _, r := range s { + if r == '!' || r >= utf8.RuneSelf { + // This should be disallowed by CheckPath, but diagnose anyway. + // The correctness of the escaping loop below depends on it. + return "", fmt.Errorf("internal error: inconsistency in EscapePath") + } + if 'A' <= r && r <= 'Z' { + haveUpper = true + } + } + + if !haveUpper { + return s, nil + } + + var buf []byte + for _, r := range s { + if 'A' <= r && r <= 'Z' { + buf = append(buf, '!', byte(r+'a'-'A')) + } else { + buf = append(buf, byte(r)) + } + } + return string(buf), nil +} + +// UnescapePath returns the module path for the given escaped path. +// It fails if the escaped path is invalid or describes an invalid path. +func UnescapePath(escaped string) (path string, err error) { + path, ok := unescapeString(escaped) + if !ok { + return "", fmt.Errorf("invalid escaped module path %q", escaped) + } + if err := CheckPath(path); err != nil { + return "", fmt.Errorf("invalid escaped module path %q: %v", escaped, err) + } + return path, nil +} + +// UnescapeVersion returns the version string for the given escaped version. +// It fails if the escaped form is invalid or describes an invalid version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func UnescapeVersion(escaped string) (v string, err error) { + v, ok := unescapeString(escaped) + if !ok { + return "", fmt.Errorf("invalid escaped version %q", escaped) + } + if err := checkElem(v, filePath); err != nil { + return "", fmt.Errorf("invalid escaped version %q: %v", v, err) + } + return v, nil +} + +func unescapeString(escaped string) (string, bool) { + var buf []byte + + bang := false + for _, r := range escaped { + if r >= utf8.RuneSelf { + return "", false + } + if bang { + bang = false + if r < 'a' || 'z' < r { + return "", false + } + buf = append(buf, byte(r+'A'-'a')) + continue + } + if r == '!' { + bang = true + continue + } + if 'A' <= r && r <= 'Z' { + return "", false + } + buf = append(buf, byte(r)) + } + if bang { + return "", false + } + return string(buf), true +} + +// MatchPrefixPatterns reports whether any path prefix of target matches one of +// the glob patterns (as defined by [path.Match]) in the comma-separated globs +// list. This implements the algorithm used when matching a module path to the +// GOPRIVATE environment variable, as described by 'go help module-private'. +// +// It ignores any empty or malformed patterns in the list. +// Trailing slashes on patterns are ignored. +func MatchPrefixPatterns(globs, target string) bool { + for globs != "" { + // Extract next non-empty glob in comma-separated list. + var glob string + if i := strings.Index(globs, ","); i >= 0 { + glob, globs = globs[:i], globs[i+1:] + } else { + glob, globs = globs, "" + } + glob = strings.TrimSuffix(glob, "/") + if glob == "" { + continue + } + + // A glob with N+1 path elements (N slashes) needs to be matched + // against the first N+1 path elements of target, + // which end just before the N+1'th slash. + n := strings.Count(glob, "/") + prefix := target + // Walk target, counting slashes, truncating at the N+1'th slash. + for i := 0; i < len(target); i++ { + if target[i] == '/' { + if n == 0 { + prefix = target[:i] + break + } + n-- + } + } + if n > 0 { + // Not enough prefix elements. + continue + } + matched, _ := path.Match(glob, prefix) + if matched { + return true + } + } + return false +} diff --git a/vendor/golang.org/x/mod/module/pseudo.go b/vendor/golang.org/x/mod/module/pseudo.go new file mode 100644 index 000000000..9cf19d325 --- /dev/null +++ b/vendor/golang.org/x/mod/module/pseudo.go @@ -0,0 +1,250 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pseudo-versions +// +// Code authors are expected to tag the revisions they want users to use, +// including prereleases. However, not all authors tag versions at all, +// and not all commits a user might want to try will have tags. +// A pseudo-version is a version with a special form that allows us to +// address an untagged commit and order that version with respect to +// other versions we might encounter. +// +// A pseudo-version takes one of the general forms: +// +// (1) vX.0.0-yyyymmddhhmmss-abcdef123456 +// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 +// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible +// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 +// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible +// +// If there is no recently tagged version with the right major version vX, +// then form (1) is used, creating a space of pseudo-versions at the bottom +// of the vX version range, less than any tagged version, including the unlikely v0.0.0. +// +// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible, +// then the pseudo-version uses form (2) or (3), making it a prerelease for the next +// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string +// ensures that the pseudo-version compares less than possible future explicit prereleases +// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1. +// +// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible, +// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease. + +package module + +import ( + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/mod/internal/lazyregexp" + "golang.org/x/mod/semver" +) + +var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`) + +const PseudoVersionTimestampFormat = "20060102150405" + +// PseudoVersion returns a pseudo-version for the given major version ("v1") +// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time, +// and revision identifier (usually a 12-byte commit hash prefix). +func PseudoVersion(major, older string, t time.Time, rev string) string { + if major == "" { + major = "v0" + } + segment := fmt.Sprintf("%s-%s", t.UTC().Format(PseudoVersionTimestampFormat), rev) + build := semver.Build(older) + older = semver.Canonical(older) + if older == "" { + return major + ".0.0-" + segment // form (1) + } + if semver.Prerelease(older) != "" { + return older + ".0." + segment + build // form (4), (5) + } + + // Form (2), (3). + // Extract patch from vMAJOR.MINOR.PATCH + i := strings.LastIndex(older, ".") + 1 + v, patch := older[:i], older[i:] + + // Reassemble. + return v + incDecimal(patch) + "-0." + segment + build +} + +// ZeroPseudoVersion returns a pseudo-version with a zero timestamp and +// revision, which may be used as a placeholder. +func ZeroPseudoVersion(major string) string { + return PseudoVersion(major, "", time.Time{}, "000000000000") +} + +// incDecimal returns the decimal string incremented by 1. +func incDecimal(decimal string) string { + // Scan right to left turning 9s to 0s until you find a digit to increment. + digits := []byte(decimal) + i := len(digits) - 1 + for ; i >= 0 && digits[i] == '9'; i-- { + digits[i] = '0' + } + if i >= 0 { + digits[i]++ + } else { + // digits is all zeros + digits[0] = '1' + digits = append(digits, '0') + } + return string(digits) +} + +// decDecimal returns the decimal string decremented by 1, or the empty string +// if the decimal is all zeroes. +func decDecimal(decimal string) string { + // Scan right to left turning 0s to 9s until you find a digit to decrement. + digits := []byte(decimal) + i := len(digits) - 1 + for ; i >= 0 && digits[i] == '0'; i-- { + digits[i] = '9' + } + if i < 0 { + // decimal is all zeros + return "" + } + if i == 0 && digits[i] == '1' && len(digits) > 1 { + digits = digits[1:] + } else { + digits[i]-- + } + return string(digits) +} + +// IsPseudoVersion reports whether v is a pseudo-version. +func IsPseudoVersion(v string) bool { + return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) +} + +// IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base, +// timestamp, and revision, as returned by [ZeroPseudoVersion]. +func IsZeroPseudoVersion(v string) bool { + return v == ZeroPseudoVersion(semver.Major(v)) +} + +// PseudoVersionTime returns the time stamp of the pseudo-version v. +// It returns an error if v is not a pseudo-version or if the time stamp +// embedded in the pseudo-version is not a valid time. +func PseudoVersionTime(v string) (time.Time, error) { + _, timestamp, _, _, err := parsePseudoVersion(v) + if err != nil { + return time.Time{}, err + } + t, err := time.Parse("20060102150405", timestamp) + if err != nil { + return time.Time{}, &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("malformed time %q", timestamp), + } + } + return t, nil +} + +// PseudoVersionRev returns the revision identifier of the pseudo-version v. +// It returns an error if v is not a pseudo-version. +func PseudoVersionRev(v string) (rev string, err error) { + _, _, rev, _, err = parsePseudoVersion(v) + return +} + +// PseudoVersionBase returns the canonical parent version, if any, upon which +// the pseudo-version v is based. +// +// If v has no parent version (that is, if it is "vX.0.0-[…]"), +// PseudoVersionBase returns the empty string and a nil error. +func PseudoVersionBase(v string) (string, error) { + base, _, _, build, err := parsePseudoVersion(v) + if err != nil { + return "", err + } + + switch pre := semver.Prerelease(base); pre { + case "": + // vX.0.0-yyyymmddhhmmss-abcdef123456 → "" + if build != "" { + // Pseudo-versions of the form vX.0.0-yyyymmddhhmmss-abcdef123456+incompatible + // are nonsensical: the "vX.0.0-" prefix implies that there is no parent tag, + // but the "+incompatible" suffix implies that the major version of + // the parent tag is not compatible with the module's import path. + // + // There are a few such entries in the index generated by proxy.golang.org, + // but we believe those entries were generated by the proxy itself. + return "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("lacks base version, but has build metadata %q", build), + } + } + return "", nil + + case "-0": + // vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z + // vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z+incompatible + base = strings.TrimSuffix(base, pre) + i := strings.LastIndexByte(base, '.') + if i < 0 { + panic("base from parsePseudoVersion missing patch number: " + base) + } + patch := decDecimal(base[i+1:]) + if patch == "" { + // vX.0.0-0 is invalid, but has been observed in the wild in the index + // generated by requests to proxy.golang.org. + // + // NOTE(bcmills): I cannot find a historical bug that accounts for + // pseudo-versions of this form, nor have I seen such versions in any + // actual go.mod files. If we find actual examples of this form and a + // reasonable theory of how they came into existence, it seems fine to + // treat them as equivalent to vX.0.0 (especially since the invalid + // pseudo-versions have lower precedence than the real ones). For now, we + // reject them. + return "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("version before %s would have negative patch number", base), + } + } + return base[:i+1] + patch + build, nil + + default: + // vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z-pre + // vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z-pre+incompatible + if !strings.HasSuffix(base, ".0") { + panic(`base from parsePseudoVersion missing ".0" before date: ` + base) + } + return strings.TrimSuffix(base, ".0") + build, nil + } +} + +var errPseudoSyntax = errors.New("syntax error") + +func parsePseudoVersion(v string) (base, timestamp, rev, build string, err error) { + if !IsPseudoVersion(v) { + return "", "", "", "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: errPseudoSyntax, + } + } + build = semver.Build(v) + v = strings.TrimSuffix(v, build) + j := strings.LastIndex(v, "-") + v, rev = v[:j], v[j+1:] + i := strings.LastIndex(v, "-") + if j := strings.LastIndex(v, "."); j > i { + base = v[:j] // "vX.Y.Z-pre.0" or "vX.Y.(Z+1)-0" + timestamp = v[j+1:] + } else { + base = v[:i] // "vX.0.0" + timestamp = v[i+1:] + } + return base, timestamp, rev, build, nil +} diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go index a30a22bf2..9a2dfd33a 100644 --- a/vendor/golang.org/x/mod/semver/semver.go +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -140,7 +140,7 @@ func Compare(v, w string) int { // Max canonicalizes its arguments and then returns the version string // that compares greater. // -// Deprecated: use Compare instead. In most cases, returning a canonicalized +// Deprecated: use [Compare] instead. In most cases, returning a canonicalized // version is not expected or desired. func Max(v, w string) string { v = Canonical(v) @@ -151,7 +151,7 @@ func Max(v, w string) string { return w } -// ByVersion implements sort.Interface for sorting semantic version strings. +// ByVersion implements [sort.Interface] for sorting semantic version strings. type ByVersion []string func (vs ByVersion) Len() int { return len(vs) } @@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool { return vs[i] < vs[j] } -// Sort sorts a list of semantic version strings using ByVersion. +// Sort sorts a list of semantic version strings using [ByVersion]. func Sort(list []string) { sort.Sort(ByVersion(list)) } diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index 2cb9c408f..0c1b86793 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.7 -// +build go1.7 package context diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go index 64d31ecc3..e31e35a90 100644 --- a/vendor/golang.org/x/net/context/go19.go +++ b/vendor/golang.org/x/net/context/go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.9 -// +build go1.9 package context diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 7b6b68511..065ff3dfa 100644 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.7 -// +build !go1.7 package context diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go index 1f9715341..ec5a63803 100644 --- a/vendor/golang.org/x/net/context/pre_go19.go +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.9 -// +build !go1.9 package context diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go index de67f938a..3c57880d6 100644 --- a/vendor/golang.org/x/net/html/token.go +++ b/vendor/golang.org/x/net/html/token.go @@ -910,9 +910,6 @@ func (z *Tokenizer) readTagAttrKey() { return } switch c { - case ' ', '\n', '\r', '\t', '\f', '/': - z.pendingAttr[0].end = z.raw.end - 1 - return case '=': if z.pendingAttr[0].start+1 == z.raw.end { // WHATWG 13.2.5.32, if we see an equals sign before the attribute name @@ -920,7 +917,9 @@ func (z *Tokenizer) readTagAttrKey() { continue } fallthrough - case '>': + case ' ', '\n', '\r', '\t', '\f', '/', '>': + // WHATWG 13.2.5.33 Attribute name state + // We need to reconsume the char in the after attribute name state to support the / character z.raw.end-- z.pendingAttr[0].end = z.raw.end return @@ -939,6 +938,11 @@ func (z *Tokenizer) readTagAttrVal() { if z.err != nil { return } + if c == '/' { + // WHATWG 13.2.5.34 After attribute name state + // U+002F SOLIDUS (/) - Switch to the self-closing start tag state. + return + } if c != '=' { z.raw.end-- return diff --git a/vendor/golang.org/x/net/http2/databuffer.go b/vendor/golang.org/x/net/http2/databuffer.go index a3067f8de..e6f55cbd1 100644 --- a/vendor/golang.org/x/net/http2/databuffer.go +++ b/vendor/golang.org/x/net/http2/databuffer.go @@ -20,41 +20,44 @@ import ( // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) -var ( - dataChunkSizeClasses = []int{ - 1 << 10, - 2 << 10, - 4 << 10, - 8 << 10, - 16 << 10, - } - dataChunkPools = [...]sync.Pool{ - {New: func() interface{} { return make([]byte, 1<<10) }}, - {New: func() interface{} { return make([]byte, 2<<10) }}, - {New: func() interface{} { return make([]byte, 4<<10) }}, - {New: func() interface{} { return make([]byte, 8<<10) }}, - {New: func() interface{} { return make([]byte, 16<<10) }}, - } -) +var dataChunkPools = [...]sync.Pool{ + {New: func() interface{} { return new([1 << 10]byte) }}, + {New: func() interface{} { return new([2 << 10]byte) }}, + {New: func() interface{} { return new([4 << 10]byte) }}, + {New: func() interface{} { return new([8 << 10]byte) }}, + {New: func() interface{} { return new([16 << 10]byte) }}, +} func getDataBufferChunk(size int64) []byte { - i := 0 - for ; i < len(dataChunkSizeClasses)-1; i++ { - if size <= int64(dataChunkSizeClasses[i]) { - break - } + switch { + case size <= 1<<10: + return dataChunkPools[0].Get().(*[1 << 10]byte)[:] + case size <= 2<<10: + return dataChunkPools[1].Get().(*[2 << 10]byte)[:] + case size <= 4<<10: + return dataChunkPools[2].Get().(*[4 << 10]byte)[:] + case size <= 8<<10: + return dataChunkPools[3].Get().(*[8 << 10]byte)[:] + default: + return dataChunkPools[4].Get().(*[16 << 10]byte)[:] } - return dataChunkPools[i].Get().([]byte) } func putDataBufferChunk(p []byte) { - for i, n := range dataChunkSizeClasses { - if len(p) == n { - dataChunkPools[i].Put(p) - return - } + switch len(p) { + case 1 << 10: + dataChunkPools[0].Put((*[1 << 10]byte)(p)) + case 2 << 10: + dataChunkPools[1].Put((*[2 << 10]byte)(p)) + case 4 << 10: + dataChunkPools[2].Put((*[4 << 10]byte)(p)) + case 8 << 10: + dataChunkPools[3].Put((*[8 << 10]byte)(p)) + case 16 << 10: + dataChunkPools[4].Put((*[16 << 10]byte)(p)) + default: + panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } - panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } // dataBuffer is an io.ReadWriter backed by a list of data chunks. diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index c1f6b90dc..43557ab7e 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -1510,13 +1510,12 @@ func (mh *MetaHeadersFrame) checkPseudos() error { } func (fr *Framer) maxHeaderStringLen() int { - v := fr.maxHeaderListSize() - if uint32(int(v)) == v { - return int(v) + v := int(fr.maxHeaderListSize()) + if v < 0 { + // If maxHeaderListSize overflows an int, use no limit (0). + return 0 } - // They had a crazy big number for MaxHeaderBytes anyway, - // so give them unlimited header lengths: - return 0 + return v } // readMetaFrame returns 0 or more CONTINUATION frames from fr and @@ -1565,6 +1564,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) { if size > remainSize { hdec.SetEmitEnabled(false) mh.Truncated = true + remainSize = 0 return } remainSize -= size @@ -1577,6 +1577,36 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) { var hc headersOrContinuation = hf for { frag := hc.HeaderBlockFragment() + + // Avoid parsing large amounts of headers that we will then discard. + // If the sender exceeds the max header list size by too much, + // skip parsing the fragment and close the connection. + // + // "Too much" is either any CONTINUATION frame after we've already + // exceeded the max header list size (in which case remainSize is 0), + // or a frame whose encoded size is more than twice the remaining + // header list bytes we're willing to accept. + if int64(len(frag)) > int64(2*remainSize) { + if VerboseLogs { + log.Printf("http2: header list too large") + } + // It would be nice to send a RST_STREAM before sending the GOAWAY, + // but the structure of the server's frame writer makes this difficult. + return nil, ConnectionError(ErrCodeProtocol) + } + + // Also close the connection after any CONTINUATION frame following an + // invalid header, since we stop tracking the size of the headers after + // an invalid one. + if invalid != nil { + if VerboseLogs { + log.Printf("http2: invalid header: %v", invalid) + } + // It would be nice to send a RST_STREAM before sending the GOAWAY, + // but the structure of the server's frame writer makes this difficult. + return nil, ConnectionError(ErrCodeProtocol) + } + if _, err := hdec.Write(frag); err != nil { return nil, ConnectionError(ErrCodeCompression) } diff --git a/vendor/golang.org/x/net/http2/go111.go b/vendor/golang.org/x/net/http2/go111.go deleted file mode 100644 index 5bf62b032..000000000 --- a/vendor/golang.org/x/net/http2/go111.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.11 -// +build go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - if trace != nil { - return trace.Got1xxResponse - } - return nil -} diff --git a/vendor/golang.org/x/net/http2/go115.go b/vendor/golang.org/x/net/http2/go115.go deleted file mode 100644 index 908af1ab9..000000000 --- a/vendor/golang.org/x/net/http2/go115.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.15 -// +build go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS -// connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - dialer := &tls.Dialer{ - Config: cfg, - } - cn, err := dialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed - return tlsCn, nil -} diff --git a/vendor/golang.org/x/net/http2/go118.go b/vendor/golang.org/x/net/http2/go118.go deleted file mode 100644 index aca4b2b31..000000000 --- a/vendor/golang.org/x/net/http2/go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return tc.NetConn() -} diff --git a/vendor/golang.org/x/net/http2/not_go111.go b/vendor/golang.org/x/net/http2/not_go111.go deleted file mode 100644 index cc0baa819..000000000 --- a/vendor/golang.org/x/net/http2/not_go111.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.11 -// +build !go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false } - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - return nil -} diff --git a/vendor/golang.org/x/net/http2/not_go115.go b/vendor/golang.org/x/net/http2/not_go115.go deleted file mode 100644 index e6c04cf7a..000000000 --- a/vendor/golang.org/x/net/http2/not_go115.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.15 -// +build !go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext opens a TLS connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - cn, err := tls.Dial(network, addr, cfg) - if err != nil { - return nil, err - } - if err := cn.Handshake(); err != nil { - return nil, err - } - if cfg.InsecureSkipVerify { - return cn, nil - } - if err := cn.VerifyHostname(cfg.ServerName); err != nil { - return nil, err - } - return cn, nil -} diff --git a/vendor/golang.org/x/net/http2/not_go118.go b/vendor/golang.org/x/net/http2/not_go118.go deleted file mode 100644 index eab532c96..000000000 --- a/vendor/golang.org/x/net/http2/not_go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return nil -} diff --git a/vendor/golang.org/x/net/http2/pipe.go b/vendor/golang.org/x/net/http2/pipe.go index 684d984fd..3b9f06b96 100644 --- a/vendor/golang.org/x/net/http2/pipe.go +++ b/vendor/golang.org/x/net/http2/pipe.go @@ -77,7 +77,10 @@ func (p *pipe) Read(d []byte) (n int, err error) { } } -var errClosedPipeWrite = errors.New("write on closed buffer") +var ( + errClosedPipeWrite = errors.New("write on closed buffer") + errUninitializedPipeWrite = errors.New("write on uninitialized buffer") +) // Write copies bytes from p into the buffer and wakes a reader. // It is an error to write more data than the buffer can hold. @@ -91,6 +94,12 @@ func (p *pipe) Write(d []byte) (n int, err error) { if p.err != nil || p.breakErr != nil { return 0, errClosedPipeWrite } + // pipe.setBuffer is never invoked, leaving the buffer uninitialized. + // We shouldn't try to write to an uninitialized pipe, + // but returning an error is better than panicking. + if p.b == nil { + return 0, errUninitializedPipeWrite + } return p.b.Write(d) } diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 02c88b6b3..ce2e8b40e 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -124,6 +124,7 @@ type Server struct { // IdleTimeout specifies how long until idle clients should be // closed with a GOAWAY frame. PING frames are not considered // activity for the purposes of IdleTimeout. + // If zero or negative, there is no timeout. IdleTimeout time.Duration // MaxUploadBufferPerConnection is the size of the initial flow @@ -434,7 +435,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { // passes the connection off to us with the deadline already set. // Write deadlines are set per stream in serverConn.newStream. // Disarm the net.Conn write deadline here. - if sc.hs.WriteTimeout != 0 { + if sc.hs.WriteTimeout > 0 { sc.conn.SetWriteDeadline(time.Time{}) } @@ -924,7 +925,7 @@ func (sc *serverConn) serve() { sc.setConnState(http.StateActive) sc.setConnState(http.StateIdle) - if sc.srv.IdleTimeout != 0 { + if sc.srv.IdleTimeout > 0 { sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) defer sc.idleTimer.Stop() } @@ -1637,7 +1638,7 @@ func (sc *serverConn) closeStream(st *stream, err error) { delete(sc.streams, st.id) if len(sc.streams) == 0 { sc.setConnState(http.StateIdle) - if sc.srv.IdleTimeout != 0 { + if sc.srv.IdleTimeout > 0 { sc.idleTimer.Reset(sc.srv.IdleTimeout) } if h1ServerKeepAlivesDisabled(sc.hs) { @@ -2017,7 +2018,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { // similar to how the http1 server works. Here it's // technically more like the http1 Server's ReadHeaderTimeout // (in Go 1.8), though. That's a more sane option anyway. - if sc.hs.ReadTimeout != 0 { + if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) } @@ -2038,7 +2039,7 @@ func (sc *serverConn) upgradeRequest(req *http.Request) { // Disable any read deadline set by the net/http package // prior to the upgrade. - if sc.hs.ReadTimeout != 0 { + if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) } @@ -2116,7 +2117,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.flow.conn = &sc.flow // link to conn-level counter st.flow.add(sc.initialStreamSendWindowSize) st.inflow.init(sc.srv.initialStreamRecvWindowSize()) - if sc.hs.WriteTimeout != 0 { + if sc.hs.WriteTimeout > 0 { st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } @@ -2549,7 +2550,6 @@ type responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished - dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -2669,7 +2669,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { - rws.dirty = true return 0, err } if endStream { @@ -2690,7 +2689,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { - rws.dirty = true return 0, err } } @@ -2702,9 +2700,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) - if err != nil { - rws.dirty = true - } return len(p), err } return len(p), nil @@ -2920,14 +2915,12 @@ func (rws *responseWriterState) writeHeader(code int) { h.Del("Transfer-Encoding") } - if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ + rws.conn.writeHeaders(rws.stream, &writeResHeaders{ streamID: rws.stream.id, httpResCode: code, h: h, endStream: rws.handlerDone && !rws.hasTrailers(), - }) != nil { - rws.dirty = true - } + }) return } @@ -2992,19 +2985,10 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, func (w *responseWriter) handlerDone() { rws := w.rws - dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - if !dirty { - // Only recycle the pool if all prior Write calls to - // the serverConn goroutine completed successfully. If - // they returned earlier due to resets from the peer - // there might still be write goroutines outstanding - // from the serverConn referencing the rws memory. See - // issue 20704. - responseWriterStatePool.Put(rws) - } + responseWriterStatePool.Put(rws) } // Push errors. @@ -3187,6 +3171,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) { panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) } + sc.curHandlers++ go sc.runHandler(rw, req, sc.handler.ServeHTTP) return promisedID, nil } diff --git a/vendor/golang.org/x/net/http2/testsync.go b/vendor/golang.org/x/net/http2/testsync.go new file mode 100644 index 000000000..61075bd16 --- /dev/null +++ b/vendor/golang.org/x/net/http2/testsync.go @@ -0,0 +1,331 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package http2 + +import ( + "context" + "sync" + "time" +) + +// testSyncHooks coordinates goroutines in tests. +// +// For example, a call to ClientConn.RoundTrip involves several goroutines, including: +// - the goroutine running RoundTrip; +// - the clientStream.doRequest goroutine, which writes the request; and +// - the clientStream.readLoop goroutine, which reads the response. +// +// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines +// are blocked waiting for some condition such as reading the Request.Body or waiting for +// flow control to become available. +// +// The testSyncHooks also manage timers and synthetic time in tests. +// This permits us to, for example, start a request and cause it to time out waiting for +// response headers without resorting to time.Sleep calls. +type testSyncHooks struct { + // active/inactive act as a mutex and condition variable. + // + // - neither chan contains a value: testSyncHooks is locked. + // - active contains a value: unlocked, and at least one goroutine is not blocked + // - inactive contains a value: unlocked, and all goroutines are blocked + active chan struct{} + inactive chan struct{} + + // goroutine counts + total int // total goroutines + condwait map[*sync.Cond]int // blocked in sync.Cond.Wait + blocked []*testBlockedGoroutine // otherwise blocked + + // fake time + now time.Time + timers []*fakeTimer + + // Transport testing: Report various events. + newclientconn func(*ClientConn) + newstream func(*clientStream) +} + +// testBlockedGoroutine is a blocked goroutine. +type testBlockedGoroutine struct { + f func() bool // blocked until f returns true + ch chan struct{} // closed when unblocked +} + +func newTestSyncHooks() *testSyncHooks { + h := &testSyncHooks{ + active: make(chan struct{}, 1), + inactive: make(chan struct{}, 1), + condwait: map[*sync.Cond]int{}, + } + h.inactive <- struct{}{} + h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) + return h +} + +// lock acquires the testSyncHooks mutex. +func (h *testSyncHooks) lock() { + select { + case <-h.active: + case <-h.inactive: + } +} + +// waitInactive waits for all goroutines to become inactive. +func (h *testSyncHooks) waitInactive() { + for { + <-h.inactive + if !h.unlock() { + break + } + } +} + +// unlock releases the testSyncHooks mutex. +// It reports whether any goroutines are active. +func (h *testSyncHooks) unlock() (active bool) { + // Look for a blocked goroutine which can be unblocked. + blocked := h.blocked[:0] + unblocked := false + for _, b := range h.blocked { + if !unblocked && b.f() { + unblocked = true + close(b.ch) + } else { + blocked = append(blocked, b) + } + } + h.blocked = blocked + + // Count goroutines blocked on condition variables. + condwait := 0 + for _, count := range h.condwait { + condwait += count + } + + if h.total > condwait+len(blocked) { + h.active <- struct{}{} + return true + } else { + h.inactive <- struct{}{} + return false + } +} + +// goRun starts a new goroutine. +func (h *testSyncHooks) goRun(f func()) { + h.lock() + h.total++ + h.unlock() + go func() { + defer func() { + h.lock() + h.total-- + h.unlock() + }() + f() + }() +} + +// blockUntil indicates that a goroutine is blocked waiting for some condition to become true. +// It waits until f returns true before proceeding. +// +// Example usage: +// +// h.blockUntil(func() bool { +// // Is the context done yet? +// select { +// case <-ctx.Done(): +// default: +// return false +// } +// return true +// }) +// // Wait for the context to become done. +// <-ctx.Done() +// +// The function f passed to blockUntil must be non-blocking and idempotent. +func (h *testSyncHooks) blockUntil(f func() bool) { + if f() { + return + } + ch := make(chan struct{}) + h.lock() + h.blocked = append(h.blocked, &testBlockedGoroutine{ + f: f, + ch: ch, + }) + h.unlock() + <-ch +} + +// broadcast is sync.Cond.Broadcast. +func (h *testSyncHooks) condBroadcast(cond *sync.Cond) { + h.lock() + delete(h.condwait, cond) + h.unlock() + cond.Broadcast() +} + +// broadcast is sync.Cond.Wait. +func (h *testSyncHooks) condWait(cond *sync.Cond) { + h.lock() + h.condwait[cond]++ + h.unlock() +} + +// newTimer creates a new fake timer. +func (h *testSyncHooks) newTimer(d time.Duration) timer { + h.lock() + defer h.unlock() + t := &fakeTimer{ + hooks: h, + when: h.now.Add(d), + c: make(chan time.Time), + } + h.timers = append(h.timers, t) + return t +} + +// afterFunc creates a new fake AfterFunc timer. +func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer { + h.lock() + defer h.unlock() + t := &fakeTimer{ + hooks: h, + when: h.now.Add(d), + f: f, + } + h.timers = append(h.timers, t) + return t +} + +func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(ctx) + t := h.afterFunc(d, cancel) + return ctx, func() { + t.Stop() + cancel() + } +} + +func (h *testSyncHooks) timeUntilEvent() time.Duration { + h.lock() + defer h.unlock() + var next time.Time + for _, t := range h.timers { + if next.IsZero() || t.when.Before(next) { + next = t.when + } + } + if d := next.Sub(h.now); d > 0 { + return d + } + return 0 +} + +// advance advances time and causes synthetic timers to fire. +func (h *testSyncHooks) advance(d time.Duration) { + h.lock() + defer h.unlock() + h.now = h.now.Add(d) + timers := h.timers[:0] + for _, t := range h.timers { + t := t // remove after go.mod depends on go1.22 + t.mu.Lock() + switch { + case t.when.After(h.now): + timers = append(timers, t) + case t.when.IsZero(): + // stopped timer + default: + t.when = time.Time{} + if t.c != nil { + close(t.c) + } + if t.f != nil { + h.total++ + go func() { + defer func() { + h.lock() + h.total-- + h.unlock() + }() + t.f() + }() + } + } + t.mu.Unlock() + } + h.timers = timers +} + +// A timer wraps a time.Timer, or a synthetic equivalent in tests. +// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires. +type timer interface { + C() <-chan time.Time + Stop() bool + Reset(d time.Duration) bool +} + +// timeTimer implements timer using real time. +type timeTimer struct { + t *time.Timer + c chan time.Time +} + +// newTimeTimer creates a new timer using real time. +func newTimeTimer(d time.Duration) timer { + ch := make(chan time.Time) + t := time.AfterFunc(d, func() { + close(ch) + }) + return &timeTimer{t, ch} +} + +// newTimeAfterFunc creates an AfterFunc timer using real time. +func newTimeAfterFunc(d time.Duration, f func()) timer { + return &timeTimer{ + t: time.AfterFunc(d, f), + } +} + +func (t timeTimer) C() <-chan time.Time { return t.c } +func (t timeTimer) Stop() bool { return t.t.Stop() } +func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) } + +// fakeTimer implements timer using fake time. +type fakeTimer struct { + hooks *testSyncHooks + + mu sync.Mutex + when time.Time // when the timer will fire + c chan time.Time // closed when the timer fires; mutually exclusive with f + f func() // called when the timer fires; mutually exclusive with c +} + +func (t *fakeTimer) C() <-chan time.Time { return t.c } + +func (t *fakeTimer) Stop() bool { + t.mu.Lock() + defer t.mu.Unlock() + stopped := t.when.IsZero() + t.when = time.Time{} + return stopped +} + +func (t *fakeTimer) Reset(d time.Duration) bool { + if t.c != nil || t.f == nil { + panic("fakeTimer only supports Reset on AfterFunc timers") + } + t.mu.Lock() + defer t.mu.Unlock() + t.hooks.lock() + defer t.hooks.unlock() + active := !t.when.IsZero() + t.when = t.hooks.now.Add(d) + if !active { + t.hooks.timers = append(t.hooks.timers, t) + } + return active +} diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 4515b22c4..ce375c8c7 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -147,6 +147,12 @@ type Transport struct { // waiting for their turn. StrictMaxConcurrentStreams bool + // IdleConnTimeout is the maximum amount of time an idle + // (keep-alive) connection will remain idle before closing + // itself. + // Zero means no limit. + IdleConnTimeout time.Duration + // ReadIdleTimeout is the timeout after which a health check using ping // frame will be carried out if no frame is received on the connection. // Note that a ping response will is considered a received frame, so if @@ -178,6 +184,8 @@ type Transport struct { connPoolOnce sync.Once connPoolOrDef ClientConnPool // non-nil version of ConnPool + + syncHooks *testSyncHooks } func (t *Transport) maxHeaderListSize() uint32 { @@ -302,7 +310,7 @@ type ClientConn struct { readerErr error // set before readerDone is closed idleTimeout time.Duration // or 0 for never - idleTimer *time.Timer + idleTimer timer mu sync.Mutex // guards following cond *sync.Cond // hold mu; broadcast on flow/closed changes @@ -344,6 +352,60 @@ type ClientConn struct { werr error // first write error that has occurred hbuf bytes.Buffer // HPACK encoder writes into this henc *hpack.Encoder + + syncHooks *testSyncHooks // can be nil +} + +// Hook points used for testing. +// Outside of tests, cc.syncHooks is nil and these all have minimal implementations. +// Inside tests, see the testSyncHooks function docs. + +// goRun starts a new goroutine. +func (cc *ClientConn) goRun(f func()) { + if cc.syncHooks != nil { + cc.syncHooks.goRun(f) + return + } + go f() +} + +// condBroadcast is cc.cond.Broadcast. +func (cc *ClientConn) condBroadcast() { + if cc.syncHooks != nil { + cc.syncHooks.condBroadcast(cc.cond) + } + cc.cond.Broadcast() +} + +// condWait is cc.cond.Wait. +func (cc *ClientConn) condWait() { + if cc.syncHooks != nil { + cc.syncHooks.condWait(cc.cond) + } + cc.cond.Wait() +} + +// newTimer creates a new time.Timer, or a synthetic timer in tests. +func (cc *ClientConn) newTimer(d time.Duration) timer { + if cc.syncHooks != nil { + return cc.syncHooks.newTimer(d) + } + return newTimeTimer(d) +} + +// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. +func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer { + if cc.syncHooks != nil { + return cc.syncHooks.afterFunc(d, f) + } + return newTimeAfterFunc(d, f) +} + +func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { + if cc.syncHooks != nil { + return cc.syncHooks.contextWithTimeout(ctx, d) + } + return context.WithTimeout(ctx, d) } // clientStream is the state for a single HTTP/2 stream. One of these @@ -425,7 +487,7 @@ func (cs *clientStream) abortStreamLocked(err error) { // TODO(dneil): Clean up tests where cs.cc.cond is nil. if cs.cc.cond != nil { // Wake up writeRequestBody if it is waiting on flow control. - cs.cc.cond.Broadcast() + cs.cc.condBroadcast() } } @@ -435,7 +497,7 @@ func (cs *clientStream) abortRequestBodyWrite() { defer cc.mu.Unlock() if cs.reqBody != nil && cs.reqBodyClosed == nil { cs.closeReqBodyLocked() - cc.cond.Broadcast() + cc.condBroadcast() } } @@ -445,10 +507,10 @@ func (cs *clientStream) closeReqBodyLocked() { } cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed - go func() { + cs.cc.goRun(func() { cs.reqBody.Close() close(reqBodyClosed) - }() + }) } type stickyErrWriter struct { @@ -537,15 +599,6 @@ func authorityAddr(scheme string, authority string) (addr string) { return net.JoinHostPort(host, port) } -var retryBackoffHook func(time.Duration) *time.Timer - -func backoffNewTimer(d time.Duration) *time.Timer { - if retryBackoffHook != nil { - return retryBackoffHook(d) - } - return time.NewTimer(d) -} - // RoundTripOpt is like RoundTrip, but takes options. func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { @@ -573,13 +626,27 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) - timer := backoffNewTimer(d) + var tm timer + if t.syncHooks != nil { + tm = t.syncHooks.newTimer(d) + t.syncHooks.blockUntil(func() bool { + select { + case <-tm.C(): + case <-req.Context().Done(): + default: + return false + } + return true + }) + } else { + tm = newTimeTimer(d) + } select { - case <-timer.C: + case <-tm.C(): t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) continue case <-req.Context().Done(): - timer.Stop() + tm.Stop() err = req.Context().Err() } } @@ -658,6 +725,9 @@ func canRetryError(err error) bool { } func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { + if t.syncHooks != nil { + return t.newClientConn(nil, singleUse, t.syncHooks) + } host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -666,7 +736,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b if err != nil { return nil, err } - return t.newClientConn(tconn, singleUse) + return t.newClientConn(tconn, singleUse, nil) } func (t *Transport) newTLSConfig(host string) *tls.Config { @@ -732,10 +802,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 { } func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { - return t.newClientConn(c, t.disableKeepAlives()) + return t.newClientConn(c, t.disableKeepAlives(), nil) } -func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { +func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) { cc := &ClientConn{ t: t, tconn: c, @@ -750,10 +820,15 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro wantSettingsAck: true, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), + syncHooks: hooks, + } + if hooks != nil { + hooks.newclientconn(cc) + c = cc.tconn } if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d - cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) + cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout) } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) @@ -818,7 +893,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro return nil, cc.werr } - go cc.readLoop() + cc.goRun(cc.readLoop) return cc, nil } @@ -826,7 +901,7 @@ func (cc *ClientConn) healthCheck() { pingTimeout := cc.t.pingTimeout() // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. - ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) + ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) @@ -1018,7 +1093,7 @@ func (cc *ClientConn) forceCloseConn() { if !ok { return } - if nc := tlsUnderlyingConn(tc); nc != nil { + if nc := tc.NetConn(); nc != nil { nc.Close() } } @@ -1056,7 +1131,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { // Wait for all in-flight streams to complete or connection to close done := make(chan struct{}) cancelled := false // guarded by cc.mu - go func() { + cc.goRun(func() { cc.mu.Lock() defer cc.mu.Unlock() for { @@ -1068,9 +1143,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { if cancelled { break } - cc.cond.Wait() + cc.condWait() } - }() + }) shutdownEnterWaitStateHook() select { case <-done: @@ -1080,7 +1155,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { cc.mu.Lock() // Free the goroutine above cancelled = true - cc.cond.Broadcast() + cc.condBroadcast() cc.mu.Unlock() return ctx.Err() } @@ -1118,7 +1193,7 @@ func (cc *ClientConn) closeForError(err error) { for _, cs := range cc.streams { cs.abortStreamLocked(err) } - cc.cond.Broadcast() + cc.condBroadcast() cc.mu.Unlock() cc.closeConn() } @@ -1215,6 +1290,10 @@ func (cc *ClientConn) decrStreamReservationsLocked() { } func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { + return cc.roundTrip(req, nil) +} + +func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) { ctx := req.Context() cs := &clientStream{ cc: cc, @@ -1229,9 +1308,23 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { respHeaderRecv: make(chan struct{}), donec: make(chan struct{}), } - go cs.doRequest(req) + cc.goRun(func() { + cs.doRequest(req) + }) waitDone := func() error { + if cc.syncHooks != nil { + cc.syncHooks.blockUntil(func() bool { + select { + case <-cs.donec: + case <-ctx.Done(): + case <-cs.reqCancel: + default: + return false + } + return true + }) + } select { case <-cs.donec: return nil @@ -1292,7 +1385,24 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { return err } + if streamf != nil { + streamf(cs) + } + for { + if cc.syncHooks != nil { + cc.syncHooks.blockUntil(func() bool { + select { + case <-cs.respHeaderRecv: + case <-cs.abort: + case <-ctx.Done(): + case <-cs.reqCancel: + default: + return false + } + return true + }) + } select { case <-cs.respHeaderRecv: return handleResponseHeaders() @@ -1348,6 +1458,21 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } + var newStreamHook func(*clientStream) + if cc.syncHooks != nil { + newStreamHook = cc.syncHooks.newstream + cc.syncHooks.blockUntil(func() bool { + select { + case cc.reqHeaderMu <- struct{}{}: + <-cc.reqHeaderMu + case <-cs.reqCancel: + case <-ctx.Done(): + default: + return false + } + return true + }) + } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: @@ -1372,6 +1497,10 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { } cc.mu.Unlock() + if newStreamHook != nil { + newStreamHook(cs) + } + // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? if !cc.t.disableCompression() && req.Header.Get("Accept-Encoding") == "" && @@ -1452,15 +1581,30 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { - timer := time.NewTimer(d) + timer := cc.newTimer(d) defer timer.Stop() - respHeaderTimer = timer.C + respHeaderTimer = timer.C() respHeaderRecv = cs.respHeaderRecv } // Wait until the peer half-closes its end of the stream, // or until the request is aborted (via context, error, or otherwise), // whichever comes first. for { + if cc.syncHooks != nil { + cc.syncHooks.blockUntil(func() bool { + select { + case <-cs.peerClosed: + case <-respHeaderTimer: + case <-respHeaderRecv: + case <-cs.abort: + case <-ctx.Done(): + case <-cs.reqCancel: + default: + return false + } + return true + }) + } select { case <-cs.peerClosed: return nil @@ -1609,7 +1753,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { return nil } cc.pendingRequests++ - cc.cond.Wait() + cc.condWait() cc.pendingRequests-- select { case <-cs.abort: @@ -1871,10 +2015,26 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) cs.flow.take(take) return take, nil } - cc.cond.Wait() + cc.condWait() } } +func validateHeaders(hdrs http.Header) string { + for k, vv := range hdrs { + if !httpguts.ValidHeaderFieldName(k) { + return fmt.Sprintf("name %q", k) + } + for _, v := range vv { + if !httpguts.ValidHeaderFieldValue(v) { + // Don't include the value in the error, + // because it may be sensitive. + return fmt.Sprintf("value for header %q", k) + } + } + } + return "" +} + var errNilRequestURL = errors.New("http2: Request.URI is nil") // requires cc.wmu be held. @@ -1912,19 +2072,14 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail } } - // Check for any invalid headers and return an error before we + // Check for any invalid headers+trailers and return an error before we // potentially pollute our hpack state. (We want to be able to // continue to reuse the hpack encoder for future requests) - for k, vv := range req.Header { - if !httpguts.ValidHeaderFieldName(k) { - return nil, fmt.Errorf("invalid HTTP header name %q", k) - } - for _, v := range vv { - if !httpguts.ValidHeaderFieldValue(v) { - // Don't include the value in the error, because it may be sensitive. - return nil, fmt.Errorf("invalid HTTP header value for header %q", k) - } - } + if err := validateHeaders(req.Header); err != "" { + return nil, fmt.Errorf("invalid HTTP header %s", err) + } + if err := validateHeaders(req.Trailer); err != "" { + return nil, fmt.Errorf("invalid HTTP trailer %s", err) } enumerateHeaders := func(f func(name, value string)) { @@ -2143,7 +2298,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) { } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. - cc.cond.Broadcast() + cc.condBroadcast() closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { @@ -2231,7 +2386,7 @@ func (rl *clientConnReadLoop) cleanup() { cs.abortStreamLocked(err) } } - cc.cond.Broadcast() + cc.condBroadcast() cc.mu.Unlock() } @@ -2266,10 +2421,9 @@ func (rl *clientConnReadLoop) run() error { cc := rl.cc gotSettings := false readIdleTimeout := cc.t.ReadIdleTimeout - var t *time.Timer + var t timer if readIdleTimeout != 0 { - t = time.AfterFunc(readIdleTimeout, cc.healthCheck) - defer t.Stop() + t = cc.afterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() @@ -2684,7 +2838,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error { }) return nil } - if !cs.firstByte { + if !cs.pastHeaders { cc.logf("protocol error: received DATA before a HEADERS frame") rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, @@ -2867,7 +3021,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { for _, cs := range cc.streams { cs.flow.add(delta) } - cc.cond.Broadcast() + cc.condBroadcast() cc.initialWindowSize = s.Val case SettingHeaderTableSize: @@ -2911,9 +3065,18 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { fl = &cs.flow } if !fl.add(int32(f.Increment)) { + // For stream, the sender sends RST_STREAM with an error code of FLOW_CONTROL_ERROR + if cs != nil { + rl.endStreamError(cs, StreamError{ + StreamID: f.StreamID, + Code: ErrCodeFlowControl, + }) + return nil + } + return ConnectionError(ErrCodeFlowControl) } - cc.cond.Broadcast() + cc.condBroadcast() return nil } @@ -2955,24 +3118,38 @@ func (cc *ClientConn) Ping(ctx context.Context) error { } cc.mu.Unlock() } - errc := make(chan error, 1) - go func() { + var pingError error + errc := make(chan struct{}) + cc.goRun(func() { cc.wmu.Lock() defer cc.wmu.Unlock() - if err := cc.fr.WritePing(false, p); err != nil { - errc <- err + if pingError = cc.fr.WritePing(false, p); pingError != nil { + close(errc) return } - if err := cc.bw.Flush(); err != nil { - errc <- err + if pingError = cc.bw.Flush(); pingError != nil { + close(errc) return } - }() + }) + if cc.syncHooks != nil { + cc.syncHooks.blockUntil(func() bool { + select { + case <-c: + case <-errc: + case <-ctx.Done(): + case <-cc.readerDone: + default: + return false + } + return true + }) + } select { case <-c: return nil - case err := <-errc: - return err + case <-errc: + return pingError case <-ctx.Done(): return ctx.Err() case <-cc.readerDone: @@ -3141,9 +3318,17 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err } func (t *Transport) idleConnTimeout() time.Duration { + // to keep things backwards compatible, we use non-zero values of + // IdleConnTimeout, followed by using the IdleConnTimeout on the underlying + // http1 transport, followed by 0 + if t.IdleConnTimeout != 0 { + return t.IdleConnTimeout + } + if t.t1 != nil { return t.t1.IdleConnTimeout } + return 0 } @@ -3201,3 +3386,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) { trace.GotFirstResponseByte() } } + +func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} diff --git a/vendor/golang.org/x/net/idna/go118.go b/vendor/golang.org/x/net/idna/go118.go index c5c4338db..712f1ad83 100644 --- a/vendor/golang.org/x/net/idna/go118.go +++ b/vendor/golang.org/x/net/idna/go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.18 -// +build go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/idna10.0.0.go b/vendor/golang.org/x/net/idna/idna10.0.0.go index 64ccf85fe..7b3717884 100644 --- a/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/idna9.0.0.go b/vendor/golang.org/x/net/idna/idna9.0.0.go index ee1698cef..cc6a892a4 100644 --- a/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/pre_go118.go b/vendor/golang.org/x/net/idna/pre_go118.go index 3aaccab1c..40e74bb3d 100644 --- a/vendor/golang.org/x/net/idna/pre_go118.go +++ b/vendor/golang.org/x/net/idna/pre_go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.18 -// +build !go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/tables10.0.0.go b/vendor/golang.org/x/net/idna/tables10.0.0.go index d1d62ef45..c6c2bf10a 100644 --- a/vendor/golang.org/x/net/idna/tables10.0.0.go +++ b/vendor/golang.org/x/net/idna/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package idna diff --git a/vendor/golang.org/x/net/idna/tables11.0.0.go b/vendor/golang.org/x/net/idna/tables11.0.0.go index 167efba71..76789393c 100644 --- a/vendor/golang.org/x/net/idna/tables11.0.0.go +++ b/vendor/golang.org/x/net/idna/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package idna diff --git a/vendor/golang.org/x/net/idna/tables12.0.0.go b/vendor/golang.org/x/net/idna/tables12.0.0.go index ab40f7bcc..0600cd2ae 100644 --- a/vendor/golang.org/x/net/idna/tables12.0.0.go +++ b/vendor/golang.org/x/net/idna/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables13.0.0.go b/vendor/golang.org/x/net/idna/tables13.0.0.go index 66701eadf..2fb768ef6 100644 --- a/vendor/golang.org/x/net/idna/tables13.0.0.go +++ b/vendor/golang.org/x/net/idna/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables15.0.0.go b/vendor/golang.org/x/net/idna/tables15.0.0.go index 40033778f..5ff05fe1a 100644 --- a/vendor/golang.org/x/net/idna/tables15.0.0.go +++ b/vendor/golang.org/x/net/idna/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables9.0.0.go b/vendor/golang.org/x/net/idna/tables9.0.0.go index 4074b5332..0f25e84ca 100644 --- a/vendor/golang.org/x/net/idna/tables9.0.0.go +++ b/vendor/golang.org/x/net/idna/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package idna diff --git a/vendor/golang.org/x/net/idna/trie12.0.0.go b/vendor/golang.org/x/net/idna/trie12.0.0.go index bb63f904b..8a75b9667 100644 --- a/vendor/golang.org/x/net/idna/trie12.0.0.go +++ b/vendor/golang.org/x/net/idna/trie12.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.16 -// +build !go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/trie13.0.0.go b/vendor/golang.org/x/net/idna/trie13.0.0.go index 7d68a8dc1..fa45bb907 100644 --- a/vendor/golang.org/x/net/idna/trie13.0.0.go +++ b/vendor/golang.org/x/net/idna/trie13.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.16 -// +build go1.16 package idna diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr.go b/vendor/golang.org/x/net/internal/socket/cmsghdr.go index 4bdaaaf1a..33a5bf59c 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go index 0d30e0a0f..68f438c84 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go index 4936e8a6f..058ea8de8 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go index f6877f98f..3ca0d3a0a 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go index d3dbe1b8e..6d0e426cd 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go index 1d9f2ed62..7ca9cb7e7 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go index 19d46789d..0211f225b 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go index 5b1d50ae7..2038f2904 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go index be6340958..70e6f448b 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || windows || zos -// +build aix windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/empty.s b/vendor/golang.org/x/net/internal/socket/empty.s index 90ab4ca3d..49d79791e 100644 --- a/vendor/golang.org/x/net/internal/socket/empty.s +++ b/vendor/golang.org/x/net/internal/socket/empty.s @@ -3,6 +3,5 @@ // license that can be found in the LICENSE file. //go:build darwin && go1.12 -// +build darwin,go1.12 // This exists solely so we can linkname in symbols from syscall. diff --git a/vendor/golang.org/x/net/internal/socket/error_unix.go b/vendor/golang.org/x/net/internal/socket/error_unix.go index 78f412904..7a5cc5c43 100644 --- a/vendor/golang.org/x/net/internal/socket/error_unix.go +++ b/vendor/golang.org/x/net/internal/socket/error_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go index 2b8fbb3f3..340e53fbd 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd) -// +build arm mips mipsle 386 ppc -// +build darwin dragonfly freebsd linux netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go index 2e94e96f8..26470c191 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos) -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build aix darwin dragonfly freebsd linux netbsd openbsd zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go index f7da2bc4d..8859ce103 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_stub.go b/vendor/golang.org/x/net/internal/socket/iovec_stub.go index 14caf5248..da886b032 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_stub.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go index 113e773cd..4825b21e3 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !linux && !netbsd -// +build !aix,!linux,!netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go index 41883c530..311fd2c78 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || netbsd -// +build aix linux netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go index 25f6847f9..ebff4f6e0 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go index 5b8e00f1c..62e6fe861 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd -// +build aix darwin dragonfly freebsd netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go index b4658fbae..3dd07250a 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go index 42411affa..5af9ddd6a 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go index 3098f5d78..e212b50f8 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go index eb79151f6..e87677645 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go index 324e9ee7d..529db68ee 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build s390x && zos -// +build s390x,zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/norace.go b/vendor/golang.org/x/net/internal/socket/norace.go index de0ad420f..8af30ecfb 100644 --- a/vendor/golang.org/x/net/internal/socket/norace.go +++ b/vendor/golang.org/x/net/internal/socket/norace.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race package socket diff --git a/vendor/golang.org/x/net/internal/socket/race.go b/vendor/golang.org/x/net/internal/socket/race.go index f0a28a625..9afa95808 100644 --- a/vendor/golang.org/x/net/internal/socket/race.go +++ b/vendor/golang.org/x/net/internal/socket/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go index 8f79b38f7..043139078 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go index f7d0b0d2b..7c0d7410b 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go index 02f328556..e363fb5a8 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go index dd785877b..ff7a8baf0 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_bsd.go b/vendor/golang.org/x/net/internal/socket/sys_bsd.go index b258879d4..e7664d48b 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || openbsd || solaris -// +build aix darwin dragonfly freebsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go index 5d99f2373..d7627f87e 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux.go b/vendor/golang.org/x/net/internal/socket/sys_linux.go index 76f5b8ae5..08d491077 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !s390x && !386 -// +build linux,!s390x,!386 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go index af964e617..1d182470d 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go index 5b128fbb2..0e407d125 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_posix.go b/vendor/golang.org/x/net/internal/socket/sys_posix.go index 42b8f2340..58d865482 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_posix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_stub.go b/vendor/golang.org/x/net/internal/socket/sys_stub.go index 7cfb349c0..2e5b473c6 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_stub.go +++ b/vendor/golang.org/x/net/internal/socket/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_unix.go b/vendor/golang.org/x/net/internal/socket/sys_unix.go index de823932b..93058db5b 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go index 00691bd52..45bab004c 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go index 6a94fec2c..b6fc15a1a 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go index c066272dd..e67fc3cba 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/ipv4/control_bsd.go b/vendor/golang.org/x/net/ipv4/control_bsd.go index b7385dfd9..c88da8cbe 100644 --- a/vendor/golang.org/x/net/ipv4/control_bsd.go +++ b/vendor/golang.org/x/net/ipv4/control_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_pktinfo.go b/vendor/golang.org/x/net/ipv4/control_pktinfo.go index 0e748dbdc..14ae2dae4 100644 --- a/vendor/golang.org/x/net/ipv4/control_pktinfo.go +++ b/vendor/golang.org/x/net/ipv4/control_pktinfo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || linux || solaris -// +build darwin linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_stub.go b/vendor/golang.org/x/net/ipv4/control_stub.go index f27322c3e..3ba661160 100644 --- a/vendor/golang.org/x/net/ipv4/control_stub.go +++ b/vendor/golang.org/x/net/ipv4/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_unix.go b/vendor/golang.org/x/net/ipv4/control_unix.go index 2413e02f8..2e765548f 100644 --- a/vendor/golang.org/x/net/ipv4/control_unix.go +++ b/vendor/golang.org/x/net/ipv4/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/icmp_stub.go b/vendor/golang.org/x/net/ipv4/icmp_stub.go index cd4ee6e1c..c2c4ce7ff 100644 --- a/vendor/golang.org/x/net/ipv4/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv4/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg.go b/vendor/golang.org/x/net/ipv4/payload_cmsg.go index 1bb370e25..91c685e8f 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go index 53f0794eb..2afd4b50e 100644 --- a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_posix.go b/vendor/golang.org/x/net/ipv4/sockopt_posix.go index eb07c1c02..82e2c3783 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_stub.go b/vendor/golang.org/x/net/ipv4/sockopt_stub.go index cf036893b..840108bf7 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_aix.go b/vendor/golang.org/x/net/ipv4/sys_aix.go index 02730cdfd..9244a68a3 100644 --- a/vendor/golang.org/x/net/ipv4/sys_aix.go +++ b/vendor/golang.org/x/net/ipv4/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq.go b/vendor/golang.org/x/net/ipv4/sys_asmreq.go index 22322b387..645f254c6 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd netbsd openbsd solaris windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go index fde640142..48cfb6db2 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go index 54eb9901b..0b27b632f 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux -// +build darwin freebsd linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go index dcb15f25a..303a5e2e6 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux -// +build !darwin,!freebsd,!linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf.go b/vendor/golang.org/x/net/ipv4/sys_bpf.go index fb11e324e..1b4780df4 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go index fc53a0d33..b1f779b49 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bsd.go b/vendor/golang.org/x/net/ipv4/sys_bsd.go index e191b2f14..b7b032d26 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv4/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build netbsd || openbsd -// +build netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go index 6a4e7abf9..a295e15ea 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux || solaris -// +build darwin freebsd linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go index 157159fd5..74bd454e2 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux && !solaris -// +build !darwin,!freebsd,!linux,!solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_stub.go b/vendor/golang.org/x/net/ipv4/sys_stub.go index d55085165..20af4074c 100644 --- a/vendor/golang.org/x/net/ipv4/sys_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go index b7f2d6e5c..dd454025c 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go index e15c22c74..54f9e1394 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go index e2edebdb8..78374a525 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go index 2733ddbe2..a8f04e7b3 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go index 9c90844aa..51fbbb1f1 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_stub.go b/vendor/golang.org/x/net/ipv6/control_stub.go index b7e8643fc..eb28ce753 100644 --- a/vendor/golang.org/x/net/ipv6/control_stub.go +++ b/vendor/golang.org/x/net/ipv6/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_unix.go b/vendor/golang.org/x/net/ipv6/control_unix.go index 63e475db8..9c73b8647 100644 --- a/vendor/golang.org/x/net/ipv6/control_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_bsd.go b/vendor/golang.org/x/net/ipv6/icmp_bsd.go index 120bf8775..2814534a0 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_bsd.go +++ b/vendor/golang.org/x/net/ipv6/icmp_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_stub.go b/vendor/golang.org/x/net/ipv6/icmp_stub.go index d60136a90..c92c9b51e 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv6/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg.go b/vendor/golang.org/x/net/ipv6/payload_cmsg.go index b0692e430..be04e4d6a 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go index cd0ff5083..29b9ccf69 100644 --- a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_posix.go b/vendor/golang.org/x/net/ipv6/sockopt_posix.go index 37c628713..34dfed588 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_stub.go b/vendor/golang.org/x/net/ipv6/sockopt_stub.go index 32fd8664c..a09c3aaf2 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_aix.go b/vendor/golang.org/x/net/ipv6/sys_aix.go index a47182afb..93c8efc46 100644 --- a/vendor/golang.org/x/net/ipv6/sys_aix.go +++ b/vendor/golang.org/x/net/ipv6/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq.go b/vendor/golang.org/x/net/ipv6/sys_asmreq.go index 6ff9950d1..5c9cb4447 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go index 485290cb8..dc7049468 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf.go b/vendor/golang.org/x/net/ipv6/sys_bpf.go index b5661fb8f..e39f75f49 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go index cb0066187..8532a8f5d 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bsd.go b/vendor/golang.org/x/net/ipv6/sys_bsd.go index bde41a6ce..9f3bc2afd 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv6/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || netbsd || openbsd -// +build dragonfly netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go index 023488a49..b40f5c685 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || solaris || zos -// +build aix darwin freebsd linux solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go index acdf2e5cf..6526aad58 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !freebsd && !linux && !solaris && !zos -// +build !aix,!darwin,!freebsd,!linux,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_stub.go b/vendor/golang.org/x/net/ipv6/sys_stub.go index 5807bba39..76602c34e 100644 --- a/vendor/golang.org/x/net/ipv6/sys_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go index f604b0f3b..668716df4 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go index 598fbfa06..6a53284db 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go index d4f78e405..13b347205 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv6 diff --git a/vendor/golang.org/x/oauth2/deviceauth.go b/vendor/golang.org/x/oauth2/deviceauth.go new file mode 100644 index 000000000..e99c92f39 --- /dev/null +++ b/vendor/golang.org/x/oauth2/deviceauth.go @@ -0,0 +1,198 @@ +package oauth2 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/oauth2/internal" +) + +// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5 +const ( + errAuthorizationPending = "authorization_pending" + errSlowDown = "slow_down" + errAccessDenied = "access_denied" + errExpiredToken = "expired_token" +) + +// DeviceAuthResponse describes a successful RFC 8628 Device Authorization Response +// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2 +type DeviceAuthResponse struct { + // DeviceCode + DeviceCode string `json:"device_code"` + // UserCode is the code the user should enter at the verification uri + UserCode string `json:"user_code"` + // VerificationURI is where user should enter the user code + VerificationURI string `json:"verification_uri"` + // VerificationURIComplete (if populated) includes the user code in the verification URI. This is typically shown to the user in non-textual form, such as a QR code. + VerificationURIComplete string `json:"verification_uri_complete,omitempty"` + // Expiry is when the device code and user code expire + Expiry time.Time `json:"expires_in,omitempty"` + // Interval is the duration in seconds that Poll should wait between requests + Interval int64 `json:"interval,omitempty"` +} + +func (d DeviceAuthResponse) MarshalJSON() ([]byte, error) { + type Alias DeviceAuthResponse + var expiresIn int64 + if !d.Expiry.IsZero() { + expiresIn = int64(time.Until(d.Expiry).Seconds()) + } + return json.Marshal(&struct { + ExpiresIn int64 `json:"expires_in,omitempty"` + *Alias + }{ + ExpiresIn: expiresIn, + Alias: (*Alias)(&d), + }) + +} + +func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error { + type Alias DeviceAuthResponse + aux := &struct { + ExpiresIn int64 `json:"expires_in"` + // workaround misspelling of verification_uri + VerificationURL string `json:"verification_url"` + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + if aux.ExpiresIn != 0 { + c.Expiry = time.Now().UTC().Add(time.Second * time.Duration(aux.ExpiresIn)) + } + if c.VerificationURI == "" { + c.VerificationURI = aux.VerificationURL + } + return nil +} + +// DeviceAuth returns a device auth struct which contains a device code +// and authorization information provided for users to enter on another device. +func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error) { + // https://datatracker.ietf.org/doc/html/rfc8628#section-3.1 + v := url.Values{ + "client_id": {c.ClientID}, + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + for _, opt := range opts { + opt.setValue(v) + } + return retrieveDeviceAuth(ctx, c, v) +} + +func retrieveDeviceAuth(ctx context.Context, c *Config, v url.Values) (*DeviceAuthResponse, error) { + if c.Endpoint.DeviceAuthURL == "" { + return nil, errors.New("endpoint missing DeviceAuthURL") + } + + req, err := http.NewRequest("POST", c.Endpoint.DeviceAuthURL, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + t := time.Now() + r, err := internal.ContextClient(ctx).Do(req) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot auth device: %v", err) + } + if code := r.StatusCode; code < 200 || code > 299 { + return nil, &RetrieveError{ + Response: r, + Body: body, + } + } + + da := &DeviceAuthResponse{} + err = json.Unmarshal(body, &da) + if err != nil { + return nil, fmt.Errorf("unmarshal %s", err) + } + + if !da.Expiry.IsZero() { + // Make a small adjustment to account for time taken by the request + da.Expiry = da.Expiry.Add(-time.Since(t)) + } + + return da, nil +} + +// DeviceAccessToken polls the server to exchange a device code for a token. +func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error) { + if !da.Expiry.IsZero() { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, da.Expiry) + defer cancel() + } + + // https://datatracker.ietf.org/doc/html/rfc8628#section-3.4 + v := url.Values{ + "client_id": {c.ClientID}, + "grant_type": {"urn:ietf:params:oauth:grant-type:device_code"}, + "device_code": {da.DeviceCode}, + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + for _, opt := range opts { + opt.setValue(v) + } + + // "If no value is provided, clients MUST use 5 as the default." + // https://datatracker.ietf.org/doc/html/rfc8628#section-3.2 + interval := da.Interval + if interval == 0 { + interval = 5 + } + + ticker := time.NewTicker(time.Duration(interval) * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-ticker.C: + tok, err := retrieveToken(ctx, c, v) + if err == nil { + return tok, nil + } + + e, ok := err.(*RetrieveError) + if !ok { + return nil, err + } + switch e.ErrorCode { + case errSlowDown: + // https://datatracker.ietf.org/doc/html/rfc8628#section-3.5 + // "the interval MUST be increased by 5 seconds for this and all subsequent requests" + interval += 5 + ticker.Reset(time.Duration(interval) * time.Second) + case errAuthorizationPending: + // Do nothing. + case errAccessDenied, errExpiredToken: + fallthrough + default: + return tok, err + } + } + } +} diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go index e1755d1d9..d28140f78 100644 --- a/vendor/golang.org/x/oauth2/internal/client_appengine.go +++ b/vendor/golang.org/x/oauth2/internal/client_appengine.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build appengine -// +build appengine package internal diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go index c0ab196cf..14989beaf 100644 --- a/vendor/golang.org/x/oauth2/internal/oauth2.go +++ b/vendor/golang.org/x/oauth2/internal/oauth2.go @@ -14,7 +14,7 @@ import ( // ParseKey converts the binary contents of a private key file // to an *rsa.PrivateKey. It detects whether the private key is in a -// PEM container or not. If so, it extracts the the private key +// PEM container or not. If so, it extracts the private key // from PEM container before conversion. It only supports PEM // containers with no passphrase. func ParseKey(key []byte) (*rsa.PrivateKey, error) { diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go index b4723fcac..e83ddeef0 100644 --- a/vendor/golang.org/x/oauth2/internal/token.go +++ b/vendor/golang.org/x/oauth2/internal/token.go @@ -18,6 +18,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" ) @@ -55,12 +56,18 @@ type Token struct { } // tokenJSON is the struct representing the HTTP response from OAuth2 -// providers returning a token in JSON form. +// providers returning a token or error in JSON form. +// https://datatracker.ietf.org/doc/html/rfc6749#section-5.1 type tokenJSON struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` RefreshToken string `json:"refresh_token"` ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number + // error fields + // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 + ErrorCode string `json:"error"` + ErrorDescription string `json:"error_description"` + ErrorURI string `json:"error_uri"` } func (e *tokenJSON) expiry() (t time.Time) { @@ -109,41 +116,60 @@ const ( AuthStyleInHeader AuthStyle = 2 ) -// authStyleCache is the set of tokenURLs we've successfully used via +// LazyAuthStyleCache is a backwards compatibility compromise to let Configs +// have a lazily-initialized AuthStyleCache. +// +// The two users of this, oauth2.Config and oauth2/clientcredentials.Config, +// both would ideally just embed an unexported AuthStyleCache but because both +// were historically allowed to be copied by value we can't retroactively add an +// uncopyable Mutex to them. +// +// We could use an atomic.Pointer, but that was added recently enough (in Go +// 1.18) that we'd break Go 1.17 users where the tests as of 2023-08-03 +// still pass. By using an atomic.Value, it supports both Go 1.17 and +// copying by value, even if that's not ideal. +type LazyAuthStyleCache struct { + v atomic.Value // of *AuthStyleCache +} + +func (lc *LazyAuthStyleCache) Get() *AuthStyleCache { + if c, ok := lc.v.Load().(*AuthStyleCache); ok { + return c + } + c := new(AuthStyleCache) + if !lc.v.CompareAndSwap(nil, c) { + c = lc.v.Load().(*AuthStyleCache) + } + return c +} + +// AuthStyleCache is the set of tokenURLs we've successfully used via // RetrieveToken and which style auth we ended up using. // It's called a cache, but it doesn't (yet?) shrink. It's expected that // the set of OAuth2 servers a program contacts over time is fixed and // small. -var authStyleCache struct { - sync.Mutex - m map[string]AuthStyle // keyed by tokenURL -} - -// ResetAuthCache resets the global authentication style cache used -// for AuthStyleUnknown token requests. -func ResetAuthCache() { - authStyleCache.Lock() - defer authStyleCache.Unlock() - authStyleCache.m = nil +type AuthStyleCache struct { + mu sync.Mutex + m map[string]AuthStyle // keyed by tokenURL } // lookupAuthStyle reports which auth style we last used with tokenURL // when calling RetrieveToken and whether we have ever done so. -func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) { - authStyleCache.Lock() - defer authStyleCache.Unlock() - style, ok = authStyleCache.m[tokenURL] +func (c *AuthStyleCache) lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + style, ok = c.m[tokenURL] return } // setAuthStyle adds an entry to authStyleCache, documented above. -func setAuthStyle(tokenURL string, v AuthStyle) { - authStyleCache.Lock() - defer authStyleCache.Unlock() - if authStyleCache.m == nil { - authStyleCache.m = make(map[string]AuthStyle) +func (c *AuthStyleCache) setAuthStyle(tokenURL string, v AuthStyle) { + c.mu.Lock() + defer c.mu.Unlock() + if c.m == nil { + c.m = make(map[string]AuthStyle) } - authStyleCache.m[tokenURL] = v + c.m[tokenURL] = v } // newTokenRequest returns a new *http.Request to retrieve a new token @@ -183,10 +209,10 @@ func cloneURLValues(v url.Values) url.Values { return v2 } -func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) { +func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle, styleCache *AuthStyleCache) (*Token, error) { needsAuthStyleProbe := authStyle == 0 if needsAuthStyleProbe { - if style, ok := lookupAuthStyle(tokenURL); ok { + if style, ok := styleCache.lookupAuthStyle(tokenURL); ok { authStyle = style needsAuthStyleProbe = false } else { @@ -216,7 +242,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, token, err = doTokenRoundTrip(ctx, req) } if needsAuthStyleProbe && err == nil { - setAuthStyle(tokenURL, authStyle) + styleCache.setAuthStyle(tokenURL, authStyle) } // Don't overwrite `RefreshToken` with an empty value // if this was a token refreshing request. @@ -236,21 +262,29 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) { if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } - if code := r.StatusCode; code < 200 || code > 299 { - return nil, &RetrieveError{ - Response: r, - Body: body, - } + + failureStatus := r.StatusCode < 200 || r.StatusCode > 299 + retrieveError := &RetrieveError{ + Response: r, + Body: body, + // attempt to populate error detail below } var token *Token content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) switch content { case "application/x-www-form-urlencoded", "text/plain": + // some endpoints return a query string vals, err := url.ParseQuery(string(body)) if err != nil { - return nil, err + if failureStatus { + return nil, retrieveError + } + return nil, fmt.Errorf("oauth2: cannot parse response: %v", err) } + retrieveError.ErrorCode = vals.Get("error") + retrieveError.ErrorDescription = vals.Get("error_description") + retrieveError.ErrorURI = vals.Get("error_uri") token = &Token{ AccessToken: vals.Get("access_token"), TokenType: vals.Get("token_type"), @@ -265,8 +299,14 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) { default: var tj tokenJSON if err = json.Unmarshal(body, &tj); err != nil { - return nil, err + if failureStatus { + return nil, retrieveError + } + return nil, fmt.Errorf("oauth2: cannot parse json: %v", err) } + retrieveError.ErrorCode = tj.ErrorCode + retrieveError.ErrorDescription = tj.ErrorDescription + retrieveError.ErrorURI = tj.ErrorURI token = &Token{ AccessToken: tj.AccessToken, TokenType: tj.TokenType, @@ -276,17 +316,37 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) { } json.Unmarshal(body, &token.Raw) // no error checks for optional fields } + // according to spec, servers should respond status 400 in error case + // https://www.rfc-editor.org/rfc/rfc6749#section-5.2 + // but some unorthodox servers respond 200 in error case + if failureStatus || retrieveError.ErrorCode != "" { + return nil, retrieveError + } if token.AccessToken == "" { return nil, errors.New("oauth2: server response missing access_token") } return token, nil } +// mirrors oauth2.RetrieveError type RetrieveError struct { - Response *http.Response - Body []byte + Response *http.Response + Body []byte + ErrorCode string + ErrorDescription string + ErrorURI string } func (r *RetrieveError) Error() string { + if r.ErrorCode != "" { + s := fmt.Sprintf("oauth2: %q", r.ErrorCode) + if r.ErrorDescription != "" { + s += fmt.Sprintf(" %q", r.ErrorDescription) + } + if r.ErrorURI != "" { + s += fmt.Sprintf(" %q", r.ErrorURI) + } + return s + } return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) } diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go index 9085fabe3..90a2c3d6d 100644 --- a/vendor/golang.org/x/oauth2/oauth2.go +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -58,6 +58,10 @@ type Config struct { // Scope specifies optional requested permissions. Scopes []string + + // authStyleCache caches which auth style to use when Endpoint.AuthStyle is + // the zero value (AuthStyleAutoDetect). + authStyleCache internal.LazyAuthStyleCache } // A TokenSource is anything that can return a token. @@ -71,8 +75,9 @@ type TokenSource interface { // Endpoint represents an OAuth 2.0 provider's authorization and token // endpoint URLs. type Endpoint struct { - AuthURL string - TokenURL string + AuthURL string + DeviceAuthURL string + TokenURL string // AuthStyle optionally specifies how the endpoint wants the // client ID & client secret sent. The zero value means to @@ -139,15 +144,19 @@ func SetAuthURLParam(key, value string) AuthCodeOption { // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page // that asks for permissions for the required scopes explicitly. // -// State is a token to protect the user from CSRF attacks. You must -// always provide a non-empty string and validate that it matches the -// state query parameter on your redirect callback. -// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. +// State is an opaque value used by the client to maintain state between the +// request and callback. The authorization server includes this value when +// redirecting the user agent back to the client. // // Opts may include AccessTypeOnline or AccessTypeOffline, as well // as ApprovalForce. -// It can also be used to pass the PKCE challenge. -// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +// +// To protect against CSRF attacks, opts should include a PKCE challenge +// (S256ChallengeOption). Not all servers support PKCE. An alternative is to +// generate a random state parameter and verify it after exchange. +// See https://datatracker.ietf.org/doc/html/rfc6749#section-10.12 (predating +// PKCE), https://www.oauth.com/oauth2-servers/pkce/ and +// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-cross-site-request-forgery (describing both approaches) func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { var buf bytes.Buffer buf.WriteString(c.Endpoint.AuthURL) @@ -162,7 +171,6 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { v.Set("scope", strings.Join(c.Scopes, " ")) } if state != "" { - // TODO(light): Docs say never to omit state; don't allow empty. v.Set("state", state) } for _, opt := range opts { @@ -207,10 +215,11 @@ func (c *Config) PasswordCredentialsToken(ctx context.Context, username, passwor // The provided context optionally controls which HTTP client is used. See the HTTPClient variable. // // The code will be in the *http.Request.FormValue("code"). Before -// calling Exchange, be sure to validate FormValue("state"). +// calling Exchange, be sure to validate FormValue("state") if you are +// using it to protect against CSRF attacks. // -// Opts may include the PKCE verifier code if previously used in AuthCodeURL. -// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +// If using PKCE to protect against CSRF attacks, opts should include a +// VerifierOption. func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) { v := url.Values{ "grant_type": {"authorization_code"}, diff --git a/vendor/golang.org/x/oauth2/pkce.go b/vendor/golang.org/x/oauth2/pkce.go new file mode 100644 index 000000000..50593b6df --- /dev/null +++ b/vendor/golang.org/x/oauth2/pkce.go @@ -0,0 +1,68 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package oauth2 + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "net/url" +) + +const ( + codeChallengeKey = "code_challenge" + codeChallengeMethodKey = "code_challenge_method" + codeVerifierKey = "code_verifier" +) + +// GenerateVerifier generates a PKCE code verifier with 32 octets of randomness. +// This follows recommendations in RFC 7636. +// +// A fresh verifier should be generated for each authorization. +// S256ChallengeOption(verifier) should then be passed to Config.AuthCodeURL +// (or Config.DeviceAccess) and VerifierOption(verifier) to Config.Exchange +// (or Config.DeviceAccessToken). +func GenerateVerifier() string { + // "RECOMMENDED that the output of a suitable random number generator be + // used to create a 32-octet sequence. The octet sequence is then + // base64url-encoded to produce a 43-octet URL-safe string to use as the + // code verifier." + // https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 + data := make([]byte, 32) + if _, err := rand.Read(data); err != nil { + panic(err) + } + return base64.RawURLEncoding.EncodeToString(data) +} + +// VerifierOption returns a PKCE code verifier AuthCodeOption. It should be +// passed to Config.Exchange or Config.DeviceAccessToken only. +func VerifierOption(verifier string) AuthCodeOption { + return setParam{k: codeVerifierKey, v: verifier} +} + +// S256ChallengeFromVerifier returns a PKCE code challenge derived from verifier with method S256. +// +// Prefer to use S256ChallengeOption where possible. +func S256ChallengeFromVerifier(verifier string) string { + sha := sha256.Sum256([]byte(verifier)) + return base64.RawURLEncoding.EncodeToString(sha[:]) +} + +// S256ChallengeOption derives a PKCE code challenge derived from verifier with +// method S256. It should be passed to Config.AuthCodeURL or Config.DeviceAccess +// only. +func S256ChallengeOption(verifier string) AuthCodeOption { + return challengeOption{ + challenge_method: "S256", + challenge: S256ChallengeFromVerifier(verifier), + } +} + +type challengeOption struct{ challenge_method, challenge string } + +func (p challengeOption) setValue(m url.Values) { + m.Set(codeChallengeMethodKey, p.challenge_method) + m.Set(codeChallengeKey, p.challenge) +} diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go index 7c64006de..5bbb33217 100644 --- a/vendor/golang.org/x/oauth2/token.go +++ b/vendor/golang.org/x/oauth2/token.go @@ -164,7 +164,7 @@ func tokenFromInternal(t *internal.Token) *Token { // This token is then mapped from *internal.Token into an *oauth2.Token which is returned along // with an error.. func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { - tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle)) + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle), c.authStyleCache.Get()) if err != nil { if rErr, ok := err.(*internal.RetrieveError); ok { return nil, (*RetrieveError)(rErr) @@ -175,14 +175,31 @@ func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) } // RetrieveError is the error returned when the token endpoint returns a -// non-2XX HTTP status code. +// non-2XX HTTP status code or populates RFC 6749's 'error' parameter. +// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 type RetrieveError struct { Response *http.Response // Body is the body that was consumed by reading Response.Body. // It may be truncated. Body []byte + // ErrorCode is RFC 6749's 'error' parameter. + ErrorCode string + // ErrorDescription is RFC 6749's 'error_description' parameter. + ErrorDescription string + // ErrorURI is RFC 6749's 'error_uri' parameter. + ErrorURI string } func (r *RetrieveError) Error() string { + if r.ErrorCode != "" { + s := fmt.Sprintf("oauth2: %q", r.ErrorCode) + if r.ErrorDescription != "" { + s += fmt.Sprintf(" %q", r.ErrorDescription) + } + if r.ErrorURI != "" { + s += fmt.Sprintf(" %q", r.ErrorURI) + } + return s + } return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) } diff --git a/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s new file mode 100644 index 000000000..269e173ca --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// +// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go +// + +TEXT ·syscall6(SB),NOSPLIT,$0-88 + JMP syscall·syscall6(SB) + +TEXT ·rawSyscall6(SB),NOSPLIT,$0-88 + JMP syscall·rawSyscall6(SB) diff --git a/vendor/golang.org/x/sys/cpu/byteorder.go b/vendor/golang.org/x/sys/cpu/byteorder.go new file mode 100644 index 000000000..271055be0 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/byteorder.go @@ -0,0 +1,66 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "runtime" +) + +// byteOrder is a subset of encoding/binary.ByteOrder. +type byteOrder interface { + Uint32([]byte) uint32 + Uint64([]byte) uint64 +} + +type littleEndian struct{} +type bigEndian struct{} + +func (littleEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (bigEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +// hostByteOrder returns littleEndian on little-endian machines and +// bigEndian on big-endian machines. +func hostByteOrder() byteOrder { + switch runtime.GOARCH { + case "386", "amd64", "amd64p32", + "alpha", + "arm", "arm64", + "loong64", + "mipsle", "mips64le", "mips64p32le", + "nios2", + "ppc64le", + "riscv", "riscv64", + "sh": + return littleEndian{} + case "armbe", "arm64be", + "m68k", + "mips", "mips64", "mips64p32", + "ppc", "ppc64", + "s390", "s390x", + "shbe", + "sparc", "sparc64": + return bigEndian{} + } + panic("unknown architecture") +} diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go new file mode 100644 index 000000000..4756ad5f7 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -0,0 +1,290 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cpu implements processor feature detection for +// various CPU architectures. +package cpu + +import ( + "os" + "strings" +) + +// Initialized reports whether the CPU features were initialized. +// +// For some GOOS/GOARCH combinations initialization of the CPU features depends +// on reading an operating specific file, e.g. /proc/self/auxv on linux/arm +// Initialized will report false if reading the file fails. +var Initialized bool + +// CacheLinePad is used to pad structs to avoid false sharing. +type CacheLinePad struct{ _ [cacheLineSize]byte } + +// X86 contains the supported CPU features of the +// current X86/AMD64 platform. If the current platform +// is not X86/AMD64 then all feature flags are false. +// +// X86 is padded to avoid false sharing. Further the HasAVX +// and HasAVX2 are only set if the OS supports XMM and YMM +// registers in addition to the CPUID feature bit being set. +var X86 struct { + _ CacheLinePad + HasAES bool // AES hardware implementation (AES NI) + HasADX bool // Multi-precision add-carry instruction extensions + HasAVX bool // Advanced vector extension + HasAVX2 bool // Advanced vector extension 2 + HasAVX512 bool // Advanced vector extension 512 + HasAVX512F bool // Advanced vector extension 512 Foundation Instructions + HasAVX512CD bool // Advanced vector extension 512 Conflict Detection Instructions + HasAVX512ER bool // Advanced vector extension 512 Exponential and Reciprocal Instructions + HasAVX512PF bool // Advanced vector extension 512 Prefetch Instructions + HasAVX512VL bool // Advanced vector extension 512 Vector Length Extensions + HasAVX512BW bool // Advanced vector extension 512 Byte and Word Instructions + HasAVX512DQ bool // Advanced vector extension 512 Doubleword and Quadword Instructions + HasAVX512IFMA bool // Advanced vector extension 512 Integer Fused Multiply Add + HasAVX512VBMI bool // Advanced vector extension 512 Vector Byte Manipulation Instructions + HasAVX5124VNNIW bool // Advanced vector extension 512 Vector Neural Network Instructions Word variable precision + HasAVX5124FMAPS bool // Advanced vector extension 512 Fused Multiply Accumulation Packed Single precision + HasAVX512VPOPCNTDQ bool // Advanced vector extension 512 Double and quad word population count instructions + HasAVX512VPCLMULQDQ bool // Advanced vector extension 512 Vector carry-less multiply operations + HasAVX512VNNI bool // Advanced vector extension 512 Vector Neural Network Instructions + HasAVX512GFNI bool // Advanced vector extension 512 Galois field New Instructions + HasAVX512VAES bool // Advanced vector extension 512 Vector AES instructions + HasAVX512VBMI2 bool // Advanced vector extension 512 Vector Byte Manipulation Instructions 2 + HasAVX512BITALG bool // Advanced vector extension 512 Bit Algorithms + HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions + HasAMXTile bool // Advanced Matrix Extension Tile instructions + HasAMXInt8 bool // Advanced Matrix Extension Int8 instructions + HasAMXBF16 bool // Advanced Matrix Extension BFloat16 instructions + HasBMI1 bool // Bit manipulation instruction set 1 + HasBMI2 bool // Bit manipulation instruction set 2 + HasCX16 bool // Compare and exchange 16 Bytes + HasERMS bool // Enhanced REP for MOVSB and STOSB + HasFMA bool // Fused-multiply-add instructions + HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. + HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM + HasPOPCNT bool // Hamming weight instruction POPCNT. + HasRDRAND bool // RDRAND instruction (on-chip random number generator) + HasRDSEED bool // RDSEED instruction (on-chip random number generator) + HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64) + HasSSE3 bool // Streaming SIMD extension 3 + HasSSSE3 bool // Supplemental streaming SIMD extension 3 + HasSSE41 bool // Streaming SIMD extension 4 and 4.1 + HasSSE42 bool // Streaming SIMD extension 4 and 4.2 + _ CacheLinePad +} + +// ARM64 contains the supported CPU features of the +// current ARMv8(aarch64) platform. If the current platform +// is not arm64 then all feature flags are false. +var ARM64 struct { + _ CacheLinePad + HasFP bool // Floating-point instruction set (always available) + HasASIMD bool // Advanced SIMD (always available) + HasEVTSTRM bool // Event stream support + HasAES bool // AES hardware implementation + HasPMULL bool // Polynomial multiplication instruction set + HasSHA1 bool // SHA1 hardware implementation + HasSHA2 bool // SHA2 hardware implementation + HasCRC32 bool // CRC32 hardware implementation + HasATOMICS bool // Atomic memory operation instruction set + HasFPHP bool // Half precision floating-point instruction set + HasASIMDHP bool // Advanced SIMD half precision instruction set + HasCPUID bool // CPUID identification scheme registers + HasASIMDRDM bool // Rounding double multiply add/subtract instruction set + HasJSCVT bool // Javascript conversion from floating-point to integer + HasFCMA bool // Floating-point multiplication and addition of complex numbers + HasLRCPC bool // Release Consistent processor consistent support + HasDCPOP bool // Persistent memory support + HasSHA3 bool // SHA3 hardware implementation + HasSM3 bool // SM3 hardware implementation + HasSM4 bool // SM4 hardware implementation + HasASIMDDP bool // Advanced SIMD double precision instruction set + HasSHA512 bool // SHA512 hardware implementation + HasSVE bool // Scalable Vector Extensions + HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 + _ CacheLinePad +} + +// ARM contains the supported CPU features of the current ARM (32-bit) platform. +// All feature flags are false if: +// 1. the current platform is not arm, or +// 2. the current operating system is not Linux. +var ARM struct { + _ CacheLinePad + HasSWP bool // SWP instruction support + HasHALF bool // Half-word load and store support + HasTHUMB bool // ARM Thumb instruction set + Has26BIT bool // Address space limited to 26-bits + HasFASTMUL bool // 32-bit operand, 64-bit result multiplication support + HasFPA bool // Floating point arithmetic support + HasVFP bool // Vector floating point support + HasEDSP bool // DSP Extensions support + HasJAVA bool // Java instruction set + HasIWMMXT bool // Intel Wireless MMX technology support + HasCRUNCH bool // MaverickCrunch context switching and handling + HasTHUMBEE bool // Thumb EE instruction set + HasNEON bool // NEON instruction set + HasVFPv3 bool // Vector floating point version 3 support + HasVFPv3D16 bool // Vector floating point version 3 D8-D15 + HasTLS bool // Thread local storage support + HasVFPv4 bool // Vector floating point version 4 support + HasIDIVA bool // Integer divide instruction support in ARM mode + HasIDIVT bool // Integer divide instruction support in Thumb mode + HasVFPD32 bool // Vector floating point version 3 D15-D31 + HasLPAE bool // Large Physical Address Extensions + HasEVTSTRM bool // Event stream support + HasAES bool // AES hardware implementation + HasPMULL bool // Polynomial multiplication instruction set + HasSHA1 bool // SHA1 hardware implementation + HasSHA2 bool // SHA2 hardware implementation + HasCRC32 bool // CRC32 hardware implementation + _ CacheLinePad +} + +// MIPS64X contains the supported CPU features of the current mips64/mips64le +// platforms. If the current platform is not mips64/mips64le or the current +// operating system is not Linux then all feature flags are false. +var MIPS64X struct { + _ CacheLinePad + HasMSA bool // MIPS SIMD architecture + _ CacheLinePad +} + +// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms. +// If the current platform is not ppc64/ppc64le then all feature flags are false. +// +// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00, +// since there are no optional categories. There are some exceptions that also +// require kernel support to work (DARN, SCV), so there are feature bits for +// those as well. The struct is padded to avoid false sharing. +var PPC64 struct { + _ CacheLinePad + HasDARN bool // Hardware random number generator (requires kernel enablement) + HasSCV bool // Syscall vectored (requires kernel enablement) + IsPOWER8 bool // ISA v2.07 (POWER8) + IsPOWER9 bool // ISA v3.00 (POWER9), implies IsPOWER8 + _ CacheLinePad +} + +// S390X contains the supported CPU features of the current IBM Z +// (s390x) platform. If the current platform is not IBM Z then all +// feature flags are false. +// +// S390X is padded to avoid false sharing. Further HasVX is only set +// if the OS supports vector registers in addition to the STFLE +// feature bit being set. +var S390X struct { + _ CacheLinePad + HasZARCH bool // z/Architecture mode is active [mandatory] + HasSTFLE bool // store facility list extended + HasLDISP bool // long (20-bit) displacements + HasEIMM bool // 32-bit immediates + HasDFP bool // decimal floating point + HasETF3EH bool // ETF-3 enhanced + HasMSA bool // message security assist (CPACF) + HasAES bool // KM-AES{128,192,256} functions + HasAESCBC bool // KMC-AES{128,192,256} functions + HasAESCTR bool // KMCTR-AES{128,192,256} functions + HasAESGCM bool // KMA-GCM-AES{128,192,256} functions + HasGHASH bool // KIMD-GHASH function + HasSHA1 bool // K{I,L}MD-SHA-1 functions + HasSHA256 bool // K{I,L}MD-SHA-256 functions + HasSHA512 bool // K{I,L}MD-SHA-512 functions + HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions + HasVX bool // vector facility + HasVXE bool // vector-enhancements facility 1 + _ CacheLinePad +} + +func init() { + archInit() + initOptions() + processOptions() +} + +// options contains the cpu debug options that can be used in GODEBUG. +// Options are arch dependent and are added by the arch specific initOptions functions. +// Features that are mandatory for the specific GOARCH should have the Required field set +// (e.g. SSE2 on amd64). +var options []option + +// Option names should be lower case. e.g. avx instead of AVX. +type option struct { + Name string + Feature *bool + Specified bool // whether feature value was specified in GODEBUG + Enable bool // whether feature should be enabled + Required bool // whether feature is mandatory and can not be disabled +} + +func processOptions() { + env := os.Getenv("GODEBUG") +field: + for env != "" { + field := "" + i := strings.IndexByte(env, ',') + if i < 0 { + field, env = env, "" + } else { + field, env = env[:i], env[i+1:] + } + if len(field) < 4 || field[:4] != "cpu." { + continue + } + i = strings.IndexByte(field, '=') + if i < 0 { + print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n") + continue + } + key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on" + + var enable bool + switch value { + case "on": + enable = true + case "off": + enable = false + default: + print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n") + continue field + } + + if key == "all" { + for i := range options { + options[i].Specified = true + options[i].Enable = enable || options[i].Required + } + continue field + } + + for i := range options { + if options[i].Name == key { + options[i].Specified = true + options[i].Enable = enable + continue field + } + } + + print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n") + } + + for _, o := range options { + if !o.Specified { + continue + } + + if o.Enable && !*o.Feature { + print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n") + continue + } + + if !o.Enable && o.Required { + print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n") + continue + } + + *o.Feature = o.Enable + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_aix.go b/vendor/golang.org/x/sys/cpu/cpu_aix.go new file mode 100644 index 000000000..9bf0c32eb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_aix.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix + +package cpu + +const ( + // getsystemcfg constants + _SC_IMPL = 2 + _IMPL_POWER8 = 0x10000 + _IMPL_POWER9 = 0x20000 +) + +func archInit() { + impl := getsystemcfg(_SC_IMPL) + if impl&_IMPL_POWER8 != 0 { + PPC64.IsPOWER8 = true + } + if impl&_IMPL_POWER9 != 0 { + PPC64.IsPOWER8 = true + PPC64.IsPOWER9 = true + } + + Initialized = true +} + +func getsystemcfg(label int) (n uint64) { + r0, _ := callgetsystemcfg(label) + n = uint64(r0) + return +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm.go b/vendor/golang.org/x/sys/cpu/cpu_arm.go new file mode 100644 index 000000000..301b752e9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm.go @@ -0,0 +1,73 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const cacheLineSize = 32 + +// HWCAP/HWCAP2 bits. +// These are specific to Linux. +const ( + hwcap_SWP = 1 << 0 + hwcap_HALF = 1 << 1 + hwcap_THUMB = 1 << 2 + hwcap_26BIT = 1 << 3 + hwcap_FAST_MULT = 1 << 4 + hwcap_FPA = 1 << 5 + hwcap_VFP = 1 << 6 + hwcap_EDSP = 1 << 7 + hwcap_JAVA = 1 << 8 + hwcap_IWMMXT = 1 << 9 + hwcap_CRUNCH = 1 << 10 + hwcap_THUMBEE = 1 << 11 + hwcap_NEON = 1 << 12 + hwcap_VFPv3 = 1 << 13 + hwcap_VFPv3D16 = 1 << 14 + hwcap_TLS = 1 << 15 + hwcap_VFPv4 = 1 << 16 + hwcap_IDIVA = 1 << 17 + hwcap_IDIVT = 1 << 18 + hwcap_VFPD32 = 1 << 19 + hwcap_LPAE = 1 << 20 + hwcap_EVTSTRM = 1 << 21 + + hwcap2_AES = 1 << 0 + hwcap2_PMULL = 1 << 1 + hwcap2_SHA1 = 1 << 2 + hwcap2_SHA2 = 1 << 3 + hwcap2_CRC32 = 1 << 4 +) + +func initOptions() { + options = []option{ + {Name: "pmull", Feature: &ARM.HasPMULL}, + {Name: "sha1", Feature: &ARM.HasSHA1}, + {Name: "sha2", Feature: &ARM.HasSHA2}, + {Name: "swp", Feature: &ARM.HasSWP}, + {Name: "thumb", Feature: &ARM.HasTHUMB}, + {Name: "thumbee", Feature: &ARM.HasTHUMBEE}, + {Name: "tls", Feature: &ARM.HasTLS}, + {Name: "vfp", Feature: &ARM.HasVFP}, + {Name: "vfpd32", Feature: &ARM.HasVFPD32}, + {Name: "vfpv3", Feature: &ARM.HasVFPv3}, + {Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16}, + {Name: "vfpv4", Feature: &ARM.HasVFPv4}, + {Name: "half", Feature: &ARM.HasHALF}, + {Name: "26bit", Feature: &ARM.Has26BIT}, + {Name: "fastmul", Feature: &ARM.HasFASTMUL}, + {Name: "fpa", Feature: &ARM.HasFPA}, + {Name: "edsp", Feature: &ARM.HasEDSP}, + {Name: "java", Feature: &ARM.HasJAVA}, + {Name: "iwmmxt", Feature: &ARM.HasIWMMXT}, + {Name: "crunch", Feature: &ARM.HasCRUNCH}, + {Name: "neon", Feature: &ARM.HasNEON}, + {Name: "idivt", Feature: &ARM.HasIDIVT}, + {Name: "idiva", Feature: &ARM.HasIDIVA}, + {Name: "lpae", Feature: &ARM.HasLPAE}, + {Name: "evtstrm", Feature: &ARM.HasEVTSTRM}, + {Name: "aes", Feature: &ARM.HasAES}, + {Name: "crc32", Feature: &ARM.HasCRC32}, + } + +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go new file mode 100644 index 000000000..f3eb993bf --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -0,0 +1,172 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import "runtime" + +// cacheLineSize is used to prevent false sharing of cache lines. +// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size. +// It doesn't cost much and is much more future-proof. +const cacheLineSize = 128 + +func initOptions() { + options = []option{ + {Name: "fp", Feature: &ARM64.HasFP}, + {Name: "asimd", Feature: &ARM64.HasASIMD}, + {Name: "evstrm", Feature: &ARM64.HasEVTSTRM}, + {Name: "aes", Feature: &ARM64.HasAES}, + {Name: "fphp", Feature: &ARM64.HasFPHP}, + {Name: "jscvt", Feature: &ARM64.HasJSCVT}, + {Name: "lrcpc", Feature: &ARM64.HasLRCPC}, + {Name: "pmull", Feature: &ARM64.HasPMULL}, + {Name: "sha1", Feature: &ARM64.HasSHA1}, + {Name: "sha2", Feature: &ARM64.HasSHA2}, + {Name: "sha3", Feature: &ARM64.HasSHA3}, + {Name: "sha512", Feature: &ARM64.HasSHA512}, + {Name: "sm3", Feature: &ARM64.HasSM3}, + {Name: "sm4", Feature: &ARM64.HasSM4}, + {Name: "sve", Feature: &ARM64.HasSVE}, + {Name: "crc32", Feature: &ARM64.HasCRC32}, + {Name: "atomics", Feature: &ARM64.HasATOMICS}, + {Name: "asimdhp", Feature: &ARM64.HasASIMDHP}, + {Name: "cpuid", Feature: &ARM64.HasCPUID}, + {Name: "asimrdm", Feature: &ARM64.HasASIMDRDM}, + {Name: "fcma", Feature: &ARM64.HasFCMA}, + {Name: "dcpop", Feature: &ARM64.HasDCPOP}, + {Name: "asimddp", Feature: &ARM64.HasASIMDDP}, + {Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM}, + } +} + +func archInit() { + switch runtime.GOOS { + case "freebsd": + readARM64Registers() + case "linux", "netbsd", "openbsd": + doinit() + default: + // Many platforms don't seem to allow reading these registers. + setMinimalFeatures() + } +} + +// setMinimalFeatures fakes the minimal ARM64 features expected by +// TestARM64minimalFeatures. +func setMinimalFeatures() { + ARM64.HasASIMD = true + ARM64.HasFP = true +} + +func readARM64Registers() { + Initialized = true + + parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0()) +} + +func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { + // ID_AA64ISAR0_EL1 + switch extractBits(isar0, 4, 7) { + case 1: + ARM64.HasAES = true + case 2: + ARM64.HasAES = true + ARM64.HasPMULL = true + } + + switch extractBits(isar0, 8, 11) { + case 1: + ARM64.HasSHA1 = true + } + + switch extractBits(isar0, 12, 15) { + case 1: + ARM64.HasSHA2 = true + case 2: + ARM64.HasSHA2 = true + ARM64.HasSHA512 = true + } + + switch extractBits(isar0, 16, 19) { + case 1: + ARM64.HasCRC32 = true + } + + switch extractBits(isar0, 20, 23) { + case 2: + ARM64.HasATOMICS = true + } + + switch extractBits(isar0, 28, 31) { + case 1: + ARM64.HasASIMDRDM = true + } + + switch extractBits(isar0, 32, 35) { + case 1: + ARM64.HasSHA3 = true + } + + switch extractBits(isar0, 36, 39) { + case 1: + ARM64.HasSM3 = true + } + + switch extractBits(isar0, 40, 43) { + case 1: + ARM64.HasSM4 = true + } + + switch extractBits(isar0, 44, 47) { + case 1: + ARM64.HasASIMDDP = true + } + + // ID_AA64ISAR1_EL1 + switch extractBits(isar1, 0, 3) { + case 1: + ARM64.HasDCPOP = true + } + + switch extractBits(isar1, 12, 15) { + case 1: + ARM64.HasJSCVT = true + } + + switch extractBits(isar1, 16, 19) { + case 1: + ARM64.HasFCMA = true + } + + switch extractBits(isar1, 20, 23) { + case 1: + ARM64.HasLRCPC = true + } + + // ID_AA64PFR0_EL1 + switch extractBits(pfr0, 16, 19) { + case 0: + ARM64.HasFP = true + case 1: + ARM64.HasFP = true + ARM64.HasFPHP = true + } + + switch extractBits(pfr0, 20, 23) { + case 0: + ARM64.HasASIMD = true + case 1: + ARM64.HasASIMD = true + ARM64.HasASIMDHP = true + } + + switch extractBits(pfr0, 32, 35) { + case 1: + ARM64.HasSVE = true + } +} + +func extractBits(data uint64, start, end uint) uint { + return (uint)(data>>start) & ((1 << (end - start + 1)) - 1) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_arm64.s new file mode 100644 index 000000000..fcb9a3888 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.s @@ -0,0 +1,31 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// func getisar0() uint64 +TEXT ·getisar0(SB),NOSPLIT,$0-8 + // get Instruction Set Attributes 0 into x0 + // mrs x0, ID_AA64ISAR0_EL1 = d5380600 + WORD $0xd5380600 + MOVD R0, ret+0(FP) + RET + +// func getisar1() uint64 +TEXT ·getisar1(SB),NOSPLIT,$0-8 + // get Instruction Set Attributes 1 into x0 + // mrs x0, ID_AA64ISAR1_EL1 = d5380620 + WORD $0xd5380620 + MOVD R0, ret+0(FP) + RET + +// func getpfr0() uint64 +TEXT ·getpfr0(SB),NOSPLIT,$0-8 + // get Processor Feature Register 0 into x0 + // mrs x0, ID_AA64PFR0_EL1 = d5380400 + WORD $0xd5380400 + MOVD R0, ret+0(FP) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go new file mode 100644 index 000000000..a8acd3e32 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +package cpu + +func getisar0() uint64 +func getisar1() uint64 +func getpfr0() uint64 diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go new file mode 100644 index 000000000..c8ae6ddc1 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +package cpu + +// haveAsmFunctions reports whether the other functions in this file can +// be safely called. +func haveAsmFunctions() bool { return true } + +// The following feature detection functions are defined in cpu_s390x.s. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList +func kmQuery() queryResult +func kmcQuery() queryResult +func kmctrQuery() queryResult +func kmaQuery() queryResult +func kimdQuery() queryResult +func klmdQuery() queryResult diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go new file mode 100644 index 000000000..910728fb1 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gc + +package cpu + +// cpuid is implemented in cpu_x86.s for gc compiler +// and in cpu_gccgo.c for gccgo. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler +// and in cpu_gccgo.c for gccgo. +func xgetbv() (eax, edx uint32) diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go new file mode 100644 index 000000000..7f1946780 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gccgo + +package cpu + +func getisar0() uint64 { return 0 } +func getisar1() uint64 { return 0 } +func getpfr0() uint64 { return 0 } diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go new file mode 100644 index 000000000..9526d2ce3 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gccgo + +package cpu + +// haveAsmFunctions reports whether the other functions in this file can +// be safely called. +func haveAsmFunctions() bool { return false } + +// TODO(mundaym): the following feature detection functions are currently +// stubs. See https://golang.org/cl/162887 for how to fix this. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList { panic("not implemented for gccgo") } +func kmQuery() queryResult { panic("not implemented for gccgo") } +func kmcQuery() queryResult { panic("not implemented for gccgo") } +func kmctrQuery() queryResult { panic("not implemented for gccgo") } +func kmaQuery() queryResult { panic("not implemented for gccgo") } +func kimdQuery() queryResult { panic("not implemented for gccgo") } +func klmdQuery() queryResult { panic("not implemented for gccgo") } diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c new file mode 100644 index 000000000..3f73a05dc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c @@ -0,0 +1,37 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gccgo + +#include +#include +#include + +// Need to wrap __get_cpuid_count because it's declared as static. +int +gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); +} + +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC push_options +#pragma GCC target("xsave") +#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function) + +// xgetbv reads the contents of an XCR (Extended Control Register) +// specified in the ECX register into registers EDX:EAX. +// Currently, the only supported value for XCR is 0. +void +gccgoXgetbv(uint32_t *eax, uint32_t *edx) +{ + uint64_t v = _xgetbv(0); + *eax = v & 0xffffffff; + *edx = v >> 32; +} + +#pragma clang attribute pop +#pragma GCC pop_options diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go new file mode 100644 index 000000000..99c60fe9f --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (386 || amd64 || amd64p32) && gccgo + +package cpu + +//extern gccgoGetCpuidCount +func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32) + +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { + var a, b, c, d uint32 + gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d) + return a, b, c, d +} + +//extern gccgoXgetbv +func gccgoXgetbv(eax, edx *uint32) + +func xgetbv() (eax, edx uint32) { + var a, d uint32 + gccgoXgetbv(&a, &d) + return a, d +} + +// gccgo doesn't build on Darwin, per: +// https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gcc.rb#L76 +func darwinSupportsAVX512() bool { + return false +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux.go b/vendor/golang.org/x/sys/cpu/cpu_linux.go new file mode 100644 index 000000000..743eb5435 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !386 && !amd64 && !amd64p32 && !arm64 + +package cpu + +func archInit() { + if err := readHWCAP(); err != nil { + return + } + doinit() + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go new file mode 100644 index 000000000..2057006dc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm.go @@ -0,0 +1,39 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func doinit() { + ARM.HasSWP = isSet(hwCap, hwcap_SWP) + ARM.HasHALF = isSet(hwCap, hwcap_HALF) + ARM.HasTHUMB = isSet(hwCap, hwcap_THUMB) + ARM.Has26BIT = isSet(hwCap, hwcap_26BIT) + ARM.HasFASTMUL = isSet(hwCap, hwcap_FAST_MULT) + ARM.HasFPA = isSet(hwCap, hwcap_FPA) + ARM.HasVFP = isSet(hwCap, hwcap_VFP) + ARM.HasEDSP = isSet(hwCap, hwcap_EDSP) + ARM.HasJAVA = isSet(hwCap, hwcap_JAVA) + ARM.HasIWMMXT = isSet(hwCap, hwcap_IWMMXT) + ARM.HasCRUNCH = isSet(hwCap, hwcap_CRUNCH) + ARM.HasTHUMBEE = isSet(hwCap, hwcap_THUMBEE) + ARM.HasNEON = isSet(hwCap, hwcap_NEON) + ARM.HasVFPv3 = isSet(hwCap, hwcap_VFPv3) + ARM.HasVFPv3D16 = isSet(hwCap, hwcap_VFPv3D16) + ARM.HasTLS = isSet(hwCap, hwcap_TLS) + ARM.HasVFPv4 = isSet(hwCap, hwcap_VFPv4) + ARM.HasIDIVA = isSet(hwCap, hwcap_IDIVA) + ARM.HasIDIVT = isSet(hwCap, hwcap_IDIVT) + ARM.HasVFPD32 = isSet(hwCap, hwcap_VFPD32) + ARM.HasLPAE = isSet(hwCap, hwcap_LPAE) + ARM.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM) + ARM.HasAES = isSet(hwCap2, hwcap2_AES) + ARM.HasPMULL = isSet(hwCap2, hwcap2_PMULL) + ARM.HasSHA1 = isSet(hwCap2, hwcap2_SHA1) + ARM.HasSHA2 = isSet(hwCap2, hwcap2_SHA2) + ARM.HasCRC32 = isSet(hwCap2, hwcap2_CRC32) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go new file mode 100644 index 000000000..a968b80fa --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go @@ -0,0 +1,111 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "strings" + "syscall" +) + +// HWCAP/HWCAP2 bits. These are exposed by Linux. +const ( + hwcap_FP = 1 << 0 + hwcap_ASIMD = 1 << 1 + hwcap_EVTSTRM = 1 << 2 + hwcap_AES = 1 << 3 + hwcap_PMULL = 1 << 4 + hwcap_SHA1 = 1 << 5 + hwcap_SHA2 = 1 << 6 + hwcap_CRC32 = 1 << 7 + hwcap_ATOMICS = 1 << 8 + hwcap_FPHP = 1 << 9 + hwcap_ASIMDHP = 1 << 10 + hwcap_CPUID = 1 << 11 + hwcap_ASIMDRDM = 1 << 12 + hwcap_JSCVT = 1 << 13 + hwcap_FCMA = 1 << 14 + hwcap_LRCPC = 1 << 15 + hwcap_DCPOP = 1 << 16 + hwcap_SHA3 = 1 << 17 + hwcap_SM3 = 1 << 18 + hwcap_SM4 = 1 << 19 + hwcap_ASIMDDP = 1 << 20 + hwcap_SHA512 = 1 << 21 + hwcap_SVE = 1 << 22 + hwcap_ASIMDFHM = 1 << 23 +) + +// linuxKernelCanEmulateCPUID reports whether we're running +// on Linux 4.11+. Ideally we'd like to ask the question about +// whether the current kernel contains +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77c97b4ee21290f5f083173d957843b615abbff2 +// but the version number will have to do. +func linuxKernelCanEmulateCPUID() bool { + var un syscall.Utsname + syscall.Uname(&un) + var sb strings.Builder + for _, b := range un.Release[:] { + if b == 0 { + break + } + sb.WriteByte(byte(b)) + } + major, minor, _, ok := parseRelease(sb.String()) + return ok && (major > 4 || major == 4 && minor >= 11) +} + +func doinit() { + if err := readHWCAP(); err != nil { + // We failed to read /proc/self/auxv. This can happen if the binary has + // been given extra capabilities(7) with /bin/setcap. + // + // When this happens, we have two options. If the Linux kernel is new + // enough (4.11+), we can read the arm64 registers directly which'll + // trap into the kernel and then return back to userspace. + // + // But on older kernels, such as Linux 4.4.180 as used on many Synology + // devices, calling readARM64Registers (specifically getisar0) will + // cause a SIGILL and we'll die. So for older kernels, parse /proc/cpuinfo + // instead. + // + // See golang/go#57336. + if linuxKernelCanEmulateCPUID() { + readARM64Registers() + } else { + readLinuxProcCPUInfo() + } + return + } + + // HWCAP feature bits + ARM64.HasFP = isSet(hwCap, hwcap_FP) + ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD) + ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM) + ARM64.HasAES = isSet(hwCap, hwcap_AES) + ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32) + ARM64.HasATOMICS = isSet(hwCap, hwcap_ATOMICS) + ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP) + ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP) + ARM64.HasCPUID = isSet(hwCap, hwcap_CPUID) + ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM) + ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT) + ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA) + ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC) + ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP) + ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3) + ARM64.HasSM3 = isSet(hwCap, hwcap_SM3) + ARM64.HasSM4 = isSet(hwCap, hwcap_SM4) + ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP) + ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512) + ARM64.HasSVE = isSet(hwCap, hwcap_SVE) + ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go new file mode 100644 index 000000000..4686c1d54 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (mips64 || mips64le) + +package cpu + +// HWCAP bits. These are exposed by the Linux kernel 5.4. +const ( + // CPU features + hwcap_MIPS_MSA = 1 << 1 +) + +func doinit() { + // HWCAP feature bits + MIPS64X.HasMSA = isSet(hwCap, hwcap_MIPS_MSA) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go new file mode 100644 index 000000000..cd63e7335 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x + +package cpu + +func doinit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go new file mode 100644 index 000000000..197188e67 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (ppc64 || ppc64le) + +package cpu + +// HWCAP/HWCAP2 bits. These are exposed by the kernel. +const ( + // ISA Level + _PPC_FEATURE2_ARCH_2_07 = 0x80000000 + _PPC_FEATURE2_ARCH_3_00 = 0x00800000 + + // CPU features + _PPC_FEATURE2_DARN = 0x00200000 + _PPC_FEATURE2_SCV = 0x00100000 +) + +func doinit() { + // HWCAP2 feature bits + PPC64.IsPOWER8 = isSet(hwCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.IsPOWER9 = isSet(hwCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(hwCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(hwCap2, _PPC_FEATURE2_SCV) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go new file mode 100644 index 000000000..1517ac61d --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_s390x.go @@ -0,0 +1,40 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const ( + // bit mask values from /usr/include/bits/hwcap.h + hwcap_ZARCH = 2 + hwcap_STFLE = 4 + hwcap_MSA = 8 + hwcap_LDISP = 16 + hwcap_EIMM = 32 + hwcap_DFP = 64 + hwcap_ETF3EH = 256 + hwcap_VX = 2048 + hwcap_VXE = 8192 +) + +func initS390Xbase() { + // test HWCAP bit vector + has := func(featureMask uint) bool { + return hwCap&featureMask == featureMask + } + + // mandatory + S390X.HasZARCH = has(hwcap_ZARCH) + + // optional + S390X.HasSTFLE = has(hwcap_STFLE) + S390X.HasLDISP = has(hwcap_LDISP) + S390X.HasEIMM = has(hwcap_EIMM) + S390X.HasETF3EH = has(hwcap_ETF3EH) + S390X.HasDFP = has(hwcap_DFP) + S390X.HasMSA = has(hwcap_MSA) + S390X.HasVX = has(hwcap_VX) + if S390X.HasVX { + S390X.HasVXE = has(hwcap_VXE) + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_loong64.go new file mode 100644 index 000000000..558635850 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package cpu + +const cacheLineSize = 64 + +func initOptions() { +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go new file mode 100644 index 000000000..fedb00cc4 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build mips64 || mips64le + +package cpu + +const cacheLineSize = 32 + +func initOptions() { + options = []option{ + {Name: "msa", Feature: &MIPS64X.HasMSA}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_mipsx.go b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go new file mode 100644 index 000000000..ffb4ec7eb --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go @@ -0,0 +1,11 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build mips || mipsle + +package cpu + +const cacheLineSize = 32 + +func initOptions() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go new file mode 100644 index 000000000..ebfb3fc8e --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go @@ -0,0 +1,173 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// Minimal copy of functionality from x/sys/unix so the cpu package can call +// sysctl without depending on x/sys/unix. + +const ( + _CTL_QUERY = -2 + + _SYSCTL_VERS_1 = 0x1000000 +) + +var _zero uintptr + +func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, errno := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(_p0), + uintptr(len(mib)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen)) + if errno != 0 { + return errno + } + return nil +} + +type sysctlNode struct { + Flags uint32 + Num int32 + Name [32]int8 + Ver uint32 + __rsvd uint32 + Un [16]byte + _sysctl_size [8]byte + _sysctl_func [8]byte + _sysctl_parent [8]byte + _sysctl_desc [8]byte +} + +func sysctlNodes(mib []int32) ([]sysctlNode, error) { + var olen uintptr + + // Get a list of all sysctl nodes below the given MIB by performing + // a sysctl for the given MIB with CTL_QUERY appended. + mib = append(mib, _CTL_QUERY) + qnode := sysctlNode{Flags: _SYSCTL_VERS_1} + qp := (*byte)(unsafe.Pointer(&qnode)) + sz := unsafe.Sizeof(qnode) + if err := sysctl(mib, nil, &olen, qp, sz); err != nil { + return nil, err + } + + // Now that we know the size, get the actual nodes. + nodes := make([]sysctlNode, olen/sz) + np := (*byte)(unsafe.Pointer(&nodes[0])) + if err := sysctl(mib, np, &olen, qp, sz); err != nil { + return nil, err + } + + return nodes, nil +} + +func nametomib(name string) ([]int32, error) { + // Split name into components. + var parts []string + last := 0 + for i := 0; i < len(name); i++ { + if name[i] == '.' { + parts = append(parts, name[last:i]) + last = i + 1 + } + } + parts = append(parts, name[last:]) + + mib := []int32{} + // Discover the nodes and construct the MIB OID. + for partno, part := range parts { + nodes, err := sysctlNodes(mib) + if err != nil { + return nil, err + } + for _, node := range nodes { + n := make([]byte, 0) + for i := range node.Name { + if node.Name[i] != 0 { + n = append(n, byte(node.Name[i])) + } + } + if string(n) == part { + mib = append(mib, int32(node.Num)) + break + } + } + if len(mib) != partno+1 { + return nil, err + } + } + + return mib, nil +} + +// aarch64SysctlCPUID is struct aarch64_sysctl_cpu_id from NetBSD's +type aarch64SysctlCPUID struct { + midr uint64 /* Main ID Register */ + revidr uint64 /* Revision ID Register */ + mpidr uint64 /* Multiprocessor Affinity Register */ + aa64dfr0 uint64 /* A64 Debug Feature Register 0 */ + aa64dfr1 uint64 /* A64 Debug Feature Register 1 */ + aa64isar0 uint64 /* A64 Instruction Set Attribute Register 0 */ + aa64isar1 uint64 /* A64 Instruction Set Attribute Register 1 */ + aa64mmfr0 uint64 /* A64 Memory Model Feature Register 0 */ + aa64mmfr1 uint64 /* A64 Memory Model Feature Register 1 */ + aa64mmfr2 uint64 /* A64 Memory Model Feature Register 2 */ + aa64pfr0 uint64 /* A64 Processor Feature Register 0 */ + aa64pfr1 uint64 /* A64 Processor Feature Register 1 */ + aa64zfr0 uint64 /* A64 SVE Feature ID Register 0 */ + mvfr0 uint32 /* Media and VFP Feature Register 0 */ + mvfr1 uint32 /* Media and VFP Feature Register 1 */ + mvfr2 uint32 /* Media and VFP Feature Register 2 */ + pad uint32 + clidr uint64 /* Cache Level ID Register */ + ctr uint64 /* Cache Type Register */ +} + +func sysctlCPUID(name string) (*aarch64SysctlCPUID, error) { + mib, err := nametomib(name) + if err != nil { + return nil, err + } + + out := aarch64SysctlCPUID{} + n := unsafe.Sizeof(out) + _, _, errno := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(len(mib)), + uintptr(unsafe.Pointer(&out)), + uintptr(unsafe.Pointer(&n)), + uintptr(0), + uintptr(0)) + if errno != 0 { + return nil, errno + } + return &out, nil +} + +func doinit() { + cpuid, err := sysctlCPUID("machdep.cpu0.cpu_id") + if err != nil { + setMinimalFeatures() + return + } + parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0) + + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go new file mode 100644 index 000000000..85b64d5cc --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// Minimal copy of functionality from x/sys/unix so the cpu package can call +// sysctl without depending on x/sys/unix. + +const ( + // From OpenBSD's sys/sysctl.h. + _CTL_MACHDEP = 7 + + // From OpenBSD's machine/cpu.h. + _CPU_ID_AA64ISAR0 = 2 + _CPU_ID_AA64ISAR1 = 3 +) + +// Implemented in the runtime package (runtime/sys_openbsd3.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 + +func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + _, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if errno != 0 { + return errno + } + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_sysctl sysctl "libc.so" + +func sysctlUint64(mib []uint32) (uint64, bool) { + var out uint64 + nout := unsafe.Sizeof(out) + if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil { + return 0, false + } + return out, true +} + +func doinit() { + setMinimalFeatures() + + // Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl. + isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0}) + if !ok { + return + } + isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1}) + if !ok { + return + } + parseARM64SystemRegisters(isar0, isar1, 0) + + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s new file mode 100644 index 000000000..054ba05d6 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) + +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go new file mode 100644 index 000000000..e9ecf2a45 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && arm + +package cpu + +func archInit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go new file mode 100644 index 000000000..5341e7f88 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && !netbsd && !openbsd && arm64 + +package cpu + +func doinit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go new file mode 100644 index 000000000..5f8f2419a --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && (mips64 || mips64le) + +package cpu + +func archInit() { + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go new file mode 100644 index 000000000..89608fba2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !aix && !linux && (ppc64 || ppc64le) + +package cpu + +func archInit() { + PPC64.IsPOWER8 = true + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go new file mode 100644 index 000000000..5ab87808f --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && riscv64 + +package cpu + +func archInit() { + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go new file mode 100644 index 000000000..c14f12b14 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +package cpu + +const cacheLineSize = 128 + +func initOptions() { + options = []option{ + {Name: "darn", Feature: &PPC64.HasDARN}, + {Name: "scv", Feature: &PPC64.HasSCV}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go new file mode 100644 index 000000000..7f0c79c00 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build riscv64 + +package cpu + +const cacheLineSize = 64 + +func initOptions() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_s390x.go new file mode 100644 index 000000000..5881b8833 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_s390x.go @@ -0,0 +1,172 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const cacheLineSize = 256 + +func initOptions() { + options = []option{ + {Name: "zarch", Feature: &S390X.HasZARCH, Required: true}, + {Name: "stfle", Feature: &S390X.HasSTFLE, Required: true}, + {Name: "ldisp", Feature: &S390X.HasLDISP, Required: true}, + {Name: "eimm", Feature: &S390X.HasEIMM, Required: true}, + {Name: "dfp", Feature: &S390X.HasDFP}, + {Name: "etf3eh", Feature: &S390X.HasETF3EH}, + {Name: "msa", Feature: &S390X.HasMSA}, + {Name: "aes", Feature: &S390X.HasAES}, + {Name: "aescbc", Feature: &S390X.HasAESCBC}, + {Name: "aesctr", Feature: &S390X.HasAESCTR}, + {Name: "aesgcm", Feature: &S390X.HasAESGCM}, + {Name: "ghash", Feature: &S390X.HasGHASH}, + {Name: "sha1", Feature: &S390X.HasSHA1}, + {Name: "sha256", Feature: &S390X.HasSHA256}, + {Name: "sha3", Feature: &S390X.HasSHA3}, + {Name: "sha512", Feature: &S390X.HasSHA512}, + {Name: "vx", Feature: &S390X.HasVX}, + {Name: "vxe", Feature: &S390X.HasVXE}, + } +} + +// bitIsSet reports whether the bit at index is set. The bit index +// is in big endian order, so bit index 0 is the leftmost bit. +func bitIsSet(bits []uint64, index uint) bool { + return bits[index/64]&((1<<63)>>(index%64)) != 0 +} + +// facility is a bit index for the named facility. +type facility uint8 + +const ( + // mandatory facilities + zarch facility = 1 // z architecture mode is active + stflef facility = 7 // store-facility-list-extended + ldisp facility = 18 // long-displacement + eimm facility = 21 // extended-immediate + + // miscellaneous facilities + dfp facility = 42 // decimal-floating-point + etf3eh facility = 30 // extended-translation 3 enhancement + + // cryptography facilities + msa facility = 17 // message-security-assist + msa3 facility = 76 // message-security-assist extension 3 + msa4 facility = 77 // message-security-assist extension 4 + msa5 facility = 57 // message-security-assist extension 5 + msa8 facility = 146 // message-security-assist extension 8 + msa9 facility = 155 // message-security-assist extension 9 + + // vector facilities + vx facility = 129 // vector facility + vxe facility = 135 // vector-enhancements 1 + vxe2 facility = 148 // vector-enhancements 2 +) + +// facilityList contains the result of an STFLE call. +// Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type facilityList struct { + bits [4]uint64 +} + +// Has reports whether the given facilities are present. +func (s *facilityList) Has(fs ...facility) bool { + if len(fs) == 0 { + panic("no facility bits provided") + } + for _, f := range fs { + if !bitIsSet(s.bits[:], uint(f)) { + return false + } + } + return true +} + +// function is the code for the named cryptographic function. +type function uint8 + +const ( + // KM{,A,C,CTR} function codes + aes128 function = 18 // AES-128 + aes192 function = 19 // AES-192 + aes256 function = 20 // AES-256 + + // K{I,L}MD function codes + sha1 function = 1 // SHA-1 + sha256 function = 2 // SHA-256 + sha512 function = 3 // SHA-512 + sha3_224 function = 32 // SHA3-224 + sha3_256 function = 33 // SHA3-256 + sha3_384 function = 34 // SHA3-384 + sha3_512 function = 35 // SHA3-512 + shake128 function = 36 // SHAKE-128 + shake256 function = 37 // SHAKE-256 + + // KLMD function codes + ghash function = 65 // GHASH +) + +// queryResult contains the result of a Query function +// call. Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type queryResult struct { + bits [2]uint64 +} + +// Has reports whether the given functions are present. +func (q *queryResult) Has(fns ...function) bool { + if len(fns) == 0 { + panic("no function codes provided") + } + for _, f := range fns { + if !bitIsSet(q.bits[:], uint(f)) { + return false + } + } + return true +} + +func doinit() { + initS390Xbase() + + // We need implementations of stfle, km and so on + // to detect cryptographic features. + if !haveAsmFunctions() { + return + } + + // optional cryptographic functions + if S390X.HasMSA { + aes := []function{aes128, aes192, aes256} + + // cipher message + km, kmc := kmQuery(), kmcQuery() + S390X.HasAES = km.Has(aes...) + S390X.HasAESCBC = kmc.Has(aes...) + if S390X.HasSTFLE { + facilities := stfle() + if facilities.Has(msa4) { + kmctr := kmctrQuery() + S390X.HasAESCTR = kmctr.Has(aes...) + } + if facilities.Has(msa8) { + kma := kmaQuery() + S390X.HasAESGCM = kma.Has(aes...) + } + } + + // compute message digest + kimd := kimdQuery() // intermediate (no padding) + klmd := klmdQuery() // last (padding) + S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) + S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) + S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) + S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist + sha3 := []function{ + sha3_224, sha3_256, sha3_384, sha3_512, + shake128, shake256, + } + S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...) + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_s390x.s b/vendor/golang.org/x/sys/cpu/cpu_s390x.s new file mode 100644 index 000000000..1fb4b7013 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_s390x.s @@ -0,0 +1,57 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc + +#include "textflag.h" + +// func stfle() facilityList +TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32 + MOVD $ret+0(FP), R1 + MOVD $3, R0 // last doubleword index to store + XC $32, (R1), (R1) // clear 4 doublewords (32 bytes) + WORD $0xb2b01000 // store facility list extended (STFLE) + RET + +// func kmQuery() queryResult +TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KM-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92E0024 // cipher message (KM) + RET + +// func kmcQuery() queryResult +TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMC-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92F0024 // cipher message with chaining (KMC) + RET + +// func kmctrQuery() queryResult +TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMCTR-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB92D4024 // cipher message with counter (KMCTR) + RET + +// func kmaQuery() queryResult +TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KMA-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xb9296024 // cipher message with authentication (KMA) + RET + +// func kimdQuery() queryResult +TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KIMD-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB93E0024 // compute intermediate message digest (KIMD) + RET + +// func klmdQuery() queryResult +TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16 + MOVD $0, R0 // set function code to 0 (KLMD-Query) + MOVD $ret+0(FP), R1 // address of 16-byte return value + WORD $0xB93F0024 // compute last message digest (KLMD) + RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_wasm.go b/vendor/golang.org/x/sys/cpu/cpu_wasm.go new file mode 100644 index 000000000..384787ea3 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_wasm.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build wasm + +package cpu + +// We're compiling the cpu package for an unknown (software-abstracted) CPU. +// Make CacheLinePad an empty struct and hope that the usual struct alignment +// rules are good enough. + +const cacheLineSize = 0 + +func initOptions() {} + +func archInit() {} diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.go b/vendor/golang.org/x/sys/cpu/cpu_x86.go new file mode 100644 index 000000000..c29f5e4c5 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -0,0 +1,151 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64 || amd64p32 + +package cpu + +import "runtime" + +const cacheLineSize = 64 + +func initOptions() { + options = []option{ + {Name: "adx", Feature: &X86.HasADX}, + {Name: "aes", Feature: &X86.HasAES}, + {Name: "avx", Feature: &X86.HasAVX}, + {Name: "avx2", Feature: &X86.HasAVX2}, + {Name: "avx512", Feature: &X86.HasAVX512}, + {Name: "avx512f", Feature: &X86.HasAVX512F}, + {Name: "avx512cd", Feature: &X86.HasAVX512CD}, + {Name: "avx512er", Feature: &X86.HasAVX512ER}, + {Name: "avx512pf", Feature: &X86.HasAVX512PF}, + {Name: "avx512vl", Feature: &X86.HasAVX512VL}, + {Name: "avx512bw", Feature: &X86.HasAVX512BW}, + {Name: "avx512dq", Feature: &X86.HasAVX512DQ}, + {Name: "avx512ifma", Feature: &X86.HasAVX512IFMA}, + {Name: "avx512vbmi", Feature: &X86.HasAVX512VBMI}, + {Name: "avx512vnniw", Feature: &X86.HasAVX5124VNNIW}, + {Name: "avx5124fmaps", Feature: &X86.HasAVX5124FMAPS}, + {Name: "avx512vpopcntdq", Feature: &X86.HasAVX512VPOPCNTDQ}, + {Name: "avx512vpclmulqdq", Feature: &X86.HasAVX512VPCLMULQDQ}, + {Name: "avx512vnni", Feature: &X86.HasAVX512VNNI}, + {Name: "avx512gfni", Feature: &X86.HasAVX512GFNI}, + {Name: "avx512vaes", Feature: &X86.HasAVX512VAES}, + {Name: "avx512vbmi2", Feature: &X86.HasAVX512VBMI2}, + {Name: "avx512bitalg", Feature: &X86.HasAVX512BITALG}, + {Name: "avx512bf16", Feature: &X86.HasAVX512BF16}, + {Name: "amxtile", Feature: &X86.HasAMXTile}, + {Name: "amxint8", Feature: &X86.HasAMXInt8}, + {Name: "amxbf16", Feature: &X86.HasAMXBF16}, + {Name: "bmi1", Feature: &X86.HasBMI1}, + {Name: "bmi2", Feature: &X86.HasBMI2}, + {Name: "cx16", Feature: &X86.HasCX16}, + {Name: "erms", Feature: &X86.HasERMS}, + {Name: "fma", Feature: &X86.HasFMA}, + {Name: "osxsave", Feature: &X86.HasOSXSAVE}, + {Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ}, + {Name: "popcnt", Feature: &X86.HasPOPCNT}, + {Name: "rdrand", Feature: &X86.HasRDRAND}, + {Name: "rdseed", Feature: &X86.HasRDSEED}, + {Name: "sse3", Feature: &X86.HasSSE3}, + {Name: "sse41", Feature: &X86.HasSSE41}, + {Name: "sse42", Feature: &X86.HasSSE42}, + {Name: "ssse3", Feature: &X86.HasSSSE3}, + + // These capabilities should always be enabled on amd64: + {Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"}, + } +} + +func archInit() { + + Initialized = true + + maxID, _, _, _ := cpuid(0, 0) + + if maxID < 1 { + return + } + + _, _, ecx1, edx1 := cpuid(1, 0) + X86.HasSSE2 = isSet(26, edx1) + + X86.HasSSE3 = isSet(0, ecx1) + X86.HasPCLMULQDQ = isSet(1, ecx1) + X86.HasSSSE3 = isSet(9, ecx1) + X86.HasFMA = isSet(12, ecx1) + X86.HasCX16 = isSet(13, ecx1) + X86.HasSSE41 = isSet(19, ecx1) + X86.HasSSE42 = isSet(20, ecx1) + X86.HasPOPCNT = isSet(23, ecx1) + X86.HasAES = isSet(25, ecx1) + X86.HasOSXSAVE = isSet(27, ecx1) + X86.HasRDRAND = isSet(30, ecx1) + + var osSupportsAVX, osSupportsAVX512 bool + // For XGETBV, OSXSAVE bit is required and sufficient. + if X86.HasOSXSAVE { + eax, _ := xgetbv() + // Check if XMM and YMM registers have OS support. + osSupportsAVX = isSet(1, eax) && isSet(2, eax) + + if runtime.GOOS == "darwin" { + // Darwin doesn't save/restore AVX-512 mask registers correctly across signal handlers. + // Since users can't rely on mask register contents, let's not advertise AVX-512 support. + // See issue 49233. + osSupportsAVX512 = false + } else { + // Check if OPMASK and ZMM registers have OS support. + osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax) + } + } + + X86.HasAVX = isSet(28, ecx1) && osSupportsAVX + + if maxID < 7 { + return + } + + _, ebx7, ecx7, edx7 := cpuid(7, 0) + X86.HasBMI1 = isSet(3, ebx7) + X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX + X86.HasBMI2 = isSet(8, ebx7) + X86.HasERMS = isSet(9, ebx7) + X86.HasRDSEED = isSet(18, ebx7) + X86.HasADX = isSet(19, ebx7) + + X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension + if X86.HasAVX512 { + X86.HasAVX512F = true + X86.HasAVX512CD = isSet(28, ebx7) + X86.HasAVX512ER = isSet(27, ebx7) + X86.HasAVX512PF = isSet(26, ebx7) + X86.HasAVX512VL = isSet(31, ebx7) + X86.HasAVX512BW = isSet(30, ebx7) + X86.HasAVX512DQ = isSet(17, ebx7) + X86.HasAVX512IFMA = isSet(21, ebx7) + X86.HasAVX512VBMI = isSet(1, ecx7) + X86.HasAVX5124VNNIW = isSet(2, edx7) + X86.HasAVX5124FMAPS = isSet(3, edx7) + X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7) + X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7) + X86.HasAVX512VNNI = isSet(11, ecx7) + X86.HasAVX512GFNI = isSet(8, ecx7) + X86.HasAVX512VAES = isSet(9, ecx7) + X86.HasAVX512VBMI2 = isSet(6, ecx7) + X86.HasAVX512BITALG = isSet(12, ecx7) + + eax71, _, _, _ := cpuid(7, 1) + X86.HasAVX512BF16 = isSet(5, eax71) + } + + X86.HasAMXTile = isSet(24, edx7) + X86.HasAMXInt8 = isSet(25, edx7) + X86.HasAMXBF16 = isSet(22, edx7) +} + +func isSet(bitpos uint, value uint32) bool { + return value&(1<> 63)) +) + +// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2 +// These are initialized in cpu_$GOARCH.go +// and should not be changed after they are initialized. +var hwCap uint +var hwCap2 uint + +func readHWCAP() error { + // For Go 1.21+, get auxv from the Go runtime. + if a := getAuxv(); len(a) > 0 { + for len(a) >= 2 { + tag, val := a[0], uint(a[1]) + a = a[2:] + switch tag { + case _AT_HWCAP: + hwCap = val + case _AT_HWCAP2: + hwCap2 = val + } + } + return nil + } + + buf, err := os.ReadFile(procAuxv) + if err != nil { + // e.g. on android /proc/self/auxv is not accessible, so silently + // ignore the error and leave Initialized = false. On some + // architectures (e.g. arm64) doinit() implements a fallback + // readout and will set Initialized = true again. + return err + } + bo := hostByteOrder() + for len(buf) >= 2*(uintSize/8) { + var tag, val uint + switch uintSize { + case 32: + tag = uint(bo.Uint32(buf[0:])) + val = uint(bo.Uint32(buf[4:])) + buf = buf[8:] + case 64: + tag = uint(bo.Uint64(buf[0:])) + val = uint(bo.Uint64(buf[8:])) + buf = buf[16:] + } + switch tag { + case _AT_HWCAP: + hwCap = val + case _AT_HWCAP2: + hwCap2 = val + } + } + return nil +} diff --git a/vendor/golang.org/x/sys/cpu/parse.go b/vendor/golang.org/x/sys/cpu/parse.go new file mode 100644 index 000000000..762b63d68 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/parse.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import "strconv" + +// parseRelease parses a dot-separated version number. It follows the semver +// syntax, but allows the minor and patch versions to be elided. +// +// This is a copy of the Go runtime's parseRelease from +// https://golang.org/cl/209597. +func parseRelease(rel string) (major, minor, patch int, ok bool) { + // Strip anything after a dash or plus. + for i := 0; i < len(rel); i++ { + if rel[i] == '-' || rel[i] == '+' { + rel = rel[:i] + break + } + } + + next := func() (int, bool) { + for i := 0; i < len(rel); i++ { + if rel[i] == '.' { + ver, err := strconv.Atoi(rel[:i]) + rel = rel[i+1:] + return ver, err == nil + } + } + ver, err := strconv.Atoi(rel) + rel = "" + return ver, err == nil + } + if major, ok = next(); !ok || rel == "" { + return + } + if minor, ok = next(); !ok || rel == "" { + return + } + patch, ok = next() + return +} diff --git a/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go new file mode 100644 index 000000000..4cd64c704 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go @@ -0,0 +1,53 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && arm64 + +package cpu + +import ( + "errors" + "io" + "os" + "strings" +) + +func readLinuxProcCPUInfo() error { + f, err := os.Open("/proc/cpuinfo") + if err != nil { + return err + } + defer f.Close() + + var buf [1 << 10]byte // enough for first CPU + n, err := io.ReadFull(f, buf[:]) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + in := string(buf[:n]) + const features = "\nFeatures : " + i := strings.Index(in, features) + if i == -1 { + return errors.New("no CPU features found") + } + in = in[i+len(features):] + if i := strings.Index(in, "\n"); i != -1 { + in = in[:i] + } + m := map[string]*bool{} + + initOptions() // need it early here; it's harmless to call twice + for _, o := range options { + m[o.Name] = o.Feature + } + // The EVTSTRM field has alias "evstrm" in Go, but Linux calls it "evtstrm". + m["evtstrm"] = &ARM64.HasEVTSTRM + + for _, f := range strings.Fields(in) { + if p, ok := m[f]; ok { + *p = true + } + } + return nil +} diff --git a/vendor/golang.org/x/sys/cpu/runtime_auxv.go b/vendor/golang.org/x/sys/cpu/runtime_auxv.go new file mode 100644 index 000000000..5f92ac9a2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/runtime_auxv.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +// getAuxvFn is non-nil on Go 1.21+ (via runtime_auxv_go121.go init) +// on platforms that use auxv. +var getAuxvFn func() []uintptr + +func getAuxv() []uintptr { + if getAuxvFn == nil { + return nil + } + return getAuxvFn() +} diff --git a/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go new file mode 100644 index 000000000..4c9788ea8 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go @@ -0,0 +1,18 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.21 + +package cpu + +import ( + _ "unsafe" // for linkname +) + +//go:linkname runtime_getAuxv runtime.getAuxv +func runtime_getAuxv() []uintptr + +func init() { + getAuxvFn = runtime_getAuxv +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go new file mode 100644 index 000000000..1b9ccb091 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Recreate a getsystemcfg syscall handler instead of +// using the one provided by x/sys/unix to avoid having +// the dependency between them. (See golang.org/issue/32102) +// Moreover, this file will be used during the building of +// gccgo's libgo and thus must not used a CGo method. + +//go:build aix && gccgo + +package cpu + +import ( + "syscall" +) + +//extern getsystemcfg +func gccgoGetsystemcfg(label uint32) (r uint64) + +func callgetsystemcfg(label int) (r1 uintptr, e1 syscall.Errno) { + r1 = uintptr(gccgoGetsystemcfg(uint32(label))) + e1 = syscall.GetErrno() + return +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go new file mode 100644 index 000000000..e8b6cdbe9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Minimal copy of x/sys/unix so the cpu package can make a +// system call on AIX without depending on x/sys/unix. +// (See golang.org/issue/32102) + +//go:build aix && ppc64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_getsystemcfg getsystemcfg "libc.a/shr_64.o" + +//go:linkname libc_getsystemcfg libc_getsystemcfg + +type syscallFunc uintptr + +var libc_getsystemcfg syscallFunc + +type errno = syscall.Errno + +// Implemented in runtime/syscall_aix.go. +func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) +func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) + +func callgetsystemcfg(label int) (r1 uintptr, e1 errno) { + r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_getsystemcfg)), 1, uintptr(label), 0, 0, 0, 0, 0) + return +} diff --git a/vendor/golang.org/x/sys/execabs/execabs.go b/vendor/golang.org/x/sys/execabs/execabs.go deleted file mode 100644 index 3bf40fdfe..000000000 --- a/vendor/golang.org/x/sys/execabs/execabs.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package execabs is a drop-in replacement for os/exec -// that requires PATH lookups to find absolute paths. -// That is, execabs.Command("cmd") runs the same PATH lookup -// as exec.Command("cmd"), but if the result is a path -// which is relative, the Run and Start methods will report -// an error instead of running the executable. -// -// See https://blog.golang.org/path-security for more information -// about when it may be necessary or appropriate to use this package. -package execabs - -import ( - "context" - "fmt" - "os/exec" - "path/filepath" - "reflect" - "unsafe" -) - -// ErrNotFound is the error resulting if a path search failed to find an executable file. -// It is an alias for exec.ErrNotFound. -var ErrNotFound = exec.ErrNotFound - -// Cmd represents an external command being prepared or run. -// It is an alias for exec.Cmd. -type Cmd = exec.Cmd - -// Error is returned by LookPath when it fails to classify a file as an executable. -// It is an alias for exec.Error. -type Error = exec.Error - -// An ExitError reports an unsuccessful exit by a command. -// It is an alias for exec.ExitError. -type ExitError = exec.ExitError - -func relError(file, path string) error { - return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path) -} - -// LookPath searches for an executable named file in the directories -// named by the PATH environment variable. If file contains a slash, -// it is tried directly and the PATH is not consulted. The result will be -// an absolute path. -// -// LookPath differs from exec.LookPath in its handling of PATH lookups, -// which are used for file names without slashes. If exec.LookPath's -// PATH lookup would have returned an executable from the current directory, -// LookPath instead returns an error. -func LookPath(file string) (string, error) { - path, err := exec.LookPath(file) - if err != nil && !isGo119ErrDot(err) { - return "", err - } - if filepath.Base(file) == file && !filepath.IsAbs(path) { - return "", relError(file, path) - } - return path, nil -} - -func fixCmd(name string, cmd *exec.Cmd) { - if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) { - // exec.Command was called with a bare binary name and - // exec.LookPath returned a path which is not absolute. - // Set cmd.lookPathErr and clear cmd.Path so that it - // cannot be run. - lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer())) - if *lookPathErr == nil { - *lookPathErr = relError(name, cmd.Path) - } - cmd.Path = "" - } -} - -// CommandContext is like Command but includes a context. -// -// The provided context is used to kill the process (by calling os.Process.Kill) -// if the context becomes done before the command completes on its own. -func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd { - cmd := exec.CommandContext(ctx, name, arg...) - fixCmd(name, cmd) - return cmd - -} - -// Command returns the Cmd struct to execute the named program with the given arguments. -// See exec.Command for most details. -// -// Command differs from exec.Command in its handling of PATH lookups, -// which are used when the program name contains no slashes. -// If exec.Command would have returned an exec.Cmd configured to run an -// executable from the current directory, Command instead -// returns an exec.Cmd that will return an error from Start or Run. -func Command(name string, arg ...string) *exec.Cmd { - cmd := exec.Command(name, arg...) - fixCmd(name, cmd) - return cmd -} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go deleted file mode 100644 index 2000064a8..000000000 --- a/vendor/golang.org/x/sys/execabs/execabs_go118.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.19 -// +build !go1.19 - -package execabs - -import "os/exec" - -func isGo119ErrDot(err error) bool { - return false -} - -func isGo119ErrFieldSet(cmd *exec.Cmd) bool { - return false -} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go deleted file mode 100644 index f364b3418..000000000 --- a/vendor/golang.org/x/sys/execabs/execabs_go119.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.19 -// +build go1.19 - -package execabs - -import ( - "errors" - "os/exec" -) - -func isGo119ErrDot(err error) bool { - return errors.Is(err, exec.ErrDot) -} - -func isGo119ErrFieldSet(cmd *exec.Cmd) bool { - return cmd.Err != nil -} diff --git a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go index c9b69937a..73687de74 100644 --- a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go +++ b/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.5 -// +build go1.5 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/pwd_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_plan9.go index 98bf56b73..fb9458218 100644 --- a/vendor/golang.org/x/sys/plan9/pwd_plan9.go +++ b/vendor/golang.org/x/sys/plan9/pwd_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.5 -// +build !go1.5 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/race.go b/vendor/golang.org/x/sys/plan9/race.go index 62377d2ff..c02d9ed33 100644 --- a/vendor/golang.org/x/sys/plan9/race.go +++ b/vendor/golang.org/x/sys/plan9/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 && race -// +build plan9,race package plan9 diff --git a/vendor/golang.org/x/sys/plan9/race0.go b/vendor/golang.org/x/sys/plan9/race0.go index f8da30876..7b15e15f6 100644 --- a/vendor/golang.org/x/sys/plan9/race0.go +++ b/vendor/golang.org/x/sys/plan9/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 && !race -// +build plan9,!race package plan9 diff --git a/vendor/golang.org/x/sys/plan9/str.go b/vendor/golang.org/x/sys/plan9/str.go index 55fa8d025..ba3e8ff8a 100644 --- a/vendor/golang.org/x/sys/plan9/str.go +++ b/vendor/golang.org/x/sys/plan9/str.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/syscall.go b/vendor/golang.org/x/sys/plan9/syscall.go index 67e5b0115..d631fd664 100644 --- a/vendor/golang.org/x/sys/plan9/syscall.go +++ b/vendor/golang.org/x/sys/plan9/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 // Package plan9 contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go index 3f40b9bd7..f780d5c80 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && 386 -// +build plan9,386 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go index 0e6a96aa4..7de61065f 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && amd64 -// +build plan9,amd64 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go index 244c501b7..ea85780f0 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && arm -// +build plan9,arm package plan9 diff --git a/vendor/golang.org/x/sys/unix/aliases.go b/vendor/golang.org/x/sys/unix/aliases.go index abc89c104..b0e419857 100644 --- a/vendor/golang.org/x/sys/unix/aliases.go +++ b/vendor/golang.org/x/sys/unix/aliases.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9 -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos -// +build go1.9 +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos package unix diff --git a/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s b/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s index db9171c2e..269e173ca 100644 --- a/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s +++ b/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_386.s b/vendor/golang.org/x/sys/unix/asm_bsd_386.s index e0fcd9b3d..a4fcef0e0 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_386.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_386.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd || netbsd || openbsd) && gc -// +build freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s b/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s index 2b99c349a..1e63615c5 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc -// +build darwin dragonfly freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_arm.s b/vendor/golang.org/x/sys/unix/asm_bsd_arm.s index d702d4adc..6496c3100 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_arm.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_arm.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd || netbsd || openbsd) && gc -// +build freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s b/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s index fe36a7391..4fd1f54da 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s b/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s index e5b9a8489..42f7eb9e4 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s index d560019ea..f8902667e 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_386.s b/vendor/golang.org/x/sys/unix/asm_linux_386.s index 8fd101d07..3b4734870 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_386.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s b/vendor/golang.org/x/sys/unix/asm_linux_amd64.s index 7ed38e43c..67e29f317 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm.s b/vendor/golang.org/x/sys/unix/asm_linux_arm.s index 8ef1d5140..d6ae269ce 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_arm.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_arm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s b/vendor/golang.org/x/sys/unix/asm_linux_arm64.s index 98ae02760..01e5e253c 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_arm64.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && arm64 && gc -// +build linux -// +build arm64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s index 565357288..2abf12f6e 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && loong64 && gc -// +build linux -// +build loong64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s b/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s index 21231d2ce..f84bae712 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) && gc -// +build linux -// +build mips64 mips64le -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s b/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s index 6783b26c6..f08f62807 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) && gc -// +build linux -// +build mips mipsle -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s index 19d498934..bdfc024d2 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) && gc -// +build linux -// +build ppc64 ppc64le -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s b/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s index e42eb81d5..2e8c99612 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && gc -// +build riscv64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s b/vendor/golang.org/x/sys/unix/asm_linux_s390x.s index c46aab339..2c394b11e 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_s390x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && s390x && gc -// +build linux -// +build s390x -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s index 5e7a1169c..fab586a2c 100644 --- a/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s b/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s index f8c5394c1..f949ec547 100644 --- a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_zos_s390x.s b/vendor/golang.org/x/sys/unix/asm_zos_s390x.s index 3b54e1858..2f67ba86d 100644 --- a/vendor/golang.org/x/sys/unix/asm_zos_s390x.s +++ b/vendor/golang.org/x/sys/unix/asm_zos_s390x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x && gc -// +build zos -// +build s390x -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/cap_freebsd.go b/vendor/golang.org/x/sys/unix/cap_freebsd.go index 0b7c6adb8..a08657890 100644 --- a/vendor/golang.org/x/sys/unix/cap_freebsd.go +++ b/vendor/golang.org/x/sys/unix/cap_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd -// +build freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/constants.go b/vendor/golang.org/x/sys/unix/constants.go index 394a3965b..6fb7cb77d 100644 --- a/vendor/golang.org/x/sys/unix/constants.go +++ b/vendor/golang.org/x/sys/unix/constants.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/dev_aix_ppc.go b/vendor/golang.org/x/sys/unix/dev_aix_ppc.go index 65a998508..d78513461 100644 --- a/vendor/golang.org/x/sys/unix/dev_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/dev_aix_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc -// +build aix,ppc // Functions to access/create device major and minor numbers matching the // encoding used by AIX. diff --git a/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go b/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go index 8fc08ad0a..623a5e697 100644 --- a/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc64 -// +build aix,ppc64 // Functions to access/create device major and minor numbers matching the // encoding used AIX. diff --git a/vendor/golang.org/x/sys/unix/dev_zos.go b/vendor/golang.org/x/sys/unix/dev_zos.go index a388e59a0..bb6a64fe9 100644 --- a/vendor/golang.org/x/sys/unix/dev_zos.go +++ b/vendor/golang.org/x/sys/unix/dev_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Functions to access/create device major and minor numbers matching the // encoding used by z/OS. diff --git a/vendor/golang.org/x/sys/unix/dirent.go b/vendor/golang.org/x/sys/unix/dirent.go index 2499f977b..1ebf11782 100644 --- a/vendor/golang.org/x/sys/unix/dirent.go +++ b/vendor/golang.org/x/sys/unix/dirent.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/endian_big.go b/vendor/golang.org/x/sys/unix/endian_big.go index a52026557..1095fd31d 100644 --- a/vendor/golang.org/x/sys/unix/endian_big.go +++ b/vendor/golang.org/x/sys/unix/endian_big.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 -// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/endian_little.go b/vendor/golang.org/x/sys/unix/endian_little.go index b0f2bc4ae..b9f0e277b 100644 --- a/vendor/golang.org/x/sys/unix/endian_little.go +++ b/vendor/golang.org/x/sys/unix/endian_little.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh -// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh package unix diff --git a/vendor/golang.org/x/sys/unix/env_unix.go b/vendor/golang.org/x/sys/unix/env_unix.go index 29ccc4d13..a96da71f4 100644 --- a/vendor/golang.org/x/sys/unix/env_unix.go +++ b/vendor/golang.org/x/sys/unix/env_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Unix environment variables. diff --git a/vendor/golang.org/x/sys/unix/epoll_zos.go b/vendor/golang.org/x/sys/unix/epoll_zos.go index cedaf7e02..7753fddea 100644 --- a/vendor/golang.org/x/sys/unix/epoll_zos.go +++ b/vendor/golang.org/x/sys/unix/epoll_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/fcntl.go b/vendor/golang.org/x/sys/unix/fcntl.go index e9b991258..6200876fb 100644 --- a/vendor/golang.org/x/sys/unix/fcntl.go +++ b/vendor/golang.org/x/sys/unix/fcntl.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go b/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go index 29d44808b..13b4acd5c 100644 --- a/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go +++ b/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc) -// +build linux,386 linux,arm linux,mips linux,mipsle linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/fdset.go b/vendor/golang.org/x/sys/unix/fdset.go index a8068f94f..9e83d18cd 100644 --- a/vendor/golang.org/x/sys/unix/fdset.go +++ b/vendor/golang.org/x/sys/unix/fdset.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/fstatfs_zos.go b/vendor/golang.org/x/sys/unix/fstatfs_zos.go index e377cc9f4..c8bde601e 100644 --- a/vendor/golang.org/x/sys/unix/fstatfs_zos.go +++ b/vendor/golang.org/x/sys/unix/fstatfs_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/gccgo.go b/vendor/golang.org/x/sys/unix/gccgo.go index b06f52d74..aca5721dd 100644 --- a/vendor/golang.org/x/sys/unix/gccgo.go +++ b/vendor/golang.org/x/sys/unix/gccgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && !aix && !hurd -// +build gccgo,!aix,!hurd package unix diff --git a/vendor/golang.org/x/sys/unix/gccgo_c.c b/vendor/golang.org/x/sys/unix/gccgo_c.c index f98a1c542..d468b7b47 100644 --- a/vendor/golang.org/x/sys/unix/gccgo_c.c +++ b/vendor/golang.org/x/sys/unix/gccgo_c.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && !aix && !hurd -// +build gccgo,!aix,!hurd #include #include diff --git a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go b/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go index e60e49a3d..972d61bd7 100644 --- a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && linux && amd64 -// +build gccgo,linux,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/ifreq_linux.go b/vendor/golang.org/x/sys/unix/ifreq_linux.go index 15721a510..848840ae4 100644 --- a/vendor/golang.org/x/sys/unix/ifreq_linux.go +++ b/vendor/golang.org/x/sys/unix/ifreq_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index 0d12c0851..dbe680eab 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) } + +// IoctlLoopConfigure configures all loop device parameters in a single step +func IoctlLoopConfigure(fd int, value *LoopConfig) error { + return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value)) +} diff --git a/vendor/golang.org/x/sys/unix/ioctl_signed.go b/vendor/golang.org/x/sys/unix/ioctl_signed.go index 7def9580e..5b0759bd8 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_signed.go +++ b/vendor/golang.org/x/sys/unix/ioctl_signed.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || solaris -// +build aix solaris package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_unsigned.go b/vendor/golang.org/x/sys/unix/ioctl_unsigned.go index 649913d1e..20f470b9d 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_unsigned.go +++ b/vendor/golang.org/x/sys/unix/ioctl_unsigned.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd hurd linux netbsd openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_zos.go b/vendor/golang.org/x/sys/unix/ioctl_zos.go index cdc21bf76..c8b2a750f 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_zos.go +++ b/vendor/golang.org/x/sys/unix/ioctl_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 47fa6a7eb..fdcaa974d 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -248,6 +248,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -283,10 +284,6 @@ struct ltchars { #include #endif -#ifndef MSG_FASTOPEN -#define MSG_FASTOPEN 0x20000000 -#endif - #ifndef PTRACE_GETREGS #define PTRACE_GETREGS 0xc #endif @@ -295,14 +292,6 @@ struct ltchars { #define PTRACE_SETREGS 0xd #endif -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -#ifndef SOL_SMC -#define SOL_SMC 286 -#endif - #ifdef SOL_BLUETOOTH // SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h // but it is already in bluetooth_linux.go @@ -319,10 +308,23 @@ struct ltchars { #undef TIPC_WAIT_FOREVER #define TIPC_WAIT_FOREVER 0xffffffff -// Copied from linux/l2tp.h -// Including linux/l2tp.h here causes conflicts between linux/in.h -// and netinet/in.h included via net/route.h above. -#define IPPROTO_L2TP 115 +// Copied from linux/netfilter/nf_nat.h +// Including linux/netfilter/nf_nat.h here causes conflicts between linux/in.h +// and netinet/in.h. +#define NF_NAT_RANGE_MAP_IPS (1 << 0) +#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1) +#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2) +#define NF_NAT_RANGE_PERSISTENT (1 << 3) +#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4) +#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5) +#define NF_NAT_RANGE_NETMAP (1 << 6) +#define NF_NAT_RANGE_PROTO_RANDOM_ALL \ + (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY) +#define NF_NAT_RANGE_MASK \ + (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \ + NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \ + NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \ + NF_NAT_RANGE_NETMAP) // Copied from linux/hid.h. // Keep in sync with the size of the referenced fields. @@ -519,6 +521,7 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || + $2 == "LOOP_CONFIGURE" || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || @@ -560,7 +563,7 @@ ccflags="$@" $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || $2 ~ /^CLONE_[A-Z_]+/ || - $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && + $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ && $2 ~ /^(BPF|DLT)_/ || $2 ~ /^AUDIT_/ || $2 ~ /^(CLOCK|TIMER)_/ || @@ -581,7 +584,7 @@ ccflags="$@" $2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ || $2 ~ /^KEYCTL_/ || $2 ~ /^PERF_/ || - $2 ~ /^SECCOMP_MODE_/ || + $2 ~ /^SECCOMP_/ || $2 ~ /^SEEK_/ || $2 ~ /^SCHED_/ || $2 ~ /^SPLICE_/ || @@ -602,6 +605,9 @@ ccflags="$@" $2 ~ /^FSOPT_/ || $2 ~ /^WDIO[CFS]_/ || $2 ~ /^NFN/ || + $2 !~ /^NFT_META_IIFTYPE/ && + $2 ~ /^NFT_/ || + $2 ~ /^NF_NAT_/ || $2 ~ /^XDP_/ || $2 ~ /^RWF_/ || $2 ~ /^(HDIO|WIN|SMART)_/ || @@ -663,7 +669,6 @@ echo '// mkerrors.sh' "$@" echo '// Code generated by the command above; see README.md. DO NOT EDIT.' echo echo "//go:build ${GOARCH} && ${GOOS}" -echo "// +build ${GOARCH},${GOOS}" echo go tool cgo -godefs -- "$@" _const.go >_error.out cat _error.out | grep -vf _error.grep | grep -vf _signal.grep diff --git a/vendor/golang.org/x/sys/unix/mmap_nomremap.go b/vendor/golang.org/x/sys/unix/mmap_nomremap.go index ca0513632..7f602ffd2 100644 --- a/vendor/golang.org/x/sys/unix/mmap_nomremap.go +++ b/vendor/golang.org/x/sys/unix/mmap_nomremap.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris -// +build aix darwin dragonfly freebsd openbsd solaris +//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris || zos package unix diff --git a/vendor/golang.org/x/sys/unix/mremap.go b/vendor/golang.org/x/sys/unix/mremap.go index fa93d0aa9..fd45fe529 100644 --- a/vendor/golang.org/x/sys/unix/mremap.go +++ b/vendor/golang.org/x/sys/unix/mremap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux || netbsd -// +build linux netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/pagesize_unix.go b/vendor/golang.org/x/sys/unix/pagesize_unix.go index 53f1b4c5b..4d0a3430e 100644 --- a/vendor/golang.org/x/sys/unix/pagesize_unix.go +++ b/vendor/golang.org/x/sys/unix/pagesize_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris // For Unix, get the pagesize from the runtime. diff --git a/vendor/golang.org/x/sys/unix/pledge_openbsd.go b/vendor/golang.org/x/sys/unix/pledge_openbsd.go index eb48294b2..6a09af53e 100644 --- a/vendor/golang.org/x/sys/unix/pledge_openbsd.go +++ b/vendor/golang.org/x/sys/unix/pledge_openbsd.go @@ -8,54 +8,31 @@ import ( "errors" "fmt" "strconv" - "syscall" - "unsafe" ) // Pledge implements the pledge syscall. // -// The pledge syscall does not accept execpromises on OpenBSD releases -// before 6.3. -// -// execpromises must be empty when Pledge is called on OpenBSD -// releases predating 6.3, otherwise an error will be returned. +// This changes both the promises and execpromises; use PledgePromises or +// PledgeExecpromises to only change the promises or execpromises +// respectively. // // For more information see pledge(2). func Pledge(promises, execpromises string) error { - maj, min, err := majmin() - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) + pptr, err := BytePtrFromString(promises) if err != nil { return err } - pptr, err := syscall.BytePtrFromString(promises) + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - // This variable will hold either a nil unsafe.Pointer or - // an unsafe.Pointer to a string (execpromises). - var expr unsafe.Pointer - - // If we're running on OpenBSD > 6.2, pass execpromises to the syscall. - if maj > 6 || (maj == 6 && min > 2) { - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - expr = unsafe.Pointer(exptr) - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, exptr) } // PledgePromises implements the pledge syscall. @@ -64,30 +41,16 @@ func Pledge(promises, execpromises string) error { // // For more information see pledge(2). func PledgePromises(promises string) error { - maj, min, err := majmin() - if err != nil { - return err - } - - err = pledgeAvailable(maj, min, "") - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - // This variable holds the execpromises and is always nil. - var expr unsafe.Pointer - - pptr, err := syscall.BytePtrFromString(promises) + pptr, err := BytePtrFromString(promises) if err != nil { return err } - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, nil) } // PledgeExecpromises implements the pledge syscall. @@ -96,30 +59,16 @@ func PledgePromises(promises string) error { // // For more information see pledge(2). func PledgeExecpromises(execpromises string) error { - maj, min, err := majmin() - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - // This variable holds the promises and is always nil. - var pptr unsafe.Pointer - - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0) - if e != 0 { - return e - } - - return nil + return pledge(nil, exptr) } // majmin returns major and minor version number for an OpenBSD system. @@ -147,16 +96,15 @@ func majmin() (major int, minor int, err error) { // pledgeAvailable checks for availability of the pledge(2) syscall // based on the running OpenBSD version. -func pledgeAvailable(maj, min int, execpromises string) error { - // If OpenBSD <= 5.9, pledge is not available. - if (maj == 5 && min != 9) || maj < 5 { - return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min) +func pledgeAvailable() error { + maj, min, err := majmin() + if err != nil { + return err } - // If OpenBSD <= 6.2 and execpromises is not empty, - // return an error - execpromises is not available before 6.3 - if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" { - return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min) + // Require OpenBSD 6.4 as a minimum. + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Pledge on OpenBSD %d.%d", maj, min) } return nil diff --git a/vendor/golang.org/x/sys/unix/ptrace_darwin.go b/vendor/golang.org/x/sys/unix/ptrace_darwin.go index 463c3eff7..3f0975f3d 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_darwin.go +++ b/vendor/golang.org/x/sys/unix/ptrace_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin && !ios -// +build darwin,!ios package unix diff --git a/vendor/golang.org/x/sys/unix/ptrace_ios.go b/vendor/golang.org/x/sys/unix/ptrace_ios.go index ed0509a01..a4d35db5d 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_ios.go +++ b/vendor/golang.org/x/sys/unix/ptrace_ios.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ios -// +build ios package unix diff --git a/vendor/golang.org/x/sys/unix/race.go b/vendor/golang.org/x/sys/unix/race.go index 6f6c5fec5..714d2aae7 100644 --- a/vendor/golang.org/x/sys/unix/race.go +++ b/vendor/golang.org/x/sys/unix/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin && race) || (linux && race) || (freebsd && race) -// +build darwin,race linux,race freebsd,race package unix diff --git a/vendor/golang.org/x/sys/unix/race0.go b/vendor/golang.org/x/sys/unix/race0.go index 706e1322a..4a9f6634c 100644 --- a/vendor/golang.org/x/sys/unix/race0.go +++ b/vendor/golang.org/x/sys/unix/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || (darwin && !race) || (linux && !race) || (freebsd && !race) || netbsd || openbsd || solaris || dragonfly || zos -// +build aix darwin,!race linux,!race freebsd,!race netbsd openbsd solaris dragonfly zos package unix diff --git a/vendor/golang.org/x/sys/unix/readdirent_getdents.go b/vendor/golang.org/x/sys/unix/readdirent_getdents.go index 4d6257569..dbd2b6ccb 100644 --- a/vendor/golang.org/x/sys/unix/readdirent_getdents.go +++ b/vendor/golang.org/x/sys/unix/readdirent_getdents.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd -// +build aix dragonfly freebsd linux netbsd openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go b/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go index 2a4ba47c4..130398b6b 100644 --- a/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go +++ b/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package unix diff --git a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go index 3865943f6..c3a62dbb1 100644 --- a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go +++ b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Socket control messages diff --git a/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go b/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go index 0840fe4a5..4a1eab37e 100644 --- a/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go +++ b/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/syscall.go b/vendor/golang.org/x/sys/unix/syscall.go index 63e8c8383..5ea74da98 100644 --- a/vendor/golang.org/x/sys/unix/syscall.go +++ b/vendor/golang.org/x/sys/unix/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Package unix contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index e94e6cdac..67ce6cef2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix // Aix system calls. // This file is compiled as ordinary Go code, @@ -107,7 +106,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go index f2871fa95..1fdaa4760 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc -// +build aix,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go index 75718ec0f..c87f9a9f4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc64 -// +build aix,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index 4217de518..a00c3e545 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd // BSD system call wrappers shared by *BSD based systems // including OS X (Darwin) and FreeBSD. Like the other @@ -317,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go index b37310ce9..0eaecf5fc 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go index d51ec9963..f36c6707c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go index 53c96641f..2f0fa76e4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin && go1.12 -// +build darwin,go1.12 +//go:build darwin package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go index 4e2d32120..14bab6b2d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go index 64d1bb4db..2b57e0f73 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -13,6 +13,7 @@ package unix import ( + "errors" "sync" "unsafe" ) @@ -169,25 +170,26 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { func Uname(uname *Utsname) error { mib := []_C_int{CTL_KERN, KERN_OSTYPE} n := unsafe.Sizeof(uname.Sysname) - if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil { + // Suppress ENOMEM errors to be compatible with the C library __xuname() implementation. + if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { return err } mib = []_C_int{CTL_KERN, KERN_HOSTNAME} n = unsafe.Sizeof(uname.Nodename) - if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil { + if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { return err } mib = []_C_int{CTL_KERN, KERN_OSRELEASE} n = unsafe.Sizeof(uname.Release) - if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil { + if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { return err } mib = []_C_int{CTL_KERN, KERN_VERSION} n = unsafe.Sizeof(uname.Version) - if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil { + if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { return err } @@ -205,7 +207,7 @@ func Uname(uname *Utsname) error { mib = []_C_int{CTL_HW, HW_MACHINE} n = unsafe.Sizeof(uname.Machine) - if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil { + if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { return err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go index b8da51004..3967bca77 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go index 47155c483..eff19ada2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go index 08932093f..4f24b517a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go index d151a0d0e..ac30759ec 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go index d5cd64b37..aab725ca7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd.go b/vendor/golang.org/x/sys/unix/syscall_hurd.go index 381fd4673..ba46651f8 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build hurd -// +build hurd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd_386.go b/vendor/golang.org/x/sys/unix/syscall_hurd_386.go index 7cf54a3e4..df89f9e6b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && hurd -// +build 386,hurd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_illumos.go b/vendor/golang.org/x/sys/unix/syscall_illumos.go index 87db5a6a8..a863f7052 100644 --- a/vendor/golang.org/x/sys/unix/syscall_illumos.go +++ b/vendor/golang.org/x/sys/unix/syscall_illumos.go @@ -5,7 +5,6 @@ // illumos system calls not present on Solaris. //go:build amd64 && illumos -// +build amd64,illumos package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index fb4e50224..5682e2628 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) ( } //sys fchmodat(dirfd int, path string, mode uint32) (err error) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - // Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior - // and check the flags. Otherwise the mode would be applied to the symlink - // destination which is not what the user expects. - if flags&^AT_SYMLINK_NOFOLLOW != 0 { - return EINVAL - } else if flags&AT_SYMLINK_NOFOLLOW != 0 { - return EOPNOTSUPP +//sys fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // Linux fchmodat doesn't support the flags parameter, but fchmodat2 does. + // Try fchmodat2 if flags are specified. + if flags != 0 { + err := fchmodat2(dirfd, path, mode, flags) + if err == ENOSYS { + // fchmodat2 isn't available. If the flags are known to be valid, + // return EOPNOTSUPP to indicate that fchmodat doesn't support them. + if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EINVAL + } else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EOPNOTSUPP + } + } + return err } return fchmodat(dirfd, path, mode) } @@ -417,7 +425,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -1301,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) { @@ -1840,6 +1849,105 @@ func Dup2(oldfd, newfd int) error { //sys Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) //sys Fsopen(fsName string, flags int) (fd int, err error) //sys Fspick(dirfd int, pathName string, flags int) (fd int, err error) + +//sys fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) + +func fsconfigCommon(fd int, cmd uint, key string, value *byte, aux int) (err error) { + var keyp *byte + if keyp, err = BytePtrFromString(key); err != nil { + return + } + return fsconfig(fd, cmd, keyp, value, aux) +} + +// FsconfigSetFlag is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_FLAG. +// +// fd is the filesystem context to act upon. +// key the parameter key to set. +func FsconfigSetFlag(fd int, key string) (err error) { + return fsconfigCommon(fd, FSCONFIG_SET_FLAG, key, nil, 0) +} + +// FsconfigSetString is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_STRING. +// +// fd is the filesystem context to act upon. +// key the parameter key to set. +// value is the parameter value to set. +func FsconfigSetString(fd int, key string, value string) (err error) { + var valuep *byte + if valuep, err = BytePtrFromString(value); err != nil { + return + } + return fsconfigCommon(fd, FSCONFIG_SET_STRING, key, valuep, 0) +} + +// FsconfigSetBinary is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_BINARY. +// +// fd is the filesystem context to act upon. +// key the parameter key to set. +// value is the parameter value to set. +func FsconfigSetBinary(fd int, key string, value []byte) (err error) { + if len(value) == 0 { + return EINVAL + } + return fsconfigCommon(fd, FSCONFIG_SET_BINARY, key, &value[0], len(value)) +} + +// FsconfigSetPath is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_PATH. +// +// fd is the filesystem context to act upon. +// key the parameter key to set. +// path is a non-empty path for specified key. +// atfd is a file descriptor at which to start lookup from or AT_FDCWD. +func FsconfigSetPath(fd int, key string, path string, atfd int) (err error) { + var valuep *byte + if valuep, err = BytePtrFromString(path); err != nil { + return + } + return fsconfigCommon(fd, FSCONFIG_SET_PATH, key, valuep, atfd) +} + +// FsconfigSetPathEmpty is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_PATH_EMPTY. The same as +// FconfigSetPath but with AT_PATH_EMPTY implied. +func FsconfigSetPathEmpty(fd int, key string, path string, atfd int) (err error) { + var valuep *byte + if valuep, err = BytePtrFromString(path); err != nil { + return + } + return fsconfigCommon(fd, FSCONFIG_SET_PATH_EMPTY, key, valuep, atfd) +} + +// FsconfigSetFd is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_SET_FD. +// +// fd is the filesystem context to act upon. +// key the parameter key to set. +// value is a file descriptor to be assigned to specified key. +func FsconfigSetFd(fd int, key string, value int) (err error) { + return fsconfigCommon(fd, FSCONFIG_SET_FD, key, nil, value) +} + +// FsconfigCreate is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_CMD_CREATE. +// +// fd is the filesystem context to act upon. +func FsconfigCreate(fd int) (err error) { + return fsconfig(fd, FSCONFIG_CMD_CREATE, nil, nil, 0) +} + +// FsconfigReconfigure is equivalent to fsconfig(2) called +// with cmd == FSCONFIG_CMD_RECONFIGURE. +// +// fd is the filesystem context to act upon. +func FsconfigReconfigure(fd int) (err error) { + return fsconfig(fd, FSCONFIG_CMD_RECONFIGURE, nil, nil, 0) +} + //sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64 //sysnb Getpgid(pid int) (pgid int, err error) @@ -2482,3 +2590,5 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { } return attr, nil } + +//sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_386.go index c7d9945ea..506dafa7b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && linux -// +build 386,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go index 08086ac6a..38d55641b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (386 || amd64 || mips || mipsle || mips64 || mipsle || ppc64 || ppc64le || ppc || s390x || sparc64) -// +build linux -// +build 386 amd64 mips mipsle mips64 mipsle ppc64 ppc64le ppc s390x sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index 70601ce36..d557cf8de 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && linux -// +build amd64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go index 8b0f0f3aa..facdb83b2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && linux && gc -// +build amd64,linux,gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index da2986415..cd2dd797f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && linux -// +build arm,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index f5266689a..cf2ee6c75 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && linux -// +build arm64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc.go index 2b1168d7d..ffc4c2b63 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gc -// +build linux,gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go index 9843fb489..9ebfdcf44 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gc && 386 -// +build linux,gc,386 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go index a6008fccd..5f2b57c4c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && gc && linux -// +build arm,gc,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go index 7740af242..d1a3ad826 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gccgo && 386 -// +build linux,gccgo,386 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go index e16a12299..f2f67423e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gccgo && arm -// +build linux,gccgo,arm package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index f6ab02ec1..3d0e98451 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 && linux -// +build loong64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index 93fe59d25..70963a95a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go index aae7f0ffd..c218ebd28 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go index 66eff19a3..e6c48500c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && ppc -// +build linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index 806aa2574..7286a9aa8 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 5e6ceee12..6f5a28894 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && linux -// +build riscv64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go index 2f89e8f5d..66f31210d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build s390x && linux -// +build s390x,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go index 7ca064ae7..11d1f1698 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build sparc64 && linux -// +build sparc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go index 5199d282f..7a5eb5743 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go index 70a9c52e9..62d8957ae 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go index 3eb5942f9..ce6a06885 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go index fc6ccfd81..d46d689d1 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 6f34479b5..b25343c71 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -137,18 +137,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { - var _p0 unsafe.Pointer + var bufptr *Statfs_t var bufsize uintptr if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + bufptr = &buf[0] bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) } - r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) - n = int(r0) - if e1 != 0 { - err = e1 - } - return + return getfsstat(bufptr, bufsize, flags) } //sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) @@ -171,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) { //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL +//sys fcntl(fd int, cmd int, arg int) (n int, err error) +//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL + +// FcntlInt performs a fcntl syscall on fd with the provided command and argument. +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return fcntl(int(fd), cmd, arg) +} + +// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. +func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { + _, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk)) + return err +} + //sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { @@ -326,4 +335,7 @@ func Uname(uname *Utsname) error { //sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) +//sys getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) //sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) +//sys pledge(promises *byte, execpromises *byte) (err error) +//sys unveil(path *byte, flags *byte) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go index 6baabcdcb..9ddc89f4f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go index bab25360e..70a3c96ee 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go index 8eed3c4d4..265caa87f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go index 483dde99d..ac4fda171 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go index 04aa43f41..0a451e6dd 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd -// +build openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go index c2796139c..30a308cbb 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go index 23199a7ff..ea954330f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index b99cfa134..21974af06 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -128,7 +128,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -157,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } const ImplementsGetwd = true diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go index 0bd25ef81..e02d8ceae 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index f6eda2705..77081de8c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go index b6919ca58..05c95bccf 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (linux && !ppc64 && !ppc64le) || netbsd || openbsd || solaris) && gc -// +build darwin dragonfly freebsd linux,!ppc64,!ppc64le netbsd openbsd solaris -// +build gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go index f6f707acf..23f39b7af 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64le || ppc64) && gc -// +build linux -// +build ppc64le ppc64 -// +build gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index 4596d041c..27c41b6f0 100644 --- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix @@ -1105,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { @@ -1521,6 +1520,14 @@ func (m *mmapper) Munmap(data []byte) (err error) { return nil } +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return mapper.Mmap(fd, offset, length, prot, flags) +} + +func Munmap(b []byte) (err error) { + return mapper.Munmap(b) +} + func Read(fd int, p []byte) (n int, err error) { n, err = read(fd, p) if raceenabled { diff --git a/vendor/golang.org/x/sys/unix/sysvshm_linux.go b/vendor/golang.org/x/sys/unix/sysvshm_linux.go index 2c3a4437f..4fcd38de2 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_linux.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package unix diff --git a/vendor/golang.org/x/sys/unix/sysvshm_unix.go b/vendor/golang.org/x/sys/unix/sysvshm_unix.go index 5bb41d17b..79a84f18b 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_unix.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin && !ios) || linux -// +build darwin,!ios linux package unix diff --git a/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go b/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go index 71bddefdb..9eb0db664 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin && !ios -// +build darwin,!ios package unix diff --git a/vendor/golang.org/x/sys/unix/timestruct.go b/vendor/golang.org/x/sys/unix/timestruct.go index 616b1b284..7997b1902 100644 --- a/vendor/golang.org/x/sys/unix/timestruct.go +++ b/vendor/golang.org/x/sys/unix/timestruct.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/unveil_openbsd.go b/vendor/golang.org/x/sys/unix/unveil_openbsd.go index 168d5ae77..cb7e598ce 100644 --- a/vendor/golang.org/x/sys/unix/unveil_openbsd.go +++ b/vendor/golang.org/x/sys/unix/unveil_openbsd.go @@ -4,39 +4,48 @@ package unix -import ( - "syscall" - "unsafe" -) +import "fmt" // Unveil implements the unveil syscall. // For more information see unveil(2). // Note that the special case of blocking further // unveil calls is handled by UnveilBlock. func Unveil(path string, flags string) error { - pathPtr, err := syscall.BytePtrFromString(path) - if err != nil { + if err := supportsUnveil(); err != nil { return err } - flagsPtr, err := syscall.BytePtrFromString(flags) + pathPtr, err := BytePtrFromString(path) if err != nil { return err } - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0) - if e != 0 { - return e + flagsPtr, err := BytePtrFromString(flags) + if err != nil { + return err } - return nil + return unveil(pathPtr, flagsPtr) } // UnveilBlock blocks future unveil calls. // For more information see unveil(2). func UnveilBlock() error { - // Both pointers must be nil. - var pathUnsafe, flagsUnsafe unsafe.Pointer - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(pathUnsafe), uintptr(flagsUnsafe), 0) - if e != 0 { - return e + if err := supportsUnveil(); err != nil { + return err } + return unveil(nil, nil) +} + +// supportsUnveil checks for availability of the unveil(2) system call based +// on the running OpenBSD version. +func supportsUnveil() error { + maj, min, err := majmin() + if err != nil { + return err + } + + // unveil is not available before 6.4 + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Unveil on OpenBSD %d.%d", maj, min) + } + return nil } diff --git a/vendor/golang.org/x/sys/unix/xattr_bsd.go b/vendor/golang.org/x/sys/unix/xattr_bsd.go index f5f8e9f36..e16879396 100644 --- a/vendor/golang.org/x/sys/unix/xattr_bsd.go +++ b/vendor/golang.org/x/sys/unix/xattr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || netbsd -// +build freebsd netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go index ca9799b79..2fb219d78 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && aix -// +build ppc,aix // Created by cgo -godefs - DO NOT EDIT // cgo -godefs -- -maix32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go index 200c8c26f..b0e6f5c85 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && aix -// +build ppc64,aix // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -maix64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go index 143007627..e40fa8524 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go index ab044a742..bb02aa6c0 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go index 17bba0e44..c0e0f8694 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go index f8c2c5138..6c6923906 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go index 96310c3be..dd9163f8e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go index 777b69def..493a2a793 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go index c557ac2db..8b437b307 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go index 341b4d962..67c02dd57 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index f9c7f479b..36bf8399f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -481,10 +480,13 @@ const ( BPF_FROM_BE = 0x8 BPF_FROM_LE = 0x0 BPF_FS_MAGIC = 0xcafe4a11 + BPF_F_AFTER = 0x10 BPF_F_ALLOW_MULTI = 0x2 BPF_F_ALLOW_OVERRIDE = 0x1 BPF_F_ANY_ALIGNMENT = 0x2 - BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_BEFORE = 0x8 + BPF_F_ID = 0x20 + BPF_F_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 @@ -521,6 +523,7 @@ const ( BPF_MAJOR_VERSION = 0x1 BPF_MAXINSNS = 0x1000 BPF_MEM = 0x60 + BPF_MEMSX = 0x80 BPF_MEMWORDS = 0x10 BPF_MINOR_VERSION = 0x1 BPF_MISC = 0x7 @@ -776,6 +779,8 @@ const ( DEVLINK_GENL_MCGRP_CONFIG_NAME = "config" DEVLINK_GENL_NAME = "devlink" DEVLINK_GENL_VERSION = 0x1 + DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO = 0x4 + DEVLINK_PORT_FN_CAP_IPSEC_PACKET = 0x8 DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2 DEVLINK_PORT_FN_CAP_ROCE = 0x1 DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14 @@ -1698,6 +1703,7 @@ const ( KEXEC_ON_CRASH = 0x1 KEXEC_PRESERVE_CONTEXT = 0x2 KEXEC_SEGMENT_MAX = 0x10 + KEXEC_UPDATE_ELFCOREHDR = 0x4 KEYCTL_ASSUME_AUTHORITY = 0x10 KEYCTL_CAPABILITIES = 0x1f KEYCTL_CAPS0_BIG_KEY = 0x10 @@ -1779,6 +1785,8 @@ const ( LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20 LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000 LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2 + LANDLOCK_ACCESS_NET_BIND_TCP = 0x1 + LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2 LANDLOCK_CREATE_RULESET_VERSION = 0x1 LINUX_REBOOT_CMD_CAD_OFF = 0x0 LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef @@ -1795,6 +1803,7 @@ const ( LOCK_SH = 0x1 LOCK_UN = 0x8 LOOP_CLR_FD = 0x4c01 + LOOP_CONFIGURE = 0x4c0a LOOP_CTL_ADD = 0x4c80 LOOP_CTL_GET_FREE = 0x4c82 LOOP_CTL_REMOVE = 0x4c81 @@ -2120,6 +2129,60 @@ const ( NFNL_SUBSYS_QUEUE = 0x3 NFNL_SUBSYS_ULOG = 0x4 NFS_SUPER_MAGIC = 0x6969 + NFT_CHAIN_FLAGS = 0x7 + NFT_CHAIN_MAXNAMELEN = 0x100 + NFT_CT_MAX = 0x17 + NFT_DATA_RESERVED_MASK = 0xffffff00 + NFT_DATA_VALUE_MAXLEN = 0x40 + NFT_EXTHDR_OP_MAX = 0x4 + NFT_FIB_RESULT_MAX = 0x3 + NFT_INNER_MASK = 0xf + NFT_LOGLEVEL_MAX = 0x8 + NFT_NAME_MAXLEN = 0x100 + NFT_NG_MAX = 0x1 + NFT_OBJECT_CONNLIMIT = 0x5 + NFT_OBJECT_COUNTER = 0x1 + NFT_OBJECT_CT_EXPECT = 0x9 + NFT_OBJECT_CT_HELPER = 0x3 + NFT_OBJECT_CT_TIMEOUT = 0x7 + NFT_OBJECT_LIMIT = 0x4 + NFT_OBJECT_MAX = 0xa + NFT_OBJECT_QUOTA = 0x2 + NFT_OBJECT_SECMARK = 0x8 + NFT_OBJECT_SYNPROXY = 0xa + NFT_OBJECT_TUNNEL = 0x6 + NFT_OBJECT_UNSPEC = 0x0 + NFT_OBJ_MAXNAMELEN = 0x100 + NFT_OSF_MAXGENRELEN = 0x10 + NFT_QUEUE_FLAG_BYPASS = 0x1 + NFT_QUEUE_FLAG_CPU_FANOUT = 0x2 + NFT_QUEUE_FLAG_MASK = 0x3 + NFT_REG32_COUNT = 0x10 + NFT_REG32_SIZE = 0x4 + NFT_REG_MAX = 0x4 + NFT_REG_SIZE = 0x10 + NFT_REJECT_ICMPX_MAX = 0x3 + NFT_RT_MAX = 0x4 + NFT_SECMARK_CTX_MAXLEN = 0x100 + NFT_SET_MAXNAMELEN = 0x100 + NFT_SOCKET_MAX = 0x3 + NFT_TABLE_F_MASK = 0x3 + NFT_TABLE_MAXNAMELEN = 0x100 + NFT_TRACETYPE_MAX = 0x3 + NFT_TUNNEL_F_MASK = 0x7 + NFT_TUNNEL_MAX = 0x1 + NFT_TUNNEL_MODE_MAX = 0x2 + NFT_USERDATA_MAXLEN = 0x100 + NFT_XFRM_KEY_MAX = 0x6 + NF_NAT_RANGE_MAP_IPS = 0x1 + NF_NAT_RANGE_MASK = 0x7f + NF_NAT_RANGE_NETMAP = 0x40 + NF_NAT_RANGE_PERSISTENT = 0x8 + NF_NAT_RANGE_PROTO_OFFSET = 0x20 + NF_NAT_RANGE_PROTO_RANDOM = 0x4 + NF_NAT_RANGE_PROTO_RANDOM_ALL = 0x14 + NF_NAT_RANGE_PROTO_RANDOM_FULLY = 0x10 + NF_NAT_RANGE_PROTO_SPECIFIED = 0x2 NILFS_SUPER_MAGIC = 0x3434 NL0 = 0x0 NL1 = 0x100 @@ -2275,6 +2338,7 @@ const ( PERF_MEM_LVLNUM_PMEM = 0xe PERF_MEM_LVLNUM_RAM = 0xd PERF_MEM_LVLNUM_SHIFT = 0x21 + PERF_MEM_LVLNUM_UNC = 0x8 PERF_MEM_LVL_HIT = 0x2 PERF_MEM_LVL_IO = 0x1000 PERF_MEM_LVL_L1 = 0x8 @@ -2403,6 +2467,7 @@ const ( PR_MCE_KILL_GET = 0x22 PR_MCE_KILL_LATE = 0x0 PR_MCE_KILL_SET = 0x1 + PR_MDWE_NO_INHERIT = 0x2 PR_MDWE_REFUSE_EXEC_GAIN = 0x1 PR_MPX_DISABLE_MANAGEMENT = 0x2c PR_MPX_ENABLE_MANAGEMENT = 0x2b @@ -2607,8 +2672,9 @@ const ( RTAX_FEATURES = 0xc RTAX_FEATURE_ALLFRAG = 0x8 RTAX_FEATURE_ECN = 0x1 - RTAX_FEATURE_MASK = 0xf + RTAX_FEATURE_MASK = 0x1f RTAX_FEATURE_SACK = 0x2 + RTAX_FEATURE_TCP_USEC_TS = 0x10 RTAX_FEATURE_TIMESTAMP = 0x4 RTAX_HOPLIMIT = 0xa RTAX_INITCWND = 0xb @@ -2851,9 +2917,38 @@ const ( SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x1d SC_LOG_FLUSH = 0x100000 + SECCOMP_ADDFD_FLAG_SEND = 0x2 + SECCOMP_ADDFD_FLAG_SETFD = 0x1 + SECCOMP_FILTER_FLAG_LOG = 0x2 + SECCOMP_FILTER_FLAG_NEW_LISTENER = 0x8 + SECCOMP_FILTER_FLAG_SPEC_ALLOW = 0x4 + SECCOMP_FILTER_FLAG_TSYNC = 0x1 + SECCOMP_FILTER_FLAG_TSYNC_ESRCH = 0x10 + SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV = 0x20 + SECCOMP_GET_ACTION_AVAIL = 0x2 + SECCOMP_GET_NOTIF_SIZES = 0x3 + SECCOMP_IOCTL_NOTIF_RECV = 0xc0502100 + SECCOMP_IOCTL_NOTIF_SEND = 0xc0182101 + SECCOMP_IOC_MAGIC = '!' SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 + SECCOMP_RET_ACTION = 0x7fff0000 + SECCOMP_RET_ACTION_FULL = 0xffff0000 + SECCOMP_RET_ALLOW = 0x7fff0000 + SECCOMP_RET_DATA = 0xffff + SECCOMP_RET_ERRNO = 0x50000 + SECCOMP_RET_KILL = 0x0 + SECCOMP_RET_KILL_PROCESS = 0x80000000 + SECCOMP_RET_KILL_THREAD = 0x0 + SECCOMP_RET_LOG = 0x7ffc0000 + SECCOMP_RET_TRACE = 0x7ff00000 + SECCOMP_RET_TRAP = 0x30000 + SECCOMP_RET_USER_NOTIF = 0x7fc00000 + SECCOMP_SET_MODE_FILTER = 0x1 + SECCOMP_SET_MODE_STRICT = 0x0 + SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP = 0x1 + SECCOMP_USER_NOTIF_FLAG_CONTINUE = 0x1 SECRETMEM_MAGIC = 0x5345434d SECURITYFS_MAGIC = 0x73636673 SEEK_CUR = 0x1 @@ -3013,6 +3108,7 @@ const ( SOL_TIPC = 0x10f SOL_TLS = 0x11a SOL_UDP = 0x11 + SOL_VSOCK = 0x11f SOL_X25 = 0x106 SOL_XDP = 0x11b SOMAXCONN = 0x1000 @@ -3461,6 +3557,7 @@ const ( XDP_PACKET_HEADROOM = 0x100 XDP_PGOFF_RX_RING = 0x0 XDP_PGOFF_TX_RING = 0x80000000 + XDP_PKT_CONTD = 0x1 XDP_RING_NEED_WAKEUP = 0x1 XDP_RX_RING = 0x2 XDP_SHARED_UMEM = 0x1 @@ -3473,6 +3570,7 @@ const ( XDP_UMEM_REG = 0x4 XDP_UMEM_UNALIGNED_CHUNK_FLAG = 0x1 XDP_USE_NEED_WAKEUP = 0x8 + XDP_USE_SG = 0x10 XDP_ZEROCOPY = 0x4 XENFS_SUPER_MAGIC = 0xabba1974 XFS_SUPER_MAGIC = 0x58465342 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 30aee00a5..42ff8c3c1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/386/include -m32 _const.go @@ -282,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 8ebfa5127..dca436004 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/amd64/include -m64 _const.go @@ -283,6 +282,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 271a21cdc..5cca668ac 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/arm/include _const.go @@ -289,6 +288,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 910c330a3..d8cae6d15 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char _const.go @@ -279,6 +278,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index a640798c9..28e39afdc 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/loong64/include _const.go @@ -119,6 +118,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 LASX_CTX_MAGIC = 0x41535801 + LBT_CTX_MAGIC = 0x42540001 LSX_CTX_MAGIC = 0x53580001 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 @@ -275,6 +275,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 0d5925d34..cd66e92cb 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips/include _const.go @@ -282,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index d72a00e0b..c1595eba7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips64/include _const.go @@ -282,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 02ba129f8..ee9456b0d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips64le/include _const.go @@ -282,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 8daa6dd96..8cfca81e1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mipsle/include _const.go @@ -282,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 63c8fa2f7..60b0deb3a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc/include _const.go @@ -337,6 +336,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 930799ec1..f90aa7281 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64/include _const.go @@ -341,6 +340,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 8605a7dd7..ba9e01503 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64le/include _const.go @@ -341,6 +340,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 95a016f1c..07cdfd6e9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/riscv64/include _const.go @@ -228,6 +227,9 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTRACE_GETFDPIC = 0x21 + PTRACE_GETFDPIC_EXEC = 0x0 + PTRACE_GETFDPIC_INTERP = 0x1 RLIMIT_AS = 0x9 RLIMIT_MEMLOCK = 0x8 RLIMIT_NOFILE = 0x7 @@ -270,6 +272,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 1ae0108f5..2f1dd214a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/s390x/include -fsigned-char _const.go @@ -345,6 +344,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 1bb7c6333..f40519d90 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/sparc64/include _const.go @@ -336,6 +335,9 @@ const ( SCM_TIMESTAMPNS = 0x21 SCM_TXTIME = 0x3f SCM_WIFI_STATUS = 0x25 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x400000 SFD_NONBLOCK = 0x4000 SF_FP = 0x38 diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go index 72f7420d2..130085df4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go index 8d4eb0c08..84769a1a3 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go index 9eef9749f..602ded003 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -marm _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go index 3b62ba192..efc0406ee 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go index af20e474b..5a6500f83 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go index 6015fcb2b..a5aeeb979 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go index 8d44955e4..0e9748a72 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go index ae16fe754..4f4449abc 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go index 03d90fe35..76a363f0f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go index 8e2c51b1e..43ca0cdfd 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go index 13d403031..b1b8bb200 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go index 1afee6a08..d2ddd3176 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && solaris -// +build amd64,solaris // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go index fc7d0506f..4dfd2e051 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Hand edited based on zerrors_linux_s390x.go // TODO: auto-generate. diff --git a/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go b/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go index 97f20ca28..586317c78 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("arm", "arm64"). DO NOT EDIT. //go:build linux && (arm || arm64) -// +build linux -// +build arm arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go b/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go index 0b5f79430..d7c881be7 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("mips", "mips64"). DO NOT EDIT. //go:build linux && (mips || mips64) -// +build linux -// +build mips mips64 package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go b/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go index 2807f7e64..2d2de5d29 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("mipsle", "mips64le"). DO NOT EDIT. //go:build linux && (mipsle || mips64le) -// +build linux -// +build mipsle mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go b/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go index 281ea64e3..5adc79fb5 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("386", "amd64"). DO NOT EDIT. //go:build linux && (386 || amd64) -// +build linux -// +build 386 amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go index d1d1d2331..6ea64a3c0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc -// +build aix,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go index f99a18adc..99ee4399a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 -// +build aix,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go index c4d50ae50..b68a78362 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 && gc -// +build aix,ppc64,gc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go index 6903d3b09..0a87450bf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 && gccgo -// +build aix,ppc64,gccgo package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 1cad561e9..ccb02f240 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build darwin && amd64 -// +build darwin,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index b18edbd0e..1b40b997b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build darwin && arm64 -// +build darwin,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go index 0c67df64a..aad65fc79 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build dragonfly && amd64 -// +build dragonfly,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index e6e05d145..c0096391a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && 386 -// +build freebsd,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index 7508accac..7664df749 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && amd64 -// +build freebsd,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index 7b56aead4..ae099182c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && arm -// +build freebsd,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go index cc623dcaa..11fd5d45b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && arm64 -// +build freebsd,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go index 581849197..c3d2d6530 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && riscv64 -// +build freebsd,riscv64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go index 6be25cd19..c698cbc01 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build illumos && amd64 -// +build illumos,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 1ff3aec74..87d8612a1 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -38,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { @@ -892,6 +906,16 @@ func Fspick(dirfd int, pathName string, flags int) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) { + _, _, e1 := Syscall6(SYS_FSCONFIG, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(value)), uintptr(aux), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Getdents(fd int, buf []byte) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { @@ -2195,3 +2219,13 @@ func schedGetattr(pid int, attr *SchedAttr, size uint, flags uint) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) { + _, _, e1 := Syscall6(SYS_CACHESTAT, uintptr(fd), uintptr(unsafe.Pointer(crange)), uintptr(unsafe.Pointer(cstat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go index 07b549cc2..4def3e9fc 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && 386 -// +build linux,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go index 5f481bf83..fef2bc8ba 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && amd64 -// +build linux,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go index 824cd52c7..a9fd76a88 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && arm -// +build linux,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go index e77aecfe9..460065028 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && arm64 -// +build linux,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go index 806ffd1e1..c8987d264 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && loong64 -// +build linux,loong64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go index 961a3afb7..921f43061 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips -// +build linux,mips package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go index ed05005e9..44f067829 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips64 -// +build linux,mips64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go index d365b718f..e7fa0abf0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips64le -// +build linux,mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go index c3f1b8bbd..8c5125675 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mipsle -// +build linux,mipsle package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go index a6574cf98..7392fd45e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc -// +build linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go index f40990264..41180434e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64 -// +build linux,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go index 9dfcc2997..40c6ce7ae 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64le -// +build linux,ppc64le package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go index 0ab4f2ed7..2cfe34adb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && riscv64 -// +build linux,riscv64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go index 6cde32237..61e6f0709 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && s390x -// +build linux,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go index 5253d65bf..834b84204 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && sparc64 -// +build linux,sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 2df3c5bac..e91ebc14a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && 386 -// +build netbsd,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index a60556bab..be28babbc 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && amd64 -// +build netbsd,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 9f788917a..fb587e826 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && arm -// +build netbsd,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go index 82a4cb2dc..d576438bb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && arm64 -// +build netbsd,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 66b3b6456..9dc42410b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && 386 -// +build openbsd,386 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 3dcacd30d..41b561731 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index c5c4cc112..0d3a0751c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && amd64 -// +build openbsd,amd64 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 2763620b0..4019a656f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 93bfbb328..c39f7776d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && arm -// +build openbsd,arm package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index c92231404..ac4af24f9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index a107b8fda..57571d072 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && arm64 -// +build openbsd,arm64 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index a6bc32c92..f77d53212 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index c427de509..e62963e67 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && mips64 -// +build openbsd,mips64 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index b4e7bceab..fae140b62 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 60c1a99ae..00831354c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && ppc64 -// +build openbsd,ppc64 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index ca3f76600..9d1e0ff06 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_fcntl(SB) + RET +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ppoll(SB) RET @@ -801,8 +807,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getfsstat(SB) + RET +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_utimensat(SB) RET GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_pledge(SB) + RET +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_unveil(SB) + RET +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 52eba360f..79029ed58 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && riscv64 -// +build openbsd,riscv64 package unix @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2269,31 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 477a7d5b2..da115f9a4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index b40189464..829b87feb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build solaris && amd64 -// +build solaris,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go index 1d8fe1d4b..94f011238 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go index 55e048471..3a58ae819 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go index d2243cf83..dcb7a0eb7 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go index 82dc51bd8..db5a7bf13 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go index cbdda1a4a..7be575a77 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go index f55eae1a8..d6e3174c6 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go index e44054470..ee97157d0 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go index a0db82fce..35c3b91d0 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go index f8298ff9b..5edda7687 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go index 5eb433bbf..0dc9e8b4d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go index 703675c0c..308ddf3a1 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go index 4e0d96107..418664e3d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go index 01636b838..34d0b86d7 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go index ad99bc106..b71cf45e2 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go index 89dcc4274..e32df1c1e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go index ee37aaa0c..15ad6111f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 9862853d3..0cc3ce496 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux package unix @@ -448,4 +447,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 8901f0f4e..856d92d69 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux package unix @@ -370,4 +369,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index 6902c37ee..8d467094c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux package unix @@ -412,4 +411,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index a6d3dff81..edc173244 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux package unix @@ -315,4 +314,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index b18f3f710..445eba206 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux package unix @@ -309,4 +308,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 0302e5e3d..adba01bca 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux package unix @@ -432,4 +431,9 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 + SYS_MAP_SHADOW_STACK = 4453 + SYS_FUTEX_WAKE = 4454 + SYS_FUTEX_WAIT = 4455 + SYS_FUTEX_REQUEUE = 4456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 6693ba4a0..014c4e9c7 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux package unix @@ -362,4 +361,9 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 + SYS_MAP_SHADOW_STACK = 5453 + SYS_FUTEX_WAKE = 5454 + SYS_FUTEX_WAIT = 5455 + SYS_FUTEX_REQUEUE = 5456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index fd93f4987..ccc97d74d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux package unix @@ -362,4 +361,9 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 + SYS_MAP_SHADOW_STACK = 5453 + SYS_FUTEX_WAKE = 5454 + SYS_FUTEX_WAIT = 5455 + SYS_FUTEX_REQUEUE = 5456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index 760ddcadc..ec2b64a95 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux package unix @@ -432,4 +431,9 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 + SYS_MAP_SHADOW_STACK = 4453 + SYS_FUTEX_WAKE = 4454 + SYS_FUTEX_WAIT = 4455 + SYS_FUTEX_REQUEUE = 4456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index cff2b2555..21a839e33 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux package unix @@ -439,4 +438,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index a4b2405d0..c11121ec3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux package unix @@ -411,4 +410,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index aca54b4e3..909b631fc 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux package unix @@ -411,4 +410,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 9d1738d64..e49bed16e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux package unix @@ -316,4 +315,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 022878dc8..66017d2d3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux package unix @@ -377,4 +376,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 4100a761c..47bab18dc 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux package unix @@ -390,4 +389,9 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go index 3a6699eba..b2aa8cd49 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go index 5677cd4f1..524a1b1c9 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go index e784cb6db..d59b943ac 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go index bd4952efa..31e771d53 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go index 597733813..9fd77c6cb 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go index 16af29189..af10af28c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go index f59b18a97..cc2028af4 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go index 721ef5910..c06dd4415 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go index 01c43a01f..9ddbf3e08 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go index f258cfa24..19a6ee413 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go index 07919e0ec..05192a782 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go index 073daad43..b2e308581 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go index 7a8161c1d..3e6d57cae 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && aix -// +build ppc,aix package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go index 07ed733c5..3a219bdce 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && aix -// +build ppc64,aix package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 690cefc3d..091d107f3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index 5bffc10ea..28ff4ef74 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go index d0ba8e9b8..30e405bb4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 29dc48337..6cbd094a3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 0a89b2890..7c03b6ee7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index c8666bb15..422107ee8 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 88fb48a88..505a12acf 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go index 698dc975e..cc986c790 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 18aa70b42..eff6bcdef 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -175,7 +174,8 @@ type FscryptPolicyV2 struct { Contents_encryption_mode uint8 Filenames_encryption_mode uint8 Flags uint8 - _ [4]uint8 + Log2_data_unit_size uint8 + _ [3]uint8 Master_key_identifier [16]uint8 } @@ -456,60 +456,63 @@ type Ucred struct { } type TCPInfo struct { - State uint8 - Ca_state uint8 - Retransmits uint8 - Probes uint8 - Backoff uint8 - Options uint8 - Rto uint32 - Ato uint32 - Snd_mss uint32 - Rcv_mss uint32 - Unacked uint32 - Sacked uint32 - Lost uint32 - Retrans uint32 - Fackets uint32 - Last_data_sent uint32 - Last_ack_sent uint32 - Last_data_recv uint32 - Last_ack_recv uint32 - Pmtu uint32 - Rcv_ssthresh uint32 - Rtt uint32 - Rttvar uint32 - Snd_ssthresh uint32 - Snd_cwnd uint32 - Advmss uint32 - Reordering uint32 - Rcv_rtt uint32 - Rcv_space uint32 - Total_retrans uint32 - Pacing_rate uint64 - Max_pacing_rate uint64 - Bytes_acked uint64 - Bytes_received uint64 - Segs_out uint32 - Segs_in uint32 - Notsent_bytes uint32 - Min_rtt uint32 - Data_segs_in uint32 - Data_segs_out uint32 - Delivery_rate uint64 - Busy_time uint64 - Rwnd_limited uint64 - Sndbuf_limited uint64 - Delivered uint32 - Delivered_ce uint32 - Bytes_sent uint64 - Bytes_retrans uint64 - Dsack_dups uint32 - Reord_seen uint32 - Rcv_ooopack uint32 - Snd_wnd uint32 - Rcv_wnd uint32 - Rehash uint32 + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 + Pacing_rate uint64 + Max_pacing_rate uint64 + Bytes_acked uint64 + Bytes_received uint64 + Segs_out uint32 + Segs_in uint32 + Notsent_bytes uint32 + Min_rtt uint32 + Data_segs_in uint32 + Data_segs_out uint32 + Delivery_rate uint64 + Busy_time uint64 + Rwnd_limited uint64 + Sndbuf_limited uint64 + Delivered uint32 + Delivered_ce uint32 + Bytes_sent uint64 + Bytes_retrans uint64 + Dsack_dups uint32 + Reord_seen uint32 + Rcv_ooopack uint32 + Snd_wnd uint32 + Rcv_wnd uint32 + Rehash uint32 + Total_rto uint16 + Total_rto_recoveries uint16 + Total_rto_time uint32 } type CanFilter struct { @@ -552,7 +555,7 @@ const ( SizeofIPv6MTUInfo = 0x20 SizeofICMPv6Filter = 0x20 SizeofUcred = 0xc - SizeofTCPInfo = 0xf0 + SizeofTCPInfo = 0xf8 SizeofCanFilter = 0x8 SizeofTCPRepairOpt = 0x8 ) @@ -833,6 +836,15 @@ const ( FSPICK_EMPTY_PATH = 0x8 FSMOUNT_CLOEXEC = 0x1 + + FSCONFIG_SET_FLAG = 0x0 + FSCONFIG_SET_STRING = 0x1 + FSCONFIG_SET_BINARY = 0x2 + FSCONFIG_SET_PATH = 0x3 + FSCONFIG_SET_PATH_EMPTY = 0x4 + FSCONFIG_SET_FD = 0x5 + FSCONFIG_CMD_CREATE = 0x6 + FSCONFIG_CMD_RECONFIGURE = 0x7 ) type OpenHow struct { @@ -1547,6 +1559,7 @@ const ( IFLA_DEVLINK_PORT = 0x3e IFLA_GSO_IPV4_MAX_SIZE = 0x3f IFLA_GRO_IPV4_MAX_SIZE = 0x40 + IFLA_DPLL_PIN = 0x41 IFLA_PROTO_DOWN_REASON_UNSPEC = 0x0 IFLA_PROTO_DOWN_REASON_MASK = 0x1 IFLA_PROTO_DOWN_REASON_VALUE = 0x2 @@ -1562,6 +1575,7 @@ const ( IFLA_INET6_ICMP6STATS = 0x6 IFLA_INET6_TOKEN = 0x7 IFLA_INET6_ADDR_GEN_MODE = 0x8 + IFLA_INET6_RA_MTU = 0x9 IFLA_BR_UNSPEC = 0x0 IFLA_BR_FORWARD_DELAY = 0x1 IFLA_BR_HELLO_TIME = 0x2 @@ -1609,6 +1623,9 @@ const ( IFLA_BR_MCAST_MLD_VERSION = 0x2c IFLA_BR_VLAN_STATS_PER_PORT = 0x2d IFLA_BR_MULTI_BOOLOPT = 0x2e + IFLA_BR_MCAST_QUERIER_STATE = 0x2f + IFLA_BR_FDB_N_LEARNED = 0x30 + IFLA_BR_FDB_MAX_LEARNED = 0x31 IFLA_BRPORT_UNSPEC = 0x0 IFLA_BRPORT_STATE = 0x1 IFLA_BRPORT_PRIORITY = 0x2 @@ -1646,6 +1663,14 @@ const ( IFLA_BRPORT_BACKUP_PORT = 0x22 IFLA_BRPORT_MRP_RING_OPEN = 0x23 IFLA_BRPORT_MRP_IN_OPEN = 0x24 + IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT = 0x25 + IFLA_BRPORT_MCAST_EHT_HOSTS_CNT = 0x26 + IFLA_BRPORT_LOCKED = 0x27 + IFLA_BRPORT_MAB = 0x28 + IFLA_BRPORT_MCAST_N_GROUPS = 0x29 + IFLA_BRPORT_MCAST_MAX_GROUPS = 0x2a + IFLA_BRPORT_NEIGH_VLAN_SUPPRESS = 0x2b + IFLA_BRPORT_BACKUP_NHID = 0x2c IFLA_INFO_UNSPEC = 0x0 IFLA_INFO_KIND = 0x1 IFLA_INFO_DATA = 0x2 @@ -1667,6 +1692,9 @@ const ( IFLA_MACVLAN_MACADDR = 0x4 IFLA_MACVLAN_MACADDR_DATA = 0x5 IFLA_MACVLAN_MACADDR_COUNT = 0x6 + IFLA_MACVLAN_BC_QUEUE_LEN = 0x7 + IFLA_MACVLAN_BC_QUEUE_LEN_USED = 0x8 + IFLA_MACVLAN_BC_CUTOFF = 0x9 IFLA_VRF_UNSPEC = 0x0 IFLA_VRF_TABLE = 0x1 IFLA_VRF_PORT_UNSPEC = 0x0 @@ -1690,9 +1718,22 @@ const ( IFLA_XFRM_UNSPEC = 0x0 IFLA_XFRM_LINK = 0x1 IFLA_XFRM_IF_ID = 0x2 + IFLA_XFRM_COLLECT_METADATA = 0x3 IFLA_IPVLAN_UNSPEC = 0x0 IFLA_IPVLAN_MODE = 0x1 IFLA_IPVLAN_FLAGS = 0x2 + NETKIT_NEXT = -0x1 + NETKIT_PASS = 0x0 + NETKIT_DROP = 0x2 + NETKIT_REDIRECT = 0x7 + NETKIT_L2 = 0x0 + NETKIT_L3 = 0x1 + IFLA_NETKIT_UNSPEC = 0x0 + IFLA_NETKIT_PEER_INFO = 0x1 + IFLA_NETKIT_PRIMARY = 0x2 + IFLA_NETKIT_POLICY = 0x3 + IFLA_NETKIT_PEER_POLICY = 0x4 + IFLA_NETKIT_MODE = 0x5 IFLA_VXLAN_UNSPEC = 0x0 IFLA_VXLAN_ID = 0x1 IFLA_VXLAN_GROUP = 0x2 @@ -1723,6 +1764,8 @@ const ( IFLA_VXLAN_GPE = 0x1b IFLA_VXLAN_TTL_INHERIT = 0x1c IFLA_VXLAN_DF = 0x1d + IFLA_VXLAN_VNIFILTER = 0x1e + IFLA_VXLAN_LOCALBYPASS = 0x1f IFLA_GENEVE_UNSPEC = 0x0 IFLA_GENEVE_ID = 0x1 IFLA_GENEVE_REMOTE = 0x2 @@ -1737,6 +1780,7 @@ const ( IFLA_GENEVE_LABEL = 0xb IFLA_GENEVE_TTL_INHERIT = 0xc IFLA_GENEVE_DF = 0xd + IFLA_GENEVE_INNER_PROTO_INHERIT = 0xe IFLA_BAREUDP_UNSPEC = 0x0 IFLA_BAREUDP_PORT = 0x1 IFLA_BAREUDP_ETHERTYPE = 0x2 @@ -1749,6 +1793,8 @@ const ( IFLA_GTP_FD1 = 0x2 IFLA_GTP_PDP_HASHSIZE = 0x3 IFLA_GTP_ROLE = 0x4 + IFLA_GTP_CREATE_SOCKETS = 0x5 + IFLA_GTP_RESTART_COUNT = 0x6 IFLA_BOND_UNSPEC = 0x0 IFLA_BOND_MODE = 0x1 IFLA_BOND_ACTIVE_SLAVE = 0x2 @@ -1778,6 +1824,9 @@ const ( IFLA_BOND_AD_ACTOR_SYSTEM = 0x1a IFLA_BOND_TLB_DYNAMIC_LB = 0x1b IFLA_BOND_PEER_NOTIF_DELAY = 0x1c + IFLA_BOND_AD_LACP_ACTIVE = 0x1d + IFLA_BOND_MISSED_MAX = 0x1e + IFLA_BOND_NS_IP6_TARGET = 0x1f IFLA_BOND_AD_INFO_UNSPEC = 0x0 IFLA_BOND_AD_INFO_AGGREGATOR = 0x1 IFLA_BOND_AD_INFO_NUM_PORTS = 0x2 @@ -1793,6 +1842,7 @@ const ( IFLA_BOND_SLAVE_AD_AGGREGATOR_ID = 0x6 IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE = 0x7 IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE = 0x8 + IFLA_BOND_SLAVE_PRIO = 0x9 IFLA_VF_INFO_UNSPEC = 0x0 IFLA_VF_INFO = 0x1 IFLA_VF_UNSPEC = 0x0 @@ -1851,8 +1901,16 @@ const ( IFLA_STATS_LINK_XSTATS_SLAVE = 0x3 IFLA_STATS_LINK_OFFLOAD_XSTATS = 0x4 IFLA_STATS_AF_SPEC = 0x5 + IFLA_STATS_GETSET_UNSPEC = 0x0 + IFLA_STATS_GET_FILTERS = 0x1 + IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS = 0x2 IFLA_OFFLOAD_XSTATS_UNSPEC = 0x0 IFLA_OFFLOAD_XSTATS_CPU_HIT = 0x1 + IFLA_OFFLOAD_XSTATS_HW_S_INFO = 0x2 + IFLA_OFFLOAD_XSTATS_L3_STATS = 0x3 + IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC = 0x0 + IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST = 0x1 + IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED = 0x2 IFLA_XDP_UNSPEC = 0x0 IFLA_XDP_FD = 0x1 IFLA_XDP_ATTACHED = 0x2 @@ -1882,6 +1940,11 @@ const ( IFLA_RMNET_UNSPEC = 0x0 IFLA_RMNET_MUX_ID = 0x1 IFLA_RMNET_FLAGS = 0x2 + IFLA_MCTP_UNSPEC = 0x0 + IFLA_MCTP_NET = 0x1 + IFLA_DSA_UNSPEC = 0x0 + IFLA_DSA_CONDUIT = 0x1 + IFLA_DSA_MASTER = 0x1 ) const ( @@ -2672,6 +2735,7 @@ const ( BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e BPF_PROG_TYPE_SYSCALL = 0x1f + BPF_PROG_TYPE_NETFILTER = 0x20 BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2716,6 +2780,11 @@ const ( BPF_PERF_EVENT = 0x29 BPF_TRACE_KPROBE_MULTI = 0x2a BPF_LSM_CGROUP = 0x2b + BPF_STRUCT_OPS = 0x2c + BPF_NETFILTER = 0x2d + BPF_TCX_INGRESS = 0x2e + BPF_TCX_EGRESS = 0x2f + BPF_TRACE_UPROBE_MULTI = 0x30 BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2726,6 +2795,18 @@ const ( BPF_LINK_TYPE_PERF_EVENT = 0x7 BPF_LINK_TYPE_KPROBE_MULTI = 0x8 BPF_LINK_TYPE_STRUCT_OPS = 0x9 + BPF_LINK_TYPE_NETFILTER = 0xa + BPF_LINK_TYPE_TCX = 0xb + BPF_LINK_TYPE_UPROBE_MULTI = 0xc + BPF_PERF_EVENT_UNSPEC = 0x0 + BPF_PERF_EVENT_UPROBE = 0x1 + BPF_PERF_EVENT_URETPROBE = 0x2 + BPF_PERF_EVENT_KPROBE = 0x3 + BPF_PERF_EVENT_KRETPROBE = 0x4 + BPF_PERF_EVENT_TRACEPOINT = 0x5 + BPF_PERF_EVENT_EVENT = 0x6 + BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_UPROBE_MULTI_RETURN = 0x1 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2743,6 +2824,8 @@ const ( BPF_F_MMAPABLE = 0x400 BPF_F_PRESERVE_ELEMS = 0x800 BPF_F_INNER_MAP = 0x1000 + BPF_F_LINK = 0x2000 + BPF_F_PATH_FD = 0x4000 BPF_STATS_RUN_TIME = 0x0 BPF_STACK_BUILD_ID_EMPTY = 0x0 BPF_STACK_BUILD_ID_VALID = 0x1 @@ -2763,6 +2846,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_NO_TUNNEL_KEY = 0x10 BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff @@ -2779,6 +2863,8 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 + BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 0x80 + BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 0x100 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2867,6 +2953,8 @@ const ( BPF_DEVCG_DEV_CHAR = 0x2 BPF_FIB_LOOKUP_DIRECT = 0x1 BPF_FIB_LOOKUP_OUTPUT = 0x2 + BPF_FIB_LOOKUP_SKIP_NEIGH = 0x4 + BPF_FIB_LOOKUP_TBID = 0x8 BPF_FIB_LKUP_RET_SUCCESS = 0x0 BPF_FIB_LKUP_RET_BLACKHOLE = 0x1 BPF_FIB_LKUP_RET_UNREACHABLE = 0x2 @@ -2902,6 +2990,7 @@ const ( BPF_CORE_ENUMVAL_EXISTS = 0xa BPF_CORE_ENUMVAL_VALUE = 0xb BPF_CORE_TYPE_MATCHES = 0xc + BPF_F_TIMER_ABS = 0x1 ) const ( @@ -2980,6 +3069,12 @@ type LoopInfo64 struct { Encrypt_key [32]uint8 Init [2]uint64 } +type LoopConfig struct { + Fd uint32 + Size uint32 + Info LoopInfo64 + _ [8]uint64 +} type TIPCSocketAddr struct { Ref uint32 @@ -3368,7 +3463,7 @@ const ( DEVLINK_PORT_FN_ATTR_STATE = 0x2 DEVLINK_PORT_FN_ATTR_OPSTATE = 0x3 DEVLINK_PORT_FN_ATTR_CAPS = 0x4 - DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x4 + DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x5 ) type FsverityDigest struct { @@ -4152,7 +4247,8 @@ const ( ) type LandlockRulesetAttr struct { - Access_fs uint64 + Access_fs uint64 + Access_net uint64 } type LandlockPathBeneathAttr struct { @@ -5103,7 +5199,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x1b + NL80211_FREQUENCY_ATTR_MAX = 0x1c NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc @@ -5516,7 +5612,7 @@ const ( NL80211_REGDOM_TYPE_CUSTOM_WORLD = 0x2 NL80211_REGDOM_TYPE_INTERSECTION = 0x3 NL80211_REGDOM_TYPE_WORLD = 0x1 - NL80211_REG_RULE_ATTR_MAX = 0x7 + NL80211_REG_RULE_ATTR_MAX = 0x8 NL80211_REKEY_DATA_AKM = 0x4 NL80211_REKEY_DATA_KCK = 0x2 NL80211_REKEY_DATA_KEK = 0x1 @@ -5883,3 +5979,15 @@ type SchedAttr struct { } const SizeofSchedAttr = 0x38 + +type Cachestat_t struct { + Cache uint64 + Dirty uint64 + Writeback uint64 + Evicted uint64 + Recently_evicted uint64 +} +type CachestatRange struct { + Off uint64 + Len uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 6d8acbcc5..438a30aff 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index 59293c688..adceca355 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 40cfa38c2..eeaa00a37 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 055bc4216..6739aa91d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go index f28affbc6..9920ef631 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 9d71e7ccd..2923b799a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index fd5ccd332..ce2750ee4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 7704de77a..3038811d7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index df00b8757..efc6fed18 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index 0942840db..9a654b75a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 034874395..40d358e33 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index bad067047..148c6ceb8 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 1b4c97c32..72ba81543 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index aa268d025..71e765508 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 444045b6c..4abbdb9de 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go index 9bc4c8f9d..f22e7947d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go index bb05f655d..066a7d83d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go index db40e3a19..439548ec9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go index 11121151c..16085d3bb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go index 26eba23b7..afd13a3af 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go index 5a5479886..5d97f1f9b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go index be58c4e1f..34871cdc1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go index 52338266c..5911bceb3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go index 605cfdb12..e4f24f3bc 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go index d6724c010..ca50a7930 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go index ddfd27a43..d7d7f7902 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go index 0400747c6..14160576d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && solaris -// +build amd64,solaris package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go index aec1efcb3..54f31be63 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Hand edited based on ztypes_linux_s390x.go // TODO: auto-generate. diff --git a/vendor/golang.org/x/sys/windows/aliases.go b/vendor/golang.org/x/sys/windows/aliases.go index a20ebea63..ce2d713d6 100644 --- a/vendor/golang.org/x/sys/windows/aliases.go +++ b/vendor/golang.org/x/sys/windows/aliases.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && go1.9 -// +build windows,go1.9 package windows diff --git a/vendor/golang.org/x/sys/windows/empty.s b/vendor/golang.org/x/sys/windows/empty.s index fdbbbcd31..ba64caca5 100644 --- a/vendor/golang.org/x/sys/windows/empty.s +++ b/vendor/golang.org/x/sys/windows/empty.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.12 -// +build !go1.12 // This file is here to allow bodyless functions with go:linkname for Go 1.11 // and earlier (see https://golang.org/issue/23311). diff --git a/vendor/golang.org/x/sys/windows/env_windows.go b/vendor/golang.org/x/sys/windows/env_windows.go index b8ad19250..d4577a423 100644 --- a/vendor/golang.org/x/sys/windows/env_windows.go +++ b/vendor/golang.org/x/sys/windows/env_windows.go @@ -37,14 +37,17 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) { return nil, err } defer DestroyEnvironmentBlock(block) - blockp := unsafe.Pointer(block) - for { - entry := UTF16PtrToString((*uint16)(blockp)) - if len(entry) == 0 { - break + size := unsafe.Sizeof(*block) + for *block != 0 { + // find NUL terminator + end := unsafe.Pointer(block) + for *(*uint16)(end) != 0 { + end = unsafe.Add(end, size) } - env = append(env, entry) - blockp = unsafe.Add(blockp, 2*(len(entry)+1)) + + entry := unsafe.Slice(block, (uintptr(end)-uintptr(unsafe.Pointer(block)))/size) + env = append(env, UTF16ToString(entry)) + block = (*uint16)(unsafe.Add(end, size)) } return env, nil } diff --git a/vendor/golang.org/x/sys/windows/eventlog.go b/vendor/golang.org/x/sys/windows/eventlog.go index 2cd60645e..6c366955d 100644 --- a/vendor/golang.org/x/sys/windows/eventlog.go +++ b/vendor/golang.org/x/sys/windows/eventlog.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/mksyscall.go b/vendor/golang.org/x/sys/windows/mksyscall.go index 8563f79c5..dbcdb090c 100644 --- a/vendor/golang.org/x/sys/windows/mksyscall.go +++ b/vendor/golang.org/x/sys/windows/mksyscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build generate -// +build generate package windows diff --git a/vendor/golang.org/x/sys/windows/race.go b/vendor/golang.org/x/sys/windows/race.go index 9196b089c..0f1bdc386 100644 --- a/vendor/golang.org/x/sys/windows/race.go +++ b/vendor/golang.org/x/sys/windows/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && race -// +build windows,race package windows diff --git a/vendor/golang.org/x/sys/windows/race0.go b/vendor/golang.org/x/sys/windows/race0.go index 7bae4817a..0c78da78b 100644 --- a/vendor/golang.org/x/sys/windows/race0.go +++ b/vendor/golang.org/x/sys/windows/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && !race -// +build windows,!race package windows diff --git a/vendor/golang.org/x/sys/windows/registry/key.go b/vendor/golang.org/x/sys/windows/registry/key.go index 6c8d97b6a..fd8632444 100644 --- a/vendor/golang.org/x/sys/windows/registry/key.go +++ b/vendor/golang.org/x/sys/windows/registry/key.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows // Package registry provides access to the Windows registry. // diff --git a/vendor/golang.org/x/sys/windows/registry/mksyscall.go b/vendor/golang.org/x/sys/windows/registry/mksyscall.go index ee74927d3..bbf86ccf0 100644 --- a/vendor/golang.org/x/sys/windows/registry/mksyscall.go +++ b/vendor/golang.org/x/sys/windows/registry/mksyscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build generate -// +build generate package registry diff --git a/vendor/golang.org/x/sys/windows/registry/syscall.go b/vendor/golang.org/x/sys/windows/registry/syscall.go index 417335123..f533091c1 100644 --- a/vendor/golang.org/x/sys/windows/registry/syscall.go +++ b/vendor/golang.org/x/sys/windows/registry/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package registry diff --git a/vendor/golang.org/x/sys/windows/registry/value.go b/vendor/golang.org/x/sys/windows/registry/value.go index 2789f6f18..74db26b94 100644 --- a/vendor/golang.org/x/sys/windows/registry/value.go +++ b/vendor/golang.org/x/sys/windows/registry/value.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package registry diff --git a/vendor/golang.org/x/sys/windows/service.go b/vendor/golang.org/x/sys/windows/service.go index c44a1b963..a9dc6308d 100644 --- a/vendor/golang.org/x/sys/windows/service.go +++ b/vendor/golang.org/x/sys/windows/service.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/str.go b/vendor/golang.org/x/sys/windows/str.go index 4fc01434e..6a4f9ce6a 100644 --- a/vendor/golang.org/x/sys/windows/str.go +++ b/vendor/golang.org/x/sys/windows/str.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/syscall.go b/vendor/golang.org/x/sys/windows/syscall.go index 8732cdb95..e85ed6b9c 100644 --- a/vendor/golang.org/x/sys/windows/syscall.go +++ b/vendor/golang.org/x/sys/windows/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows // Package windows contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 35cfc57ca..6525c62f3 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -125,8 +125,7 @@ func UTF16PtrToString(p *uint16) string { for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ { ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p)) } - - return string(utf16.Decode(unsafe.Slice(p, n))) + return UTF16ToString(unsafe.Slice(p, n)) } func Getpagesize() int { return 4096 } @@ -155,6 +154,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW //sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW //sys SetDefaultDllDirectories(directoryFlags uint32) (err error) +//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory +//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory //sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW //sys GetVersion() (ver uint32, err error) //sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW @@ -164,6 +165,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW //sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW //sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) +//sys DisconnectNamedPipe(pipe Handle) (err error) //sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) //sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState @@ -192,6 +194,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW //sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys SetEndOfFile(handle Handle) (err error) +//sys SetFileValidData(handle Handle, validDataLength int64) (err error) //sys GetSystemTimeAsFileTime(time *Filetime) //sys GetSystemTimePreciseAsFileTime(time *Filetime) //sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff] @@ -233,6 +236,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock //sys getTickCount64() (ms uint64) = kernel32.GetTickCount64 +//sys GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) //sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) //sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW //sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW @@ -345,8 +349,19 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost //sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32) //sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error) +//sys ClearCommBreak(handle Handle) (err error) +//sys ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) +//sys EscapeCommFunction(handle Handle, dwFunc uint32) (err error) +//sys GetCommState(handle Handle, lpDCB *DCB) (err error) +//sys GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) //sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) +//sys PurgeComm(handle Handle, dwFlags uint32) (err error) +//sys SetCommBreak(handle Handle) (err error) +//sys SetCommMask(handle Handle, dwEvtMask uint32) (err error) +//sys SetCommState(handle Handle, lpDCB *DCB) (err error) //sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) +//sys SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) +//sys WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) //sys GetActiveProcessorCount(groupNumber uint16) (ret uint32) //sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32) //sys EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) = user32.EnumWindows @@ -969,7 +984,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) { if n > 0 { sl += int32(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -1830,3 +1846,73 @@ func ResizePseudoConsole(pconsole Handle, size Coord) error { // accept arguments that can be casted to uintptr, and Coord can't. return resizePseudoConsole(pconsole, *((*uint32)(unsafe.Pointer(&size)))) } + +// DCB constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-dcb. +const ( + CBR_110 = 110 + CBR_300 = 300 + CBR_600 = 600 + CBR_1200 = 1200 + CBR_2400 = 2400 + CBR_4800 = 4800 + CBR_9600 = 9600 + CBR_14400 = 14400 + CBR_19200 = 19200 + CBR_38400 = 38400 + CBR_57600 = 57600 + CBR_115200 = 115200 + CBR_128000 = 128000 + CBR_256000 = 256000 + + DTR_CONTROL_DISABLE = 0x00000000 + DTR_CONTROL_ENABLE = 0x00000010 + DTR_CONTROL_HANDSHAKE = 0x00000020 + + RTS_CONTROL_DISABLE = 0x00000000 + RTS_CONTROL_ENABLE = 0x00001000 + RTS_CONTROL_HANDSHAKE = 0x00002000 + RTS_CONTROL_TOGGLE = 0x00003000 + + NOPARITY = 0 + ODDPARITY = 1 + EVENPARITY = 2 + MARKPARITY = 3 + SPACEPARITY = 4 + + ONESTOPBIT = 0 + ONE5STOPBITS = 1 + TWOSTOPBITS = 2 +) + +// EscapeCommFunction constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-escapecommfunction. +const ( + SETXOFF = 1 + SETXON = 2 + SETRTS = 3 + CLRRTS = 4 + SETDTR = 5 + CLRDTR = 6 + SETBREAK = 8 + CLRBREAK = 9 +) + +// PurgeComm constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-purgecomm. +const ( + PURGE_TXABORT = 0x0001 + PURGE_RXABORT = 0x0002 + PURGE_TXCLEAR = 0x0004 + PURGE_RXCLEAR = 0x0008 +) + +// SetCommMask constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask. +const ( + EV_RXCHAR = 0x0001 + EV_RXFLAG = 0x0002 + EV_TXEMPTY = 0x0004 + EV_CTS = 0x0008 + EV_DSR = 0x0010 + EV_RLSD = 0x0020 + EV_BREAK = 0x0040 + EV_ERR = 0x0080 + EV_RING = 0x0100 +) diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index b88dc7c85..d8cb71db0 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -1094,7 +1094,33 @@ const ( SOMAXCONN = 0x7fffffff - TCP_NODELAY = 1 + TCP_NODELAY = 1 + TCP_EXPEDITED_1122 = 2 + TCP_KEEPALIVE = 3 + TCP_MAXSEG = 4 + TCP_MAXRT = 5 + TCP_STDURG = 6 + TCP_NOURG = 7 + TCP_ATMARK = 8 + TCP_NOSYNRETRIES = 9 + TCP_TIMESTAMPS = 10 + TCP_OFFLOAD_PREFERENCE = 11 + TCP_CONGESTION_ALGORITHM = 12 + TCP_DELAY_FIN_ACK = 13 + TCP_MAXRTMS = 14 + TCP_FASTOPEN = 15 + TCP_KEEPCNT = 16 + TCP_KEEPIDLE = TCP_KEEPALIVE + TCP_KEEPINTVL = 17 + TCP_FAIL_CONNECT_ON_ICMP_ERROR = 18 + TCP_ICMP_ERROR_INFO = 19 + + UDP_NOCHECKSUM = 1 + UDP_SEND_MSG_SIZE = 2 + UDP_RECV_MAX_COALESCED_SIZE = 3 + UDP_CHECKSUM_COVERAGE = 20 + + UDP_COALESCED_INFO = 3 SHUT_RD = 0 SHUT_WR = 1 @@ -3354,3 +3380,27 @@ type BLOB struct { Size uint32 BlobData *byte } + +type ComStat struct { + Flags uint32 + CBInQue uint32 + CBOutQue uint32 +} + +type DCB struct { + DCBlength uint32 + BaudRate uint32 + Flags uint32 + wReserved uint16 + XonLim uint16 + XoffLim uint16 + ByteSize uint8 + Parity uint8 + StopBits uint8 + XonChar byte + XoffChar byte + ErrorChar byte + EofChar byte + EvtChar byte + wReserved1 uint16 +} diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 8b1688de4..5c6035ddf 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -184,9 +184,12 @@ var ( procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procClearCommBreak = modkernel32.NewProc("ClearCommBreak") + procClearCommError = modkernel32.NewProc("ClearCommError") procCloseHandle = modkernel32.NewProc("CloseHandle") procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole") procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") @@ -211,7 +214,9 @@ var ( procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList") procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW") procDeviceIoControl = modkernel32.NewProc("DeviceIoControl") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") procDuplicateHandle = modkernel32.NewProc("DuplicateHandle") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procExitProcess = modkernel32.NewProc("ExitProcess") procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW") procFindClose = modkernel32.NewProc("FindClose") @@ -235,6 +240,8 @@ var ( procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent") procGetACP = modkernel32.NewProc("GetACP") procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procGetCommState = modkernel32.NewProc("GetCommState") procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts") procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") @@ -253,6 +260,7 @@ var ( procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW") procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle") procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procGetFileTime = modkernel32.NewProc("GetFileTime") procGetFileType = modkernel32.NewProc("GetFileType") procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") @@ -320,6 +328,7 @@ var ( procProcess32NextW = modkernel32.NewProc("Process32NextW") procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId") procPulseEvent = modkernel32.NewProc("PulseEvent") + procPurgeComm = modkernel32.NewProc("PurgeComm") procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW") procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW") procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject") @@ -329,9 +338,13 @@ var ( procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") procReleaseMutex = modkernel32.NewProc("ReleaseMutex") procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") + procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory") procResetEvent = modkernel32.NewProc("ResetEvent") procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procResumeThread = modkernel32.NewProc("ResumeThread") + procSetCommBreak = modkernel32.NewProc("SetCommBreak") + procSetCommMask = modkernel32.NewProc("SetCommMask") + procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition") procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") @@ -347,6 +360,7 @@ var ( procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") procSetFilePointer = modkernel32.NewProc("SetFilePointer") procSetFileTime = modkernel32.NewProc("SetFileTime") + procSetFileValidData = modkernel32.NewProc("SetFileValidData") procSetHandleInformation = modkernel32.NewProc("SetHandleInformation") procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject") procSetNamedPipeHandleState = modkernel32.NewProc("SetNamedPipeHandleState") @@ -357,6 +371,7 @@ var ( procSetStdHandle = modkernel32.NewProc("SetStdHandle") procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW") procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW") + procSetupComm = modkernel32.NewProc("SetupComm") procSizeofResource = modkernel32.NewProc("SizeofResource") procSleepEx = modkernel32.NewProc("SleepEx") procTerminateJobObject = modkernel32.NewProc("TerminateJobObject") @@ -375,6 +390,7 @@ var ( procVirtualQueryEx = modkernel32.NewProc("VirtualQueryEx") procVirtualUnlock = modkernel32.NewProc("VirtualUnlock") procWTSGetActiveConsoleSessionId = modkernel32.NewProc("WTSGetActiveConsoleSessionId") + procWaitCommEvent = modkernel32.NewProc("WaitCommEvent") procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects") procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject") procWriteConsoleW = modkernel32.NewProc("WriteConsoleW") @@ -1604,6 +1620,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { return } +func AddDllDirectory(path *uint16) (cookie uintptr, err error) { + r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + cookie = uintptr(r0) + if cookie == 0 { + err = errnoErr(e1) + } + return +} + func AssignProcessToJobObject(job Handle, process Handle) (err error) { r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0) if r1 == 0 { @@ -1628,6 +1653,22 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) { return } +func ClearCommBreak(handle Handle) (err error) { + r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) { + r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func CloseHandle(handle Handle) (err error) { r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0) if r1 == 0 { @@ -1832,6 +1873,14 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff return } +func DisconnectNamedPipe(pipe Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(pipe), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) { var _p0 uint32 if bInheritHandle { @@ -1844,6 +1893,14 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP return } +func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) { + r1, _, e1 := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(dwFunc), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ExitProcess(exitcode uint32) { syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0) return @@ -2045,6 +2102,22 @@ func GetActiveProcessorCount(groupNumber uint16) (ret uint32) { return } +func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpModemStat)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetCommState(handle Handle, lpDCB *DCB) (err error) { + r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { @@ -2185,6 +2258,14 @@ func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte, return } +func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { + r1, _, e1 := syscall.Syscall6(procGetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetFileType(filehandle Handle) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0) n = uint32(r0) @@ -2789,6 +2870,14 @@ func PulseEvent(event Handle) (err error) { return } +func PurgeComm(handle Handle, dwFlags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(dwFlags), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max)) n = uint32(r0) @@ -2870,6 +2959,14 @@ func RemoveDirectory(path *uint16) (err error) { return } +func RemoveDllDirectory(cookie uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ResetEvent(event Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0) if r1 == 0 { @@ -2895,6 +2992,30 @@ func ResumeThread(thread Handle) (ret uint32, err error) { return } +func SetCommBreak(handle Handle) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func SetCommMask(handle Handle, dwEvtMask uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(dwEvtMask), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func SetCommState(handle Handle, lpDCB *DCB) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { @@ -3023,6 +3144,14 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim return } +func SetFileValidData(handle Handle, validDataLength int64) (err error) { + r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) { r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags)) if r1 == 0 { @@ -3108,6 +3237,14 @@ func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err erro return } +func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetupComm.Addr(), 3, uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) { r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0) size = uint32(r0) @@ -3254,6 +3391,14 @@ func WTSGetActiveConsoleSessionId() (sessionID uint32) { return } +func WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procWaitCommEvent.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMilliseconds uint32) (event uint32, err error) { var _p0 uint32 if waitAll { diff --git a/vendor/golang.org/x/term/term_unix.go b/vendor/golang.org/x/term/term_unix.go index 62c2b3f41..1ad0ddfe3 100644 --- a/vendor/golang.org/x/term/term_unix.go +++ b/vendor/golang.org/x/term/term_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package term diff --git a/vendor/golang.org/x/term/term_unix_bsd.go b/vendor/golang.org/x/term/term_unix_bsd.go index 853b3d698..9dbf54629 100644 --- a/vendor/golang.org/x/term/term_unix_bsd.go +++ b/vendor/golang.org/x/term/term_unix_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package term diff --git a/vendor/golang.org/x/term/term_unix_other.go b/vendor/golang.org/x/term/term_unix_other.go index 1e8955c93..1b36de799 100644 --- a/vendor/golang.org/x/term/term_unix_other.go +++ b/vendor/golang.org/x/term/term_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || solaris || zos -// +build aix linux solaris zos package term diff --git a/vendor/golang.org/x/term/term_unsupported.go b/vendor/golang.org/x/term/term_unsupported.go index f1df85065..3c409e588 100644 --- a/vendor/golang.org/x/term/term_unsupported.go +++ b/vendor/golang.org/x/term/term_unsupported.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9 -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9 package term diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go index 8a7392c4a..784bb8808 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 package bidirule diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go index bb0a92001..8e1e94395 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 package bidirule diff --git a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go index 42fa8d72c..d2bd71181 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go index 56a0e1ea2..f76bdca27 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go index baacf32b4..3aa2c3bdf 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go index ffadb7beb..a71375790 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go index 92cce5802..f15746f7d 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go index f517fdb20..c164d3791 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package bidi diff --git a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go index f5a078827..1af161c75 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go index cb7239c43..eb73ecc37 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go index 11b273300..276cb8d8c 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go index f65785e8a..0cceffd73 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go index e1858b879..b0819e42d 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go index 0175eae50..bf65457d9 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package norm diff --git a/vendor/golang.org/x/time/AUTHORS b/vendor/golang.org/x/time/AUTHORS deleted file mode 100644 index 15167cd74..000000000 --- a/vendor/golang.org/x/time/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/time/CONTRIBUTORS b/vendor/golang.org/x/time/CONTRIBUTORS deleted file mode 100644 index 1c4577e96..000000000 --- a/vendor/golang.org/x/time/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/golang.org/x/time/rate/rate.go index b0b982e9c..8f6c7f493 100644 --- a/vendor/golang.org/x/time/rate/rate.go +++ b/vendor/golang.org/x/time/rate/rate.go @@ -52,6 +52,8 @@ func Every(interval time.Duration) Limit { // or its associated context.Context is canceled. // // The methods AllowN, ReserveN, and WaitN consume n tokens. +// +// Limiter is safe for simultaneous use by multiple goroutines. type Limiter struct { mu sync.Mutex limit Limit @@ -80,6 +82,19 @@ func (lim *Limiter) Burst() int { return lim.burst } +// TokensAt returns the number of tokens available at time t. +func (lim *Limiter) TokensAt(t time.Time) float64 { + lim.mu.Lock() + _, tokens := lim.advance(t) // does not mutate lim + lim.mu.Unlock() + return tokens +} + +// Tokens returns the number of tokens available now. +func (lim *Limiter) Tokens() float64 { + return lim.TokensAt(time.Now()) +} + // NewLimiter returns a new Limiter that allows events up to rate r and permits // bursts of at most b tokens. func NewLimiter(r Limit, b int) *Limiter { @@ -89,16 +104,16 @@ func NewLimiter(r Limit, b int) *Limiter { } } -// Allow is shorthand for AllowN(time.Now(), 1). +// Allow reports whether an event may happen now. func (lim *Limiter) Allow() bool { return lim.AllowN(time.Now(), 1) } -// AllowN reports whether n events may happen at time now. +// AllowN reports whether n events may happen at time t. // Use this method if you intend to drop / skip events that exceed the rate limit. // Otherwise use Reserve or Wait. -func (lim *Limiter) AllowN(now time.Time, n int) bool { - return lim.reserveN(now, n, 0).ok +func (lim *Limiter) AllowN(t time.Time, n int) bool { + return lim.reserveN(t, n, 0).ok } // A Reservation holds information about events that are permitted by a Limiter to happen after a delay. @@ -125,17 +140,17 @@ func (r *Reservation) Delay() time.Duration { } // InfDuration is the duration returned by Delay when a Reservation is not OK. -const InfDuration = time.Duration(1<<63 - 1) +const InfDuration = time.Duration(math.MaxInt64) // DelayFrom returns the duration for which the reservation holder must wait // before taking the reserved action. Zero duration means act immediately. // InfDuration means the limiter cannot grant the tokens requested in this // Reservation within the maximum wait time. -func (r *Reservation) DelayFrom(now time.Time) time.Duration { +func (r *Reservation) DelayFrom(t time.Time) time.Duration { if !r.ok { return InfDuration } - delay := r.timeToAct.Sub(now) + delay := r.timeToAct.Sub(t) if delay < 0 { return 0 } @@ -150,7 +165,7 @@ func (r *Reservation) Cancel() { // CancelAt indicates that the reservation holder will not perform the reserved action // and reverses the effects of this Reservation on the rate limit as much as possible, // considering that other reservations may have already been made. -func (r *Reservation) CancelAt(now time.Time) { +func (r *Reservation) CancelAt(t time.Time) { if !r.ok { return } @@ -158,7 +173,7 @@ func (r *Reservation) CancelAt(now time.Time) { r.lim.mu.Lock() defer r.lim.mu.Unlock() - if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) { + if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(t) { return } @@ -170,18 +185,18 @@ func (r *Reservation) CancelAt(now time.Time) { return } // advance time to now - now, _, tokens := r.lim.advance(now) + t, tokens := r.lim.advance(t) // calculate new number of tokens tokens += restoreTokens if burst := float64(r.lim.burst); tokens > burst { tokens = burst } // update state - r.lim.last = now + r.lim.last = t r.lim.tokens = tokens if r.timeToAct == r.lim.lastEvent { prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens))) - if !prevEvent.Before(now) { + if !prevEvent.Before(t) { r.lim.lastEvent = prevEvent } } @@ -196,18 +211,20 @@ func (lim *Limiter) Reserve() *Reservation { // The Limiter takes this Reservation into account when allowing future events. // The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size. // Usage example: -// r := lim.ReserveN(time.Now(), 1) -// if !r.OK() { -// // Not allowed to act! Did you remember to set lim.burst to be > 0 ? -// return -// } -// time.Sleep(r.Delay()) -// Act() +// +// r := lim.ReserveN(time.Now(), 1) +// if !r.OK() { +// // Not allowed to act! Did you remember to set lim.burst to be > 0 ? +// return +// } +// time.Sleep(r.Delay()) +// Act() +// // Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events. // If you need to respect a deadline or cancel the delay, use Wait instead. // To drop or skip events exceeding rate limit, use Allow instead. -func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation { - r := lim.reserveN(now, n, InfDuration) +func (lim *Limiter) ReserveN(t time.Time, n int) *Reservation { + r := lim.reserveN(t, n, InfDuration) return &r } @@ -221,6 +238,18 @@ func (lim *Limiter) Wait(ctx context.Context) (err error) { // canceled, or the expected wait time exceeds the Context's Deadline. // The burst limit is ignored if the rate limit is Inf. func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { + // The test code calls lim.wait with a fake timer generator. + // This is the real timer generator. + newTimer := func(d time.Duration) (<-chan time.Time, func() bool, func()) { + timer := time.NewTimer(d) + return timer.C, timer.Stop, func() {} + } + + return lim.wait(ctx, n, time.Now(), newTimer) +} + +// wait is the internal implementation of WaitN. +func (lim *Limiter) wait(ctx context.Context, n int, t time.Time, newTimer func(d time.Duration) (<-chan time.Time, func() bool, func())) error { lim.mu.Lock() burst := lim.burst limit := lim.limit @@ -236,25 +265,25 @@ func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { default: } // Determine wait limit - now := time.Now() waitLimit := InfDuration if deadline, ok := ctx.Deadline(); ok { - waitLimit = deadline.Sub(now) + waitLimit = deadline.Sub(t) } // Reserve - r := lim.reserveN(now, n, waitLimit) + r := lim.reserveN(t, n, waitLimit) if !r.ok { return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n) } // Wait if necessary - delay := r.DelayFrom(now) + delay := r.DelayFrom(t) if delay == 0 { return nil } - t := time.NewTimer(delay) - defer t.Stop() + ch, stop, advance := newTimer(delay) + defer stop() + advance() // only has an effect when testing select { - case <-t.C: + case <-ch: // We can proceed. return nil case <-ctx.Done(): @@ -273,13 +302,13 @@ func (lim *Limiter) SetLimit(newLimit Limit) { // SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated // or underutilized by those which reserved (using Reserve or Wait) but did not yet act // before SetLimitAt was called. -func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) { +func (lim *Limiter) SetLimitAt(t time.Time, newLimit Limit) { lim.mu.Lock() defer lim.mu.Unlock() - now, _, tokens := lim.advance(now) + t, tokens := lim.advance(t) - lim.last = now + lim.last = t lim.tokens = tokens lim.limit = newLimit } @@ -290,13 +319,13 @@ func (lim *Limiter) SetBurst(newBurst int) { } // SetBurstAt sets a new burst size for the limiter. -func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) { +func (lim *Limiter) SetBurstAt(t time.Time, newBurst int) { lim.mu.Lock() defer lim.mu.Unlock() - now, _, tokens := lim.advance(now) + t, tokens := lim.advance(t) - lim.last = now + lim.last = t lim.tokens = tokens lim.burst = newBurst } @@ -304,7 +333,7 @@ func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) { // reserveN is a helper method for AllowN, ReserveN, and WaitN. // maxFutureReserve specifies the maximum reservation wait duration allowed. // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. -func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { +func (lim *Limiter) reserveN(t time.Time, n int, maxFutureReserve time.Duration) Reservation { lim.mu.Lock() defer lim.mu.Unlock() @@ -313,7 +342,7 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio ok: true, lim: lim, tokens: n, - timeToAct: now, + timeToAct: t, } } else if lim.limit == 0 { var ok bool @@ -325,11 +354,11 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio ok: ok, lim: lim, tokens: lim.burst, - timeToAct: now, + timeToAct: t, } } - now, last, tokens := lim.advance(now) + t, tokens := lim.advance(t) // Calculate the remaining number of tokens resulting from the request. tokens -= float64(n) @@ -351,16 +380,12 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio } if ok { r.tokens = n - r.timeToAct = now.Add(waitDuration) - } + r.timeToAct = t.Add(waitDuration) - // Update state - if ok { - lim.last = now + // Update state + lim.last = t lim.tokens = tokens lim.lastEvent = r.timeToAct - } else { - lim.last = last } return r @@ -369,20 +394,20 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio // advance calculates and returns an updated state for lim resulting from the passage of time. // lim is not changed. // advance requires that lim.mu is held. -func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { +func (lim *Limiter) advance(t time.Time) (newT time.Time, newTokens float64) { last := lim.last - if now.Before(last) { - last = now + if t.Before(last) { + last = t } // Calculate the new number of tokens, due to time that passed. - elapsed := now.Sub(last) + elapsed := t.Sub(last) delta := lim.limit.tokensFromDuration(elapsed) tokens := lim.tokens + delta if burst := float64(lim.burst); tokens > burst { tokens = burst } - return now, last, tokens + return t, tokens } // durationFromTokens is a unit conversion function from the number of tokens to the duration diff --git a/vendor/golang.org/x/time/rate/sometimes.go b/vendor/golang.org/x/time/rate/sometimes.go new file mode 100644 index 000000000..6ba99ddb6 --- /dev/null +++ b/vendor/golang.org/x/time/rate/sometimes.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rate + +import ( + "sync" + "time" +) + +// Sometimes will perform an action occasionally. The First, Every, and +// Interval fields govern the behavior of Do, which performs the action. +// A zero Sometimes value will perform an action exactly once. +// +// # Example: logging with rate limiting +// +// var sometimes = rate.Sometimes{First: 3, Interval: 10*time.Second} +// func Spammy() { +// sometimes.Do(func() { log.Info("here I am!") }) +// } +type Sometimes struct { + First int // if non-zero, the first N calls to Do will run f. + Every int // if non-zero, every Nth call to Do will run f. + Interval time.Duration // if non-zero and Interval has elapsed since f's last run, Do will run f. + + mu sync.Mutex + count int // number of Do calls + last time.Time // last time f was run +} + +// Do runs the function f as allowed by First, Every, and Interval. +// +// The model is a union (not intersection) of filters. The first call to Do +// always runs f. Subsequent calls to Do run f if allowed by First or Every or +// Interval. +// +// A non-zero First:N causes the first N Do(f) calls to run f. +// +// A non-zero Every:M causes every Mth Do(f) call, starting with the first, to +// run f. +// +// A non-zero Interval causes Do(f) to run f if Interval has elapsed since +// Do last ran f. +// +// Specifying multiple filters produces the union of these execution streams. +// For example, specifying both First:N and Every:M causes the first N Do(f) +// calls and every Mth Do(f) call, starting with the first, to run f. See +// Examples for more. +// +// If Do is called multiple times simultaneously, the calls will block and run +// serially. Therefore, Do is intended for lightweight operations. +// +// Because a call to Do may block until f returns, if f causes Do to be called, +// it will deadlock. +func (s *Sometimes) Do(f func()) { + s.mu.Lock() + defer s.mu.Unlock() + if s.count == 0 || + (s.First > 0 && s.count < s.First) || + (s.Every > 0 && s.count%s.Every == 0) || + (s.Interval > 0 && time.Since(s.last) >= s.Interval) { + f() + s.last = time.Now() + } + s.count++ +} diff --git a/vendor/golang.org/x/tools/cmd/stringer/stringer.go b/vendor/golang.org/x/tools/cmd/stringer/stringer.go new file mode 100644 index 000000000..2b19c93e8 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/stringer/stringer.go @@ -0,0 +1,660 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer +// interface. Given the name of a (signed or unsigned) integer type T that has constants +// defined, stringer will create a new self-contained Go source file implementing +// +// func (t T) String() string +// +// The file is created in the same package and directory as the package that defines T. +// It has helpful defaults designed for use with go generate. +// +// Stringer works best with constants that are consecutive values such as created using iota, +// but creates good code regardless. In the future it might also provide custom support for +// constant sets that are bit patterns. +// +// For example, given this snippet, +// +// package painkiller +// +// type Pill int +// +// const ( +// Placebo Pill = iota +// Aspirin +// Ibuprofen +// Paracetamol +// Acetaminophen = Paracetamol +// ) +// +// running this command +// +// stringer -type=Pill +// +// in the same directory will create the file pill_string.go, in package painkiller, +// containing a definition of +// +// func (Pill) String() string +// +// That method will translate the value of a Pill constant to the string representation +// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will +// print the string "Aspirin". +// +// Typically this process would be run using go generate, like this: +// +// //go:generate stringer -type=Pill +// +// If multiple constants have the same value, the lexically first matching name will +// be used (in the example, Acetaminophen will print as "Paracetamol"). +// +// With no arguments, it processes the package in the current directory. +// Otherwise, the arguments must name a single directory holding a Go package +// or a set of Go source files that represent a single Go package. +// +// The -type flag accepts a comma-separated list of types so a single run can +// generate methods for multiple types. The default output file is t_string.go, +// where t is the lower-cased name of the first type listed. It can be overridden +// with the -output flag. +// +// The -linecomment flag tells stringer to generate the text of any line comment, trimmed +// of leading spaces, instead of the constant name. For instance, if the constants above had a +// Pill prefix, one could write +// +// PillAspirin // Aspirin +// +// to suppress it in the output. +package main // import "golang.org/x/tools/cmd/stringer" + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/constant" + "go/format" + "go/token" + "go/types" + "log" + "os" + "path/filepath" + "sort" + "strings" + + "golang.org/x/tools/go/packages" +) + +var ( + typeNames = flag.String("type", "", "comma-separated list of type names; must be set") + output = flag.String("output", "", "output file name; default srcdir/_string.go") + trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") + linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") + buildTags = flag.String("tags", "", "comma-separated list of build tags to apply") +) + +// Usage is a replacement usage function for the flags package. +func Usage() { + fmt.Fprintf(os.Stderr, "Usage of stringer:\n") + fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n") + fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n") + fmt.Fprintf(os.Stderr, "For more information, see:\n") + fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n") + fmt.Fprintf(os.Stderr, "Flags:\n") + flag.PrintDefaults() +} + +func main() { + log.SetFlags(0) + log.SetPrefix("stringer: ") + flag.Usage = Usage + flag.Parse() + if len(*typeNames) == 0 { + flag.Usage() + os.Exit(2) + } + types := strings.Split(*typeNames, ",") + var tags []string + if len(*buildTags) > 0 { + tags = strings.Split(*buildTags, ",") + } + + // We accept either one directory or a list of files. Which do we have? + args := flag.Args() + if len(args) == 0 { + // Default: process whole package in current directory. + args = []string{"."} + } + + // Parse the package once. + var dir string + g := Generator{ + trimPrefix: *trimprefix, + lineComment: *linecomment, + } + // TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc). + if len(args) == 1 && isDirectory(args[0]) { + dir = args[0] + } else { + if len(tags) != 0 { + log.Fatal("-tags option applies only to directories, not when files are specified") + } + dir = filepath.Dir(args[0]) + } + + g.parsePackage(args, tags) + + // Print the header and package clause. + g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) + g.Printf("\n") + g.Printf("package %s", g.pkg.name) + g.Printf("\n") + g.Printf("import \"strconv\"\n") // Used by all methods. + + // Run generate for each type. + for _, typeName := range types { + g.generate(typeName) + } + + // Format the output. + src := g.format() + + // Write to file. + outputName := *output + if outputName == "" { + baseName := fmt.Sprintf("%s_string.go", types[0]) + outputName = filepath.Join(dir, strings.ToLower(baseName)) + } + err := os.WriteFile(outputName, src, 0644) + if err != nil { + log.Fatalf("writing output: %s", err) + } +} + +// isDirectory reports whether the named file is a directory. +func isDirectory(name string) bool { + info, err := os.Stat(name) + if err != nil { + log.Fatal(err) + } + return info.IsDir() +} + +// Generator holds the state of the analysis. Primarily used to buffer +// the output for format.Source. +type Generator struct { + buf bytes.Buffer // Accumulated output. + pkg *Package // Package we are scanning. + + trimPrefix string + lineComment bool + + logf func(format string, args ...interface{}) // test logging hook; nil when not testing +} + +func (g *Generator) Printf(format string, args ...interface{}) { + fmt.Fprintf(&g.buf, format, args...) +} + +// File holds a single parsed file and associated data. +type File struct { + pkg *Package // Package to which this file belongs. + file *ast.File // Parsed AST. + // These fields are reset for each type being generated. + typeName string // Name of the constant type. + values []Value // Accumulator for constant values of that type. + + trimPrefix string + lineComment bool +} + +type Package struct { + name string + defs map[*ast.Ident]types.Object + files []*File +} + +// parsePackage analyzes the single package constructed from the patterns and tags. +// parsePackage exits if there is an error. +func (g *Generator) parsePackage(patterns []string, tags []string) { + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax, + // TODO: Need to think about constants in test files. Maybe write type_string_test.go + // in a separate pass? For later. + Tests: false, + BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, + Logf: g.logf, + } + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + log.Fatal(err) + } + if len(pkgs) != 1 { + log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " ")) + } + g.addPackage(pkgs[0]) +} + +// addPackage adds a type checked Package and its syntax files to the generator. +func (g *Generator) addPackage(pkg *packages.Package) { + g.pkg = &Package{ + name: pkg.Name, + defs: pkg.TypesInfo.Defs, + files: make([]*File, len(pkg.Syntax)), + } + + for i, file := range pkg.Syntax { + g.pkg.files[i] = &File{ + file: file, + pkg: g.pkg, + trimPrefix: g.trimPrefix, + lineComment: g.lineComment, + } + } +} + +// generate produces the String method for the named type. +func (g *Generator) generate(typeName string) { + values := make([]Value, 0, 100) + for _, file := range g.pkg.files { + // Set the state for this run of the walker. + file.typeName = typeName + file.values = nil + if file.file != nil { + ast.Inspect(file.file, file.genDecl) + values = append(values, file.values...) + } + } + + if len(values) == 0 { + log.Fatalf("no values defined for type %s", typeName) + } + // Generate code that will fail if the constants change value. + g.Printf("func _() {\n") + g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n") + g.Printf("\t// Re-run the stringer command to generate them again.\n") + g.Printf("\tvar x [1]struct{}\n") + for _, v := range values { + g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str) + } + g.Printf("}\n") + runs := splitIntoRuns(values) + // The decision of which pattern to use depends on the number of + // runs in the numbers. If there's only one, it's easy. For more than + // one, there's a tradeoff between complexity and size of the data + // and code vs. the simplicity of a map. A map takes more space, + // but so does the code. The decision here (crossover at 10) is + // arbitrary, but considers that for large numbers of runs the cost + // of the linear scan in the switch might become important, and + // rather than use yet another algorithm such as binary search, + // we punt and use a map. In any case, the likelihood of a map + // being necessary for any realistic example other than bitmasks + // is very low. And bitmasks probably deserve their own analysis, + // to be done some other day. + switch { + case len(runs) == 1: + g.buildOneRun(runs, typeName) + case len(runs) <= 10: + g.buildMultipleRuns(runs, typeName) + default: + g.buildMap(runs, typeName) + } +} + +// splitIntoRuns breaks the values into runs of contiguous sequences. +// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}. +// The input slice is known to be non-empty. +func splitIntoRuns(values []Value) [][]Value { + // We use stable sort so the lexically first name is chosen for equal elements. + sort.Stable(byValue(values)) + // Remove duplicates. Stable sort has put the one we want to print first, + // so use that one. The String method won't care about which named constant + // was the argument, so the first name for the given value is the only one to keep. + // We need to do this because identical values would cause the switch or map + // to fail to compile. + j := 1 + for i := 1; i < len(values); i++ { + if values[i].value != values[i-1].value { + values[j] = values[i] + j++ + } + } + values = values[:j] + runs := make([][]Value, 0, 10) + for len(values) > 0 { + // One contiguous sequence per outer loop. + i := 1 + for i < len(values) && values[i].value == values[i-1].value+1 { + i++ + } + runs = append(runs, values[:i]) + values = values[i:] + } + return runs +} + +// format returns the gofmt-ed contents of the Generator's buffer. +func (g *Generator) format() []byte { + src, err := format.Source(g.buf.Bytes()) + if err != nil { + // Should never happen, but can arise when developing this code. + // The user can compile the output to see the error. + log.Printf("warning: internal error: invalid Go generated: %s", err) + log.Printf("warning: compile the package to analyze the error") + return g.buf.Bytes() + } + return src +} + +// Value represents a declared constant. +type Value struct { + originalName string // The name of the constant. + name string // The name with trimmed prefix. + // The value is stored as a bit pattern alone. The boolean tells us + // whether to interpret it as an int64 or a uint64; the only place + // this matters is when sorting. + // Much of the time the str field is all we need; it is printed + // by Value.String. + value uint64 // Will be converted to int64 when needed. + signed bool // Whether the constant is a signed type. + str string // The string representation given by the "go/constant" package. +} + +func (v *Value) String() string { + return v.str +} + +// byValue lets us sort the constants into increasing order. +// We take care in the Less method to sort in signed or unsigned order, +// as appropriate. +type byValue []Value + +func (b byValue) Len() int { return len(b) } +func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byValue) Less(i, j int) bool { + if b[i].signed { + return int64(b[i].value) < int64(b[j].value) + } + return b[i].value < b[j].value +} + +// genDecl processes one declaration clause. +func (f *File) genDecl(node ast.Node) bool { + decl, ok := node.(*ast.GenDecl) + if !ok || decl.Tok != token.CONST { + // We only care about const declarations. + return true + } + // The name of the type of the constants we are declaring. + // Can change if this is a multi-element declaration. + typ := "" + // Loop over the elements of the declaration. Each element is a ValueSpec: + // a list of names possibly followed by a type, possibly followed by values. + // If the type and value are both missing, we carry down the type (and value, + // but the "go/types" package takes care of that). + for _, spec := range decl.Specs { + vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. + if vspec.Type == nil && len(vspec.Values) > 0 { + // "X = 1". With no type but a value. If the constant is untyped, + // skip this vspec and reset the remembered type. + typ = "" + + // If this is a simple type conversion, remember the type. + // We don't mind if this is actually a call; a qualified call won't + // be matched (that will be SelectorExpr, not Ident), and only unusual + // situations will result in a function call that appears to be + // a type conversion. + ce, ok := vspec.Values[0].(*ast.CallExpr) + if !ok { + continue + } + id, ok := ce.Fun.(*ast.Ident) + if !ok { + continue + } + typ = id.Name + } + if vspec.Type != nil { + // "X T". We have a type. Remember it. + ident, ok := vspec.Type.(*ast.Ident) + if !ok { + continue + } + typ = ident.Name + } + if typ != f.typeName { + // This is not the type we're looking for. + continue + } + // We now have a list of names (from one line of source code) all being + // declared with the desired type. + // Grab their names and actual values and store them in f.values. + for _, name := range vspec.Names { + if name.Name == "_" { + continue + } + // This dance lets the type checker find the values for us. It's a + // bit tricky: look up the object declared by the name, find its + // types.Const, and extract its value. + obj, ok := f.pkg.defs[name] + if !ok { + log.Fatalf("no value for constant %s", name) + } + info := obj.Type().Underlying().(*types.Basic).Info() + if info&types.IsInteger == 0 { + log.Fatalf("can't handle non-integer constant type %s", typ) + } + value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. + if value.Kind() != constant.Int { + log.Fatalf("can't happen: constant is not an integer %s", name) + } + i64, isInt := constant.Int64Val(value) + u64, isUint := constant.Uint64Val(value) + if !isInt && !isUint { + log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) + } + if !isInt { + u64 = uint64(i64) + } + v := Value{ + originalName: name.Name, + value: u64, + signed: info&types.IsUnsigned == 0, + str: value.String(), + } + if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 { + v.name = strings.TrimSpace(c.Text()) + } else { + v.name = strings.TrimPrefix(v.originalName, f.trimPrefix) + } + f.values = append(f.values, v) + } + } + return false +} + +// Helpers + +// usize returns the number of bits of the smallest unsigned integer +// type that will hold n. Used to create the smallest possible slice of +// integers to use as indexes into the concatenated strings. +func usize(n int) int { + switch { + case n < 1<<8: + return 8 + case n < 1<<16: + return 16 + default: + // 2^32 is enough constants for anyone. + return 32 + } +} + +// declareIndexAndNameVars declares the index slices and concatenated names +// strings representing the runs of values. +func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { + var indexes, names []string + for i, run := range runs { + index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) + if len(run) != 1 { + indexes = append(indexes, index) + } + names = append(names, name) + } + g.Printf("const (\n") + for _, name := range names { + g.Printf("\t%s\n", name) + } + g.Printf(")\n\n") + + if len(indexes) > 0 { + g.Printf("var (") + for _, index := range indexes { + g.Printf("\t%s\n", index) + } + g.Printf(")\n\n") + } +} + +// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars +func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { + index, name := g.createIndexAndNameDecl(run, typeName, "") + g.Printf("const %s\n", name) + g.Printf("var %s\n", index) +} + +// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var". +func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { + b := new(bytes.Buffer) + indexes := make([]int, len(run)) + for i := range run { + b.WriteString(run[i].name) + indexes[i] = b.Len() + } + nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) + nameLen := b.Len() + b.Reset() + fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) + for i, v := range indexes { + if i > 0 { + fmt.Fprintf(b, ", ") + } + fmt.Fprintf(b, "%d", v) + } + fmt.Fprintf(b, "}") + return b.String(), nameConst +} + +// declareNameVars declares the concatenated names string representing all the values in the runs. +func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) { + g.Printf("const _%s_name%s = \"", typeName, suffix) + for _, run := range runs { + for i := range run { + g.Printf("%s", run[i].name) + } + } + g.Printf("\"\n") +} + +// buildOneRun generates the variables and String method for a single run of contiguous values. +func (g *Generator) buildOneRun(runs [][]Value, typeName string) { + values := runs[0] + g.Printf("\n") + g.declareIndexAndNameVar(values, typeName) + // The generated code is simple enough to write as a Printf format. + lessThanZero := "" + if values[0].signed { + lessThanZero = "i < 0 || " + } + if values[0].value == 0 { // Signed or unsigned, 0 is still 0. + g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) + } else { + g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) + } +} + +// Arguments to format are: +// +// [1]: type name +// [2]: size of index element (8 for uint8 etc.) +// [3]: less than zero check (for signed types) +const stringOneRun = `func (i %[1]s) String() string { + if %[3]si >= %[1]s(len(_%[1]s_index)-1) { + return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] +} +` + +// Arguments to format are: +// [1]: type name +// [2]: lowest defined value for type, as a string +// [3]: size of index element (8 for uint8 etc.) +// [4]: less than zero check (for signed types) +/* + */ +const stringOneRunWithOffset = `func (i %[1]s) String() string { + i -= %[2]s + if %[4]si >= %[1]s(len(_%[1]s_index)-1) { + return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")" + } + return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]] +} +` + +// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. +// For this pattern, a single Printf format won't do. +func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { + g.Printf("\n") + g.declareIndexAndNameVars(runs, typeName) + g.Printf("func (i %s) String() string {\n", typeName) + g.Printf("\tswitch {\n") + for i, values := range runs { + if len(values) == 1 { + g.Printf("\tcase i == %s:\n", &values[0]) + g.Printf("\t\treturn _%s_name_%d\n", typeName, i) + continue + } + if values[0].value == 0 && !values[0].signed { + // For an unsigned lower bound of 0, "0 <= i" would be redundant. + g.Printf("\tcase i <= %s:\n", &values[len(values)-1]) + } else { + g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) + } + if values[0].value != 0 { + g.Printf("\t\ti -= %s\n", &values[0]) + } + g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n", + typeName, i, typeName, i, typeName, i) + } + g.Printf("\tdefault:\n") + g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) + g.Printf("\t}\n") + g.Printf("}\n") +} + +// buildMap handles the case where the space is so sparse a map is a reasonable fallback. +// It's a rare situation but has simple code. +func (g *Generator) buildMap(runs [][]Value, typeName string) { + g.Printf("\n") + g.declareNameVars(runs, typeName, "") + g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) + n := 0 + for _, values := range runs { + for _, value := range values { + g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) + n += len(value.name) + } + } + g.Printf("}\n\n") + g.Printf(stringMap, typeName) +} + +// Argument to format is the type name. +const stringMap = `func (i %[1]s) String() string { + if str, ok := _%[1]s_map[i]; ok { + return str + } + return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" +} +` diff --git a/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go b/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go new file mode 100644 index 000000000..2c4c4e232 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go @@ -0,0 +1,634 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +// This file defines utilities for working with source positions. + +import ( + "fmt" + "go/ast" + "go/token" + "sort" +) + +// PathEnclosingInterval returns the node that encloses the source +// interval [start, end), and all its ancestors up to the AST root. +// +// The definition of "enclosing" used by this function considers +// additional whitespace abutting a node to be enclosed by it. +// In this example: +// +// z := x + y // add them +// <-A-> +// <----B-----> +// +// the ast.BinaryExpr(+) node is considered to enclose interval B +// even though its [Pos()..End()) is actually only interval A. +// This behaviour makes user interfaces more tolerant of imperfect +// input. +// +// This function treats tokens as nodes, though they are not included +// in the result. e.g. PathEnclosingInterval("+") returns the +// enclosing ast.BinaryExpr("x + y"). +// +// If start==end, the 1-char interval following start is used instead. +// +// The 'exact' result is true if the interval contains only path[0] +// and perhaps some adjacent whitespace. It is false if the interval +// overlaps multiple children of path[0], or if it contains only +// interior whitespace of path[0]. +// In this example: +// +// z := x + y // add them +// <--C--> <---E--> +// ^ +// D +// +// intervals C, D and E are inexact. C is contained by the +// z-assignment statement, because it spans three of its children (:=, +// x, +). So too is the 1-char interval D, because it contains only +// interior whitespace of the assignment. E is considered interior +// whitespace of the BlockStmt containing the assignment. +// +// The resulting path is never empty; it always contains at least the +// 'root' *ast.File. Ideally PathEnclosingInterval would reject +// intervals that lie wholly or partially outside the range of the +// file, but unfortunately ast.File records only the token.Pos of +// the 'package' keyword, but not of the start of the file itself. +func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) { + // fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging + + // Precondition: node.[Pos..End) and adjoining whitespace contain [start, end). + var visit func(node ast.Node) bool + visit = func(node ast.Node) bool { + path = append(path, node) + + nodePos := node.Pos() + nodeEnd := node.End() + + // fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging + + // Intersect [start, end) with interval of node. + if start < nodePos { + start = nodePos + } + if end > nodeEnd { + end = nodeEnd + } + + // Find sole child that contains [start, end). + children := childrenOf(node) + l := len(children) + for i, child := range children { + // [childPos, childEnd) is unaugmented interval of child. + childPos := child.Pos() + childEnd := child.End() + + // [augPos, augEnd) is whitespace-augmented interval of child. + augPos := childPos + augEnd := childEnd + if i > 0 { + augPos = children[i-1].End() // start of preceding whitespace + } + if i < l-1 { + nextChildPos := children[i+1].Pos() + // Does [start, end) lie between child and next child? + if start >= augEnd && end <= nextChildPos { + return false // inexact match + } + augEnd = nextChildPos // end of following whitespace + } + + // fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n", + // i, augPos, augEnd, start, end) // debugging + + // Does augmented child strictly contain [start, end)? + if augPos <= start && end <= augEnd { + _, isToken := child.(tokenNode) + return isToken || visit(child) + } + + // Does [start, end) overlap multiple children? + // i.e. left-augmented child contains start + // but LR-augmented child does not contain end. + if start < childEnd && end > augEnd { + break + } + } + + // No single child contained [start, end), + // so node is the result. Is it exact? + + // (It's tempting to put this condition before the + // child loop, but it gives the wrong result in the + // case where a node (e.g. ExprStmt) and its sole + // child have equal intervals.) + if start == nodePos && end == nodeEnd { + return true // exact match + } + + return false // inexact: overlaps multiple children + } + + // Ensure [start,end) is nondecreasing. + if start > end { + start, end = end, start + } + + if start < root.End() && end > root.Pos() { + if start == end { + end = start + 1 // empty interval => interval of size 1 + } + exact = visit(root) + + // Reverse the path: + for i, l := 0, len(path); i < l/2; i++ { + path[i], path[l-1-i] = path[l-1-i], path[i] + } + } else { + // Selection lies within whitespace preceding the + // first (or following the last) declaration in the file. + // The result nonetheless always includes the ast.File. + path = append(path, root) + } + + return +} + +// tokenNode is a dummy implementation of ast.Node for a single token. +// They are used transiently by PathEnclosingInterval but never escape +// this package. +type tokenNode struct { + pos token.Pos + end token.Pos +} + +func (n tokenNode) Pos() token.Pos { + return n.pos +} + +func (n tokenNode) End() token.Pos { + return n.end +} + +func tok(pos token.Pos, len int) ast.Node { + return tokenNode{pos, pos + token.Pos(len)} +} + +// childrenOf returns the direct non-nil children of ast.Node n. +// It may include fake ast.Node implementations for bare tokens. +// it is not safe to call (e.g.) ast.Walk on such nodes. +func childrenOf(n ast.Node) []ast.Node { + var children []ast.Node + + // First add nodes for all true subtrees. + ast.Inspect(n, func(node ast.Node) bool { + if node == n { // push n + return true // recur + } + if node != nil { // push child + children = append(children, node) + } + return false // no recursion + }) + + // Then add fake Nodes for bare tokens. + switch n := n.(type) { + case *ast.ArrayType: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Elt.End(), len("]"))) + + case *ast.AssignStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.BasicLit: + children = append(children, + tok(n.ValuePos, len(n.Value))) + + case *ast.BinaryExpr: + children = append(children, tok(n.OpPos, len(n.Op.String()))) + + case *ast.BlockStmt: + children = append(children, + tok(n.Lbrace, len("{")), + tok(n.Rbrace, len("}"))) + + case *ast.BranchStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.CallExpr: + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + if n.Ellipsis != 0 { + children = append(children, tok(n.Ellipsis, len("..."))) + } + + case *ast.CaseClause: + if n.List == nil { + children = append(children, + tok(n.Case, len("default"))) + } else { + children = append(children, + tok(n.Case, len("case"))) + } + children = append(children, tok(n.Colon, len(":"))) + + case *ast.ChanType: + switch n.Dir { + case ast.RECV: + children = append(children, tok(n.Begin, len("<-chan"))) + case ast.SEND: + children = append(children, tok(n.Begin, len("chan<-"))) + case ast.RECV | ast.SEND: + children = append(children, tok(n.Begin, len("chan"))) + } + + case *ast.CommClause: + if n.Comm == nil { + children = append(children, + tok(n.Case, len("default"))) + } else { + children = append(children, + tok(n.Case, len("case"))) + } + children = append(children, tok(n.Colon, len(":"))) + + case *ast.Comment: + // nop + + case *ast.CommentGroup: + // nop + + case *ast.CompositeLit: + children = append(children, + tok(n.Lbrace, len("{")), + tok(n.Rbrace, len("{"))) + + case *ast.DeclStmt: + // nop + + case *ast.DeferStmt: + children = append(children, + tok(n.Defer, len("defer"))) + + case *ast.Ellipsis: + children = append(children, + tok(n.Ellipsis, len("..."))) + + case *ast.EmptyStmt: + // nop + + case *ast.ExprStmt: + // nop + + case *ast.Field: + // TODO(adonovan): Field.{Doc,Comment,Tag}? + + case *ast.FieldList: + children = append(children, + tok(n.Opening, len("(")), // or len("[") + tok(n.Closing, len(")"))) // or len("]") + + case *ast.File: + // TODO test: Doc + children = append(children, + tok(n.Package, len("package"))) + + case *ast.ForStmt: + children = append(children, + tok(n.For, len("for"))) + + case *ast.FuncDecl: + // TODO(adonovan): FuncDecl.Comment? + + // Uniquely, FuncDecl breaks the invariant that + // preorder traversal yields tokens in lexical order: + // in fact, FuncDecl.Recv precedes FuncDecl.Type.Func. + // + // As a workaround, we inline the case for FuncType + // here and order things correctly. + // + children = nil // discard ast.Walk(FuncDecl) info subtrees + children = append(children, tok(n.Type.Func, len("func"))) + if n.Recv != nil { + children = append(children, n.Recv) + } + children = append(children, n.Name) + if tparams := n.Type.TypeParams; tparams != nil { + children = append(children, tparams) + } + if n.Type.Params != nil { + children = append(children, n.Type.Params) + } + if n.Type.Results != nil { + children = append(children, n.Type.Results) + } + if n.Body != nil { + children = append(children, n.Body) + } + + case *ast.FuncLit: + // nop + + case *ast.FuncType: + if n.Func != 0 { + children = append(children, + tok(n.Func, len("func"))) + } + + case *ast.GenDecl: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + if n.Lparen != 0 { + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + } + + case *ast.GoStmt: + children = append(children, + tok(n.Go, len("go"))) + + case *ast.Ident: + children = append(children, + tok(n.NamePos, len(n.Name))) + + case *ast.IfStmt: + children = append(children, + tok(n.If, len("if"))) + + case *ast.ImportSpec: + // TODO(adonovan): ImportSpec.{Doc,EndPos}? + + case *ast.IncDecStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.IndexExpr: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Rbrack, len("]"))) + + case *ast.IndexListExpr: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Rbrack, len("]"))) + + case *ast.InterfaceType: + children = append(children, + tok(n.Interface, len("interface"))) + + case *ast.KeyValueExpr: + children = append(children, + tok(n.Colon, len(":"))) + + case *ast.LabeledStmt: + children = append(children, + tok(n.Colon, len(":"))) + + case *ast.MapType: + children = append(children, + tok(n.Map, len("map"))) + + case *ast.ParenExpr: + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + + case *ast.RangeStmt: + children = append(children, + tok(n.For, len("for")), + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.ReturnStmt: + children = append(children, + tok(n.Return, len("return"))) + + case *ast.SelectStmt: + children = append(children, + tok(n.Select, len("select"))) + + case *ast.SelectorExpr: + // nop + + case *ast.SendStmt: + children = append(children, + tok(n.Arrow, len("<-"))) + + case *ast.SliceExpr: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Rbrack, len("]"))) + + case *ast.StarExpr: + children = append(children, tok(n.Star, len("*"))) + + case *ast.StructType: + children = append(children, tok(n.Struct, len("struct"))) + + case *ast.SwitchStmt: + children = append(children, tok(n.Switch, len("switch"))) + + case *ast.TypeAssertExpr: + children = append(children, + tok(n.Lparen-1, len(".")), + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + + case *ast.TypeSpec: + // TODO(adonovan): TypeSpec.{Doc,Comment}? + + case *ast.TypeSwitchStmt: + children = append(children, tok(n.Switch, len("switch"))) + + case *ast.UnaryExpr: + children = append(children, tok(n.OpPos, len(n.Op.String()))) + + case *ast.ValueSpec: + // TODO(adonovan): ValueSpec.{Doc,Comment}? + + case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt: + // nop + } + + // TODO(adonovan): opt: merge the logic of ast.Inspect() into + // the switch above so we can make interleaved callbacks for + // both Nodes and Tokens in the right order and avoid the need + // to sort. + sort.Sort(byPos(children)) + + return children +} + +type byPos []ast.Node + +func (sl byPos) Len() int { + return len(sl) +} +func (sl byPos) Less(i, j int) bool { + return sl[i].Pos() < sl[j].Pos() +} +func (sl byPos) Swap(i, j int) { + sl[i], sl[j] = sl[j], sl[i] +} + +// NodeDescription returns a description of the concrete type of n suitable +// for a user interface. +// +// TODO(adonovan): in some cases (e.g. Field, FieldList, Ident, +// StarExpr) we could be much more specific given the path to the AST +// root. Perhaps we should do that. +func NodeDescription(n ast.Node) string { + switch n := n.(type) { + case *ast.ArrayType: + return "array type" + case *ast.AssignStmt: + return "assignment" + case *ast.BadDecl: + return "bad declaration" + case *ast.BadExpr: + return "bad expression" + case *ast.BadStmt: + return "bad statement" + case *ast.BasicLit: + return "basic literal" + case *ast.BinaryExpr: + return fmt.Sprintf("binary %s operation", n.Op) + case *ast.BlockStmt: + return "block" + case *ast.BranchStmt: + switch n.Tok { + case token.BREAK: + return "break statement" + case token.CONTINUE: + return "continue statement" + case token.GOTO: + return "goto statement" + case token.FALLTHROUGH: + return "fall-through statement" + } + case *ast.CallExpr: + if len(n.Args) == 1 && !n.Ellipsis.IsValid() { + return "function call (or conversion)" + } + return "function call" + case *ast.CaseClause: + return "case clause" + case *ast.ChanType: + return "channel type" + case *ast.CommClause: + return "communication clause" + case *ast.Comment: + return "comment" + case *ast.CommentGroup: + return "comment group" + case *ast.CompositeLit: + return "composite literal" + case *ast.DeclStmt: + return NodeDescription(n.Decl) + " statement" + case *ast.DeferStmt: + return "defer statement" + case *ast.Ellipsis: + return "ellipsis" + case *ast.EmptyStmt: + return "empty statement" + case *ast.ExprStmt: + return "expression statement" + case *ast.Field: + // Can be any of these: + // struct {x, y int} -- struct field(s) + // struct {T} -- anon struct field + // interface {I} -- interface embedding + // interface {f()} -- interface method + // func (A) func(B) C -- receiver, param(s), result(s) + return "field/method/parameter" + case *ast.FieldList: + return "field/method/parameter list" + case *ast.File: + return "source file" + case *ast.ForStmt: + return "for loop" + case *ast.FuncDecl: + return "function declaration" + case *ast.FuncLit: + return "function literal" + case *ast.FuncType: + return "function type" + case *ast.GenDecl: + switch n.Tok { + case token.IMPORT: + return "import declaration" + case token.CONST: + return "constant declaration" + case token.TYPE: + return "type declaration" + case token.VAR: + return "variable declaration" + } + case *ast.GoStmt: + return "go statement" + case *ast.Ident: + return "identifier" + case *ast.IfStmt: + return "if statement" + case *ast.ImportSpec: + return "import specification" + case *ast.IncDecStmt: + if n.Tok == token.INC { + return "increment statement" + } + return "decrement statement" + case *ast.IndexExpr: + return "index expression" + case *ast.IndexListExpr: + return "index list expression" + case *ast.InterfaceType: + return "interface type" + case *ast.KeyValueExpr: + return "key/value association" + case *ast.LabeledStmt: + return "statement label" + case *ast.MapType: + return "map type" + case *ast.Package: + return "package" + case *ast.ParenExpr: + return "parenthesized " + NodeDescription(n.X) + case *ast.RangeStmt: + return "range loop" + case *ast.ReturnStmt: + return "return statement" + case *ast.SelectStmt: + return "select statement" + case *ast.SelectorExpr: + return "selector" + case *ast.SendStmt: + return "channel send" + case *ast.SliceExpr: + return "slice expression" + case *ast.StarExpr: + return "*-operation" // load/store expr or pointer type + case *ast.StructType: + return "struct type" + case *ast.SwitchStmt: + return "switch statement" + case *ast.TypeAssertExpr: + return "type assertion" + case *ast.TypeSpec: + return "type specification" + case *ast.TypeSwitchStmt: + return "type switch" + case *ast.UnaryExpr: + return fmt.Sprintf("unary %s operation", n.Op) + case *ast.ValueSpec: + return "value specification" + + } + panic(fmt.Sprintf("unexpected node type: %T", n)) +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/imports.go b/vendor/golang.org/x/tools/go/ast/astutil/imports.go new file mode 100644 index 000000000..18d1adb05 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/imports.go @@ -0,0 +1,485 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package astutil contains common utilities for working with the Go AST. +package astutil // import "golang.org/x/tools/go/ast/astutil" + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" + "strings" +) + +// AddImport adds the import path to the file f, if absent. +func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) { + return AddNamedImport(fset, f, "", path) +} + +// AddNamedImport adds the import with the given name and path to the file f, if absent. +// If name is not empty, it is used to rename the import. +// +// For example, calling +// +// AddNamedImport(fset, f, "pathpkg", "path") +// +// adds +// +// import pathpkg "path" +func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) { + if imports(f, name, path) { + return false + } + + newImport := &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(path), + }, + } + if name != "" { + newImport.Name = &ast.Ident{Name: name} + } + + // Find an import decl to add to. + // The goal is to find an existing import + // whose import path has the longest shared + // prefix with path. + var ( + bestMatch = -1 // length of longest shared prefix + lastImport = -1 // index in f.Decls of the file's final import decl + impDecl *ast.GenDecl // import decl containing the best match + impIndex = -1 // spec index in impDecl containing the best match + + isThirdPartyPath = isThirdParty(path) + ) + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if ok && gen.Tok == token.IMPORT { + lastImport = i + // Do not add to import "C", to avoid disrupting the + // association with its doc comment, breaking cgo. + if declImports(gen, "C") { + continue + } + + // Match an empty import decl if that's all that is available. + if len(gen.Specs) == 0 && bestMatch == -1 { + impDecl = gen + } + + // Compute longest shared prefix with imports in this group and find best + // matched import spec. + // 1. Always prefer import spec with longest shared prefix. + // 2. While match length is 0, + // - for stdlib package: prefer first import spec. + // - for third party package: prefer first third party import spec. + // We cannot use last import spec as best match for third party package + // because grouped imports are usually placed last by goimports -local + // flag. + // See issue #19190. + seenAnyThirdParty := false + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + p := importPath(impspec) + n := matchLen(p, path) + if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) { + bestMatch = n + impDecl = gen + impIndex = j + } + seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p) + } + } + } + + // If no import decl found, add one after the last import. + if impDecl == nil { + impDecl = &ast.GenDecl{ + Tok: token.IMPORT, + } + if lastImport >= 0 { + impDecl.TokPos = f.Decls[lastImport].End() + } else { + // There are no existing imports. + // Our new import, preceded by a blank line, goes after the package declaration + // and after the comment, if any, that starts on the same line as the + // package declaration. + impDecl.TokPos = f.Package + + file := fset.File(f.Package) + pkgLine := file.Line(f.Package) + for _, c := range f.Comments { + if file.Line(c.Pos()) > pkgLine { + break + } + // +2 for a blank line + impDecl.TokPos = c.End() + 2 + } + } + f.Decls = append(f.Decls, nil) + copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) + f.Decls[lastImport+1] = impDecl + } + + // Insert new import at insertAt. + insertAt := 0 + if impIndex >= 0 { + // insert after the found import + insertAt = impIndex + 1 + } + impDecl.Specs = append(impDecl.Specs, nil) + copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) + impDecl.Specs[insertAt] = newImport + pos := impDecl.Pos() + if insertAt > 0 { + // If there is a comment after an existing import, preserve the comment + // position by adding the new import after the comment. + if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil { + pos = spec.Comment.End() + } else { + // Assign same position as the previous import, + // so that the sorter sees it as being in the same block. + pos = impDecl.Specs[insertAt-1].Pos() + } + } + if newImport.Name != nil { + newImport.Name.NamePos = pos + } + newImport.Path.ValuePos = pos + newImport.EndPos = pos + + // Clean up parens. impDecl contains at least one spec. + if len(impDecl.Specs) == 1 { + // Remove unneeded parens. + impDecl.Lparen = token.NoPos + } else if !impDecl.Lparen.IsValid() { + // impDecl needs parens added. + impDecl.Lparen = impDecl.Specs[0].Pos() + } + + f.Imports = append(f.Imports, newImport) + + if len(f.Decls) <= 1 { + return true + } + + // Merge all the import declarations into the first one. + var first *ast.GenDecl + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { + continue + } + if first == nil { + first = gen + continue // Don't touch the first one. + } + // We now know there is more than one package in this import + // declaration. Ensure that it ends up parenthesized. + first.Lparen = first.Pos() + // Move the imports of the other import declaration to the first one. + for _, spec := range gen.Specs { + spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + first.Specs = append(first.Specs, spec) + } + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } + + return true +} + +func isThirdParty(importPath string) bool { + // Third party package import path usually contains "." (".com", ".org", ...) + // This logic is taken from golang.org/x/tools/imports package. + return strings.Contains(importPath, ".") +} + +// DeleteImport deletes the import path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. +func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) { + return DeleteNamedImport(fset, f, "", path) +} + +// DeleteNamedImport deletes the import with the given name and path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. +func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) { + var delspecs []*ast.ImportSpec + var delcomments []*ast.CommentGroup + + // Find the import nodes that import path, if any. + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT { + continue + } + for j := 0; j < len(gen.Specs); j++ { + spec := gen.Specs[j] + impspec := spec.(*ast.ImportSpec) + if importName(impspec) != name || importPath(impspec) != path { + continue + } + + // We found an import spec that imports path. + // Delete it. + delspecs = append(delspecs, impspec) + deleted = true + copy(gen.Specs[j:], gen.Specs[j+1:]) + gen.Specs = gen.Specs[:len(gen.Specs)-1] + + // If this was the last import spec in this decl, + // delete the decl, too. + if len(gen.Specs) == 0 { + copy(f.Decls[i:], f.Decls[i+1:]) + f.Decls = f.Decls[:len(f.Decls)-1] + i-- + break + } else if len(gen.Specs) == 1 { + if impspec.Doc != nil { + delcomments = append(delcomments, impspec.Doc) + } + if impspec.Comment != nil { + delcomments = append(delcomments, impspec.Comment) + } + for _, cg := range f.Comments { + // Found comment on the same line as the import spec. + if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line { + delcomments = append(delcomments, cg) + break + } + } + + spec := gen.Specs[0].(*ast.ImportSpec) + + // Move the documentation right after the import decl. + if spec.Doc != nil { + for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line { + fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) + } + } + for _, cg := range f.Comments { + if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line { + for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line { + fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) + } + break + } + } + } + if j > 0 { + lastImpspec := gen.Specs[j-1].(*ast.ImportSpec) + lastLine := fset.PositionFor(lastImpspec.Path.ValuePos, false).Line + line := fset.PositionFor(impspec.Path.ValuePos, false).Line + + // We deleted an entry but now there may be + // a blank line-sized hole where the import was. + if line-lastLine > 1 || !gen.Rparen.IsValid() { + // There was a blank line immediately preceding the deleted import, + // so there's no need to close the hole. The right parenthesis is + // invalid after AddImport to an import statement without parenthesis. + // Do nothing. + } else if line != fset.File(gen.Rparen).LineCount() { + // There was no blank line. Close the hole. + fset.File(gen.Rparen).MergeLine(line) + } + } + j-- + } + } + + // Delete imports from f.Imports. + for i := 0; i < len(f.Imports); i++ { + imp := f.Imports[i] + for j, del := range delspecs { + if imp == del { + copy(f.Imports[i:], f.Imports[i+1:]) + f.Imports = f.Imports[:len(f.Imports)-1] + copy(delspecs[j:], delspecs[j+1:]) + delspecs = delspecs[:len(delspecs)-1] + i-- + break + } + } + } + + // Delete comments from f.Comments. + for i := 0; i < len(f.Comments); i++ { + cg := f.Comments[i] + for j, del := range delcomments { + if cg == del { + copy(f.Comments[i:], f.Comments[i+1:]) + f.Comments = f.Comments[:len(f.Comments)-1] + copy(delcomments[j:], delcomments[j+1:]) + delcomments = delcomments[:len(delcomments)-1] + i-- + break + } + } + } + + if len(delspecs) > 0 { + panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs)) + } + + return +} + +// RewriteImport rewrites any import of path oldPath to path newPath. +func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) { + for _, imp := range f.Imports { + if importPath(imp) == oldPath { + rewrote = true + // record old End, because the default is to compute + // it using the length of imp.Path.Value. + imp.EndPos = imp.End() + imp.Path.Value = strconv.Quote(newPath) + } + } + return +} + +// UsesImport reports whether a given import is used. +func UsesImport(f *ast.File, path string) (used bool) { + spec := importSpec(f, path) + if spec == nil { + return + } + + name := spec.Name.String() + switch name { + case "": + // 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:] + } + case "_", ".": + // Not sure if this import is used - err on the side of caution. + return true + } + + ast.Walk(visitFn(func(n ast.Node) { + sel, ok := n.(*ast.SelectorExpr) + if ok && isTopName(sel.X, name) { + used = true + } + }), f) + + return +} + +type visitFn func(node ast.Node) + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + fn(node) + return fn +} + +// imports reports whether f has an import with the specified name and path. +func imports(f *ast.File, name, path string) bool { + for _, s := range f.Imports { + if importName(s) == name && importPath(s) == path { + return true + } + } + return false +} + +// importSpec returns the import spec if f imports path, +// or nil otherwise. +func importSpec(f *ast.File, path string) *ast.ImportSpec { + for _, s := range f.Imports { + if importPath(s) == path { + return s + } + } + return nil +} + +// importName returns the name of s, +// or "" if the import is not named. +func importName(s *ast.ImportSpec) string { + if s.Name == nil { + return "" + } + return s.Name.Name +} + +// importPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func importPath(s *ast.ImportSpec) string { + t, err := strconv.Unquote(s.Path.Value) + if err != nil { + return "" + } + return t +} + +// declImports reports whether gen contains an import of path. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +// matchLen returns the length of the longest path segment prefix shared by x and y. +func matchLen(x, y string) int { + n := 0 + for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ { + if x[i] == '/' { + n++ + } + } + return n +} + +// isTopName returns true if n is a top-level unresolved identifier with the given name. +func isTopName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.Name == name && id.Obj == nil +} + +// Imports returns the file imports grouped by paragraph. +func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec { + var groups [][]*ast.ImportSpec + + for _, decl := range f.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok || genDecl.Tok != token.IMPORT { + break + } + + group := []*ast.ImportSpec{} + + var lastLine int + for _, spec := range genDecl.Specs { + importSpec := spec.(*ast.ImportSpec) + pos := importSpec.Path.ValuePos + line := fset.Position(pos).Line + if lastLine > 0 && pos > 0 && line-lastLine > 1 { + groups = append(groups, group) + group = []*ast.ImportSpec{} + } + group = append(group, importSpec) + lastLine = line + } + groups = append(groups, group) + } + + return groups +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go b/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go new file mode 100644 index 000000000..58934f766 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go @@ -0,0 +1,486 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +import ( + "fmt" + "go/ast" + "reflect" + "sort" +) + +// An ApplyFunc is invoked by Apply for each node n, even if n is nil, +// before and/or after the node's children, using a Cursor describing +// the current node and providing operations on it. +// +// The return value of ApplyFunc controls the syntax tree traversal. +// See Apply for details. +type ApplyFunc func(*Cursor) bool + +// Apply traverses a syntax tree recursively, starting with root, +// and calling pre and post for each node as described below. +// Apply returns the syntax tree, possibly modified. +// +// If pre is not nil, it is called for each node before the node's +// children are traversed (pre-order). If pre returns false, no +// children are traversed, and post is not called for that node. +// +// If post is not nil, and a prior call of pre didn't return false, +// post is called for each node after its children are traversed +// (post-order). If post returns false, traversal is terminated and +// Apply returns immediately. +// +// Only fields that refer to AST nodes are considered children; +// i.e., token.Pos, Scopes, Objects, and fields of basic types +// (strings, etc.) are ignored. +// +// Children are traversed in the order in which they appear in the +// respective node's struct definition. A package's files are +// traversed in the filenames' alphabetical order. +func Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) { + parent := &struct{ ast.Node }{root} + defer func() { + if r := recover(); r != nil && r != abort { + panic(r) + } + result = parent.Node + }() + a := &application{pre: pre, post: post} + a.apply(parent, "Node", nil, root) + return +} + +var abort = new(int) // singleton, to signal termination of Apply + +// A Cursor describes a node encountered during Apply. +// Information about the node and its parent is available +// from the Node, Parent, Name, and Index methods. +// +// If p is a variable of type and value of the current parent node +// c.Parent(), and f is the field identifier with name c.Name(), +// the following invariants hold: +// +// p.f == c.Node() if c.Index() < 0 +// p.f[c.Index()] == c.Node() if c.Index() >= 0 +// +// The methods Replace, Delete, InsertBefore, and InsertAfter +// can be used to change the AST without disrupting Apply. +type Cursor struct { + parent ast.Node + name string + iter *iterator // valid if non-nil + node ast.Node +} + +// Node returns the current Node. +func (c *Cursor) Node() ast.Node { return c.node } + +// Parent returns the parent of the current Node. +func (c *Cursor) Parent() ast.Node { return c.parent } + +// Name returns the name of the parent Node field that contains the current Node. +// If the parent is a *ast.Package and the current Node is a *ast.File, Name returns +// the filename for the current Node. +func (c *Cursor) Name() string { return c.name } + +// Index reports the index >= 0 of the current Node in the slice of Nodes that +// contains it, or a value < 0 if the current Node is not part of a slice. +// The index of the current node changes if InsertBefore is called while +// processing the current node. +func (c *Cursor) Index() int { + if c.iter != nil { + return c.iter.index + } + return -1 +} + +// field returns the current node's parent field value. +func (c *Cursor) field() reflect.Value { + return reflect.Indirect(reflect.ValueOf(c.parent)).FieldByName(c.name) +} + +// Replace replaces the current Node with n. +// The replacement node is not walked by Apply. +func (c *Cursor) Replace(n ast.Node) { + if _, ok := c.node.(*ast.File); ok { + file, ok := n.(*ast.File) + if !ok { + panic("attempt to replace *ast.File with non-*ast.File") + } + c.parent.(*ast.Package).Files[c.name] = file + return + } + + v := c.field() + if i := c.Index(); i >= 0 { + v = v.Index(i) + } + v.Set(reflect.ValueOf(n)) +} + +// Delete deletes the current Node from its containing slice. +// If the current Node is not part of a slice, Delete panics. +// As a special case, if the current node is a package file, +// Delete removes it from the package's Files map. +func (c *Cursor) Delete() { + if _, ok := c.node.(*ast.File); ok { + delete(c.parent.(*ast.Package).Files, c.name) + return + } + + i := c.Index() + if i < 0 { + panic("Delete node not contained in slice") + } + v := c.field() + l := v.Len() + reflect.Copy(v.Slice(i, l), v.Slice(i+1, l)) + v.Index(l - 1).Set(reflect.Zero(v.Type().Elem())) + v.SetLen(l - 1) + c.iter.step-- +} + +// InsertAfter inserts n after the current Node in its containing slice. +// If the current Node is not part of a slice, InsertAfter panics. +// Apply does not walk n. +func (c *Cursor) InsertAfter(n ast.Node) { + i := c.Index() + if i < 0 { + panic("InsertAfter node not contained in slice") + } + v := c.field() + v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) + l := v.Len() + reflect.Copy(v.Slice(i+2, l), v.Slice(i+1, l)) + v.Index(i + 1).Set(reflect.ValueOf(n)) + c.iter.step++ +} + +// InsertBefore inserts n before the current Node in its containing slice. +// If the current Node is not part of a slice, InsertBefore panics. +// Apply will not walk n. +func (c *Cursor) InsertBefore(n ast.Node) { + i := c.Index() + if i < 0 { + panic("InsertBefore node not contained in slice") + } + v := c.field() + v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) + l := v.Len() + reflect.Copy(v.Slice(i+1, l), v.Slice(i, l)) + v.Index(i).Set(reflect.ValueOf(n)) + c.iter.index++ +} + +// application carries all the shared data so we can pass it around cheaply. +type application struct { + pre, post ApplyFunc + cursor Cursor + iter iterator +} + +func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) { + // convert typed nil into untyped nil + if v := reflect.ValueOf(n); v.Kind() == reflect.Ptr && v.IsNil() { + n = nil + } + + // avoid heap-allocating a new cursor for each apply call; reuse a.cursor instead + saved := a.cursor + a.cursor.parent = parent + a.cursor.name = name + a.cursor.iter = iter + a.cursor.node = n + + if a.pre != nil && !a.pre(&a.cursor) { + a.cursor = saved + return + } + + // walk children + // (the order of the cases matches the order of the corresponding node types in go/ast) + switch n := n.(type) { + case nil: + // nothing to do + + // Comments and fields + case *ast.Comment: + // nothing to do + + case *ast.CommentGroup: + if n != nil { + a.applyList(n, "List") + } + + case *ast.Field: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Names") + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Tag", nil, n.Tag) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.FieldList: + a.applyList(n, "List") + + // Expressions + case *ast.BadExpr, *ast.Ident, *ast.BasicLit: + // nothing to do + + case *ast.Ellipsis: + a.apply(n, "Elt", nil, n.Elt) + + case *ast.FuncLit: + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Body", nil, n.Body) + + case *ast.CompositeLit: + a.apply(n, "Type", nil, n.Type) + a.applyList(n, "Elts") + + case *ast.ParenExpr: + a.apply(n, "X", nil, n.X) + + case *ast.SelectorExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Sel", nil, n.Sel) + + case *ast.IndexExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Index", nil, n.Index) + + case *ast.IndexListExpr: + a.apply(n, "X", nil, n.X) + a.applyList(n, "Indices") + + case *ast.SliceExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Low", nil, n.Low) + a.apply(n, "High", nil, n.High) + a.apply(n, "Max", nil, n.Max) + + case *ast.TypeAssertExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Type", nil, n.Type) + + case *ast.CallExpr: + a.apply(n, "Fun", nil, n.Fun) + a.applyList(n, "Args") + + case *ast.StarExpr: + a.apply(n, "X", nil, n.X) + + case *ast.UnaryExpr: + a.apply(n, "X", nil, n.X) + + case *ast.BinaryExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Y", nil, n.Y) + + case *ast.KeyValueExpr: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + + // Types + case *ast.ArrayType: + a.apply(n, "Len", nil, n.Len) + a.apply(n, "Elt", nil, n.Elt) + + case *ast.StructType: + a.apply(n, "Fields", nil, n.Fields) + + case *ast.FuncType: + if tparams := n.TypeParams; tparams != nil { + a.apply(n, "TypeParams", nil, tparams) + } + a.apply(n, "Params", nil, n.Params) + a.apply(n, "Results", nil, n.Results) + + case *ast.InterfaceType: + a.apply(n, "Methods", nil, n.Methods) + + case *ast.MapType: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + + case *ast.ChanType: + a.apply(n, "Value", nil, n.Value) + + // Statements + case *ast.BadStmt: + // nothing to do + + case *ast.DeclStmt: + a.apply(n, "Decl", nil, n.Decl) + + case *ast.EmptyStmt: + // nothing to do + + case *ast.LabeledStmt: + a.apply(n, "Label", nil, n.Label) + a.apply(n, "Stmt", nil, n.Stmt) + + case *ast.ExprStmt: + a.apply(n, "X", nil, n.X) + + case *ast.SendStmt: + a.apply(n, "Chan", nil, n.Chan) + a.apply(n, "Value", nil, n.Value) + + case *ast.IncDecStmt: + a.apply(n, "X", nil, n.X) + + case *ast.AssignStmt: + a.applyList(n, "Lhs") + a.applyList(n, "Rhs") + + case *ast.GoStmt: + a.apply(n, "Call", nil, n.Call) + + case *ast.DeferStmt: + a.apply(n, "Call", nil, n.Call) + + case *ast.ReturnStmt: + a.applyList(n, "Results") + + case *ast.BranchStmt: + a.apply(n, "Label", nil, n.Label) + + case *ast.BlockStmt: + a.applyList(n, "List") + + case *ast.IfStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Cond", nil, n.Cond) + a.apply(n, "Body", nil, n.Body) + a.apply(n, "Else", nil, n.Else) + + case *ast.CaseClause: + a.applyList(n, "List") + a.applyList(n, "Body") + + case *ast.SwitchStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Tag", nil, n.Tag) + a.apply(n, "Body", nil, n.Body) + + case *ast.TypeSwitchStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Assign", nil, n.Assign) + a.apply(n, "Body", nil, n.Body) + + case *ast.CommClause: + a.apply(n, "Comm", nil, n.Comm) + a.applyList(n, "Body") + + case *ast.SelectStmt: + a.apply(n, "Body", nil, n.Body) + + case *ast.ForStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Cond", nil, n.Cond) + a.apply(n, "Post", nil, n.Post) + a.apply(n, "Body", nil, n.Body) + + case *ast.RangeStmt: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + a.apply(n, "X", nil, n.X) + a.apply(n, "Body", nil, n.Body) + + // Declarations + case *ast.ImportSpec: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + a.apply(n, "Path", nil, n.Path) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.ValueSpec: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Names") + a.apply(n, "Type", nil, n.Type) + a.applyList(n, "Values") + a.apply(n, "Comment", nil, n.Comment) + + case *ast.TypeSpec: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + if tparams := n.TypeParams; tparams != nil { + a.apply(n, "TypeParams", nil, tparams) + } + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.BadDecl: + // nothing to do + + case *ast.GenDecl: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Specs") + + case *ast.FuncDecl: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Recv", nil, n.Recv) + a.apply(n, "Name", nil, n.Name) + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Body", nil, n.Body) + + // Files and packages + case *ast.File: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + a.applyList(n, "Decls") + // Don't walk n.Comments; they have either been walked already if + // they are Doc comments, or they can be easily walked explicitly. + + case *ast.Package: + // collect and sort names for reproducible behavior + var names []string + for name := range n.Files { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + a.apply(n, name, nil, n.Files[name]) + } + + default: + panic(fmt.Sprintf("Apply: unexpected node type %T", n)) + } + + if a.post != nil && !a.post(&a.cursor) { + panic(abort) + } + + a.cursor = saved +} + +// An iterator controls iteration over a slice of nodes. +type iterator struct { + index, step int +} + +func (a *application) applyList(parent ast.Node, name string) { + // avoid heap-allocating a new iterator for each applyList call; reuse a.iter instead + saved := a.iter + a.iter.index = 0 + for { + // must reload parent.name each time, since cursor modifications might change it + v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(name) + if a.iter.index >= v.Len() { + break + } + + // element x may be nil in a bad AST - be cautious + var x ast.Node + if e := v.Index(a.iter.index); e.IsValid() { + x = e.Interface().(ast.Node) + } + + a.iter.step = 1 + a.apply(parent, name, &a.iter, x) + a.iter.index += a.iter.step + } + a.iter = saved +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/util.go b/vendor/golang.org/x/tools/go/ast/astutil/util.go new file mode 100644 index 000000000..919d5305a --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/util.go @@ -0,0 +1,18 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +import "go/ast" + +// Unparen returns e with any enclosing parentheses stripped. +func Unparen(e ast.Expr) ast.Expr { + for { + p, ok := e.(*ast.ParenExpr) + if !ok { + return e + } + e = p.X + } +} diff --git a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go index 3fbfebf36..1fc1de0bd 100644 --- a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go +++ b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go @@ -64,8 +64,9 @@ type event struct { // depth-first order. It calls f(n) for each node n before it visits // n's children. // +// The complete traversal sequence is determined by ast.Inspect. // The types argument, if non-empty, enables type-based filtering of -// events. The function f if is called only for nodes whose type +// events. The function f is called only for nodes whose type // matches an element of the types slice. func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // Because it avoids postorder calls to f, and the pruning @@ -97,6 +98,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // of the non-nil children of the node, followed by a call of // f(n, false). // +// The complete traversal sequence is determined by ast.Inspect. // The types argument, if non-empty, enables type-based filtering of // events. The function f if is called only for nodes whose type // matches an element of the types slice. diff --git a/vendor/golang.org/x/tools/go/ast/inspector/typeof.go b/vendor/golang.org/x/tools/go/ast/inspector/typeof.go index 703c81395..2a872f89d 100644 --- a/vendor/golang.org/x/tools/go/ast/inspector/typeof.go +++ b/vendor/golang.org/x/tools/go/ast/inspector/typeof.go @@ -12,8 +12,6 @@ package inspector import ( "go/ast" "math" - - "golang.org/x/tools/internal/typeparams" ) const ( @@ -171,7 +169,7 @@ func typeOf(n ast.Node) uint64 { return 1 << nIncDecStmt case *ast.IndexExpr: return 1 << nIndexExpr - case *typeparams.IndexListExpr: + case *ast.IndexListExpr: return 1 << nIndexListExpr case *ast.InterfaceType: return 1 << nInterfaceType diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index 165ede0f8..03543bd4b 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -128,15 +128,14 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, // (from "version"). Select appropriate importer. if len(data) > 0 { switch data[0] { - case 'i': - _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) - return pkg, err + case 'v', 'c', 'd': // binary, till go1.10 + return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'v', 'c', 'd': - _, pkg, err := gcimporter.BImportData(fset, imports, data, path) + case 'i': // indexed, till go1.19 + _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) return pkg, err - case 'u': + case 'u': // unified, from go1.20 _, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path) return pkg, err diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go index 18a002f82..333676b7c 100644 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -8,42 +8,46 @@ package packagesdriver import ( "context" "fmt" - "go/types" "strings" "golang.org/x/tools/internal/gocommand" ) -var debug = false - -func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) { +func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) { inv.Verb = "list" inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) var goarch, compiler string if rawErr != nil { - if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { - // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. + rawErrMsg := rawErr.Error() + if strings.Contains(rawErrMsg, "cannot find main module") || + strings.Contains(rawErrMsg, "go.mod file not found") { + // User's running outside of a module. + // All bets are off. Get GOARCH and guess compiler is gc. // TODO(matloob): Is this a problem in practice? inv.Verb = "env" inv.Args = []string{"GOARCH"} envout, enverr := gocmdRunner.Run(ctx, inv) if enverr != nil { - return nil, enverr + return "", "", enverr } goarch = strings.TrimSpace(envout.String()) compiler = "gc" + } else if friendlyErr != nil { + return "", "", friendlyErr } else { - return nil, friendlyErr + // This should be unreachable, but be defensive + // in case RunRaw's error results are inconsistent. + return "", "", rawErr } } else { fields := strings.Fields(stdout.String()) if len(fields) < 2 { - return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", + return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", stdout.String(), stderr.String()) } goarch = fields[0] compiler = fields[1] } - return types.SizesFor(compiler, goarch), nil + return compiler, goarch, nil } diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index da4ab89fe..b2a0b7c6a 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -5,12 +5,32 @@ /* Package packages loads Go packages for inspection and analysis. -The Load function takes as input a list of patterns and return a list of Package -structs describing individual packages matched by those patterns. -The LoadMode controls the amount of detail in the loaded packages. - -Load passes most patterns directly to the underlying build tool, -but all patterns with the prefix "query=", where query is a +The [Load] function takes as input a list of patterns and returns a +list of [Package] values describing individual packages matched by those +patterns. +A [Config] specifies configuration options, the most important of which is +the [LoadMode], which controls the amount of detail in the loaded packages. + +Load passes most patterns directly to the underlying build tool. +The default build tool is the go command. +Its supported patterns are described at +https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns. + +Load may be used in Go projects that use alternative build systems, by +installing an appropriate "driver" program for the build system and +specifying its location in the GOPACKAGESDRIVER environment variable. +For example, +https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration +explains how to use the driver for Bazel. +The driver program is responsible for interpreting patterns in its +preferred notation and reporting information about the packages that +they identify. +(See driverRequest and driverResponse types for the JSON +schema used by the protocol. +Though the protocol is supported, these types are currently unexported; +see #64608 for a proposal to publish them.) + +Regardless of driver, all patterns with the prefix "query=", where query is a non-empty string of letters from [a-z], are reserved and may be interpreted as query operators. @@ -35,7 +55,7 @@ The Package struct provides basic information about the package, including - Imports, a map from source import strings to the Packages they name; - Types, the type information for the package's exported symbols; - Syntax, the parsed syntax trees for the package's source code; and - - TypeInfo, the result of a complete type-check of the package syntax trees. + - TypesInfo, the result of a complete type-check of the package syntax trees. (See the documentation for type Package for the complete list of fields and more detailed descriptions.) @@ -64,7 +84,7 @@ reported about the loaded packages. See the documentation for type LoadMode for details. Most tools should pass their command-line arguments (after any flags) -uninterpreted to the loader, so that the loader can interpret them +uninterpreted to [Load], so that it can interpret them according to the conventions of the underlying build system. See the Example function for typical usage. */ diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index 7242a0a7d..7db1d1293 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -12,8 +12,8 @@ import ( "bytes" "encoding/json" "fmt" - exec "golang.org/x/sys/execabs" "os" + "os/exec" "strings" ) diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 6bb7168d2..cd375fbc3 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -9,10 +9,9 @@ import ( "context" "encoding/json" "fmt" - "go/types" - "io/ioutil" "log" "os" + "os/exec" "path" "path/filepath" "reflect" @@ -22,7 +21,6 @@ import ( "sync" "unicode" - exec "golang.org/x/sys/execabs" "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" @@ -153,10 +151,10 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { sizeswg.Add(1) go func() { - var sizes types.Sizes - sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) - // types.SizesFor always returns nil or a *types.StdSizes. - response.dr.Sizes, _ = sizes.(*types.StdSizes) + compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) + sizeserr = err + response.dr.Compiler = compiler + response.dr.Arch = arch sizeswg.Done() }() } @@ -210,62 +208,6 @@ extractQueries: } } - // Only use go/packages' overlay processing if we're using a Go version - // below 1.16. Otherwise, go list handles it. - if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 { - modifiedPkgs, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return nil, err - } - - var containsCandidates []string - if len(containFiles) > 0 { - containsCandidates = append(containsCandidates, modifiedPkgs...) - containsCandidates = append(containsCandidates, needPkgs...) - } - if err := state.addNeededOverlayPackages(response, needPkgs); err != nil { - return nil, err - } - // Check candidate packages for containFiles. - if len(containFiles) > 0 { - for _, id := range containsCandidates { - pkg, ok := response.seenPackages[id] - if !ok { - response.addPackage(&Package{ - ID: id, - Errors: []Error{{ - Kind: ListError, - Msg: fmt.Sprintf("package %s expected but not seen", id), - }}, - }) - continue - } - for _, f := range containFiles { - for _, g := range pkg.GoFiles { - if sameFile(f, g) { - response.addRoot(id) - } - } - } - } - } - // Add root for any package that matches a pattern. This applies only to - // packages that are modified by overlays, since they are not added as - // roots automatically. - for _, pattern := range restPatterns { - match := matchPattern(pattern) - for _, pkgID := range modifiedPkgs { - pkg, ok := response.seenPackages[pkgID] - if !ok { - continue - } - if match(pkg.PkgPath) { - response.addRoot(pkg.ID) - } - } - } - } - sizeswg.Wait() if sizeserr != nil { return nil, sizeserr @@ -273,24 +215,6 @@ extractQueries: return response.dr, nil } -func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error { - if len(pkgs) == 0 { - return nil - } - dr, err := state.createDriverResponse(pkgs...) - if err != nil { - return err - } - for _, pkg := range dr.Packages { - response.addPackage(pkg) - } - _, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return err - } - return state.addNeededOverlayPackages(response, needPkgs) -} - func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. @@ -625,7 +549,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse } if pkg.PkgPath == "unsafe" { - pkg.GoFiles = nil // ignore fake unsafe.go file + pkg.CompiledGoFiles = nil // ignore fake unsafe.go file (#59929) + } else if len(pkg.CompiledGoFiles) == 0 { + // Work around for pre-go.1.11 versions of go list. + // TODO(matloob): they should be handled by the fallback. + // Can we delete this? + pkg.CompiledGoFiles = pkg.GoFiles } // Assume go list emits only absolute paths for Dir. @@ -663,16 +592,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse response.Roots = append(response.Roots, pkg.ID) } - // Work around for pre-go.1.11 versions of go list. - // TODO(matloob): they should be handled by the fallback. - // Can we delete this? - if len(pkg.CompiledGoFiles) == 0 { - pkg.CompiledGoFiles = pkg.GoFiles - } - // Temporary work-around for golang/go#39986. Parse filenames out of // error messages. This happens if there are unrecoverable syntax // errors in the source, so we can't match on a specific error message. + // + // TODO(rfindley): remove this heuristic, in favor of considering + // InvalidGoFiles from the list driver. if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { addFilenameFromPos := func(pos string) bool { split := strings.Split(pos, ":") @@ -891,6 +816,15 @@ func golistargs(cfg *Config, words []string, goVersion int) []string { // probably because you'd just get the TestMain. fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)), } + + // golang/go#60456: with go1.21 and later, go list serves pgo variants, which + // can be costly to compute and may result in redundant processing for the + // caller. Disable these variants. If someone wants to add e.g. a NeedPGO + // mode flag, that should be a separate proposal. + if goVersion >= 21 { + fullargs = append(fullargs, "-pgo=off") + } + fullargs = append(fullargs, cfg.BuildFlags...) fullargs = append(fullargs, "--") fullargs = append(fullargs, words...) @@ -1100,7 +1034,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err if len(state.cfg.Overlay) == 0 { return "", func() {}, nil } - dir, err := ioutil.TempDir("", "gopackages-*") + dir, err := os.MkdirTemp("", "gopackages-*") if err != nil { return "", nil, err } @@ -1119,7 +1053,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err // Create a unique filename for the overlaid files, to avoid // creating nested directories. noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") - f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) + f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator)) if err != nil { return "", func() {}, err } @@ -1137,7 +1071,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err } // Write out the overlay file that contains the filepath mappings. filename = filepath.Join(dir, "overlay.json") - if err := ioutil.WriteFile(filename, b, 0665); err != nil { + if err := os.WriteFile(filename, b, 0665); err != nil { return "", func() {}, err } return filename, cleanup, nil diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index 9576b472f..d823c474a 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -6,314 +6,11 @@ package packages import ( "encoding/json" - "fmt" - "go/parser" - "go/token" - "os" "path/filepath" - "regexp" - "sort" - "strconv" - "strings" "golang.org/x/tools/internal/gocommand" ) -// processGolistOverlay provides rudimentary support for adding -// files that don't exist on disk to an overlay. The results can be -// sometimes incorrect. -// TODO(matloob): Handle unsupported cases, including the following: -// - determining the correct package to add given a new import path -func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { - havePkgs := make(map[string]string) // importPath -> non-test package ID - needPkgsSet := make(map[string]bool) - modifiedPkgsSet := make(map[string]bool) - - pkgOfDir := make(map[string][]*Package) - for _, pkg := range response.dr.Packages { - // This is an approximation of import path to id. This can be - // wrong for tests, vendored packages, and a number of other cases. - havePkgs[pkg.PkgPath] = pkg.ID - dir, err := commonDir(pkg.GoFiles) - if err != nil { - return nil, nil, err - } - if dir != "" { - pkgOfDir[dir] = append(pkgOfDir[dir], pkg) - } - } - - // If no new imports are added, it is safe to avoid loading any needPkgs. - // Otherwise, it's hard to tell which package is actually being loaded - // (due to vendoring) and whether any modified package will show up - // in the transitive set of dependencies (because new imports are added, - // potentially modifying the transitive set of dependencies). - var overlayAddsImports bool - - // If both a package and its test package are created by the overlay, we - // need the real package first. Process all non-test files before test - // files, and make the whole process deterministic while we're at it. - var overlayFiles []string - for opath := range state.cfg.Overlay { - overlayFiles = append(overlayFiles, opath) - } - sort.Slice(overlayFiles, func(i, j int) bool { - iTest := strings.HasSuffix(overlayFiles[i], "_test.go") - jTest := strings.HasSuffix(overlayFiles[j], "_test.go") - if iTest != jTest { - return !iTest // non-tests are before tests. - } - return overlayFiles[i] < overlayFiles[j] - }) - for _, opath := range overlayFiles { - contents := state.cfg.Overlay[opath] - base := filepath.Base(opath) - dir := filepath.Dir(opath) - var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant - var testVariantOf *Package // if opath is a test file, this is the package it is testing - var fileExists bool - isTestFile := strings.HasSuffix(opath, "_test.go") - pkgName, ok := extractPackageName(opath, contents) - if !ok { - // Don't bother adding a file that doesn't even have a parsable package statement - // to the overlay. - continue - } - // If all the overlay files belong to a different package, change the - // package name to that package. - maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) - nextPackage: - for _, p := range response.dr.Packages { - if pkgName != p.Name && p.ID != "command-line-arguments" { - continue - } - for _, f := range p.GoFiles { - if !sameFile(filepath.Dir(f), dir) { - continue - } - // Make sure to capture information on the package's test variant, if needed. - if isTestFile && !hasTestFiles(p) { - // TODO(matloob): Are there packages other than the 'production' variant - // of a package that this can match? This shouldn't match the test main package - // because the file is generated in another directory. - testVariantOf = p - continue nextPackage - } else if !isTestFile && hasTestFiles(p) { - // We're examining a test variant, but the overlaid file is - // a non-test file. Because the overlay implementation - // (currently) only adds a file to one package, skip this - // package, so that we can add the file to the production - // variant of the package. (https://golang.org/issue/36857 - // tracks handling overlays on both the production and test - // variant of a package). - continue nextPackage - } - if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { - // We have already seen the production version of the - // for which p is a test variant. - if hasTestFiles(p) { - testVariantOf = pkg - } - } - pkg = p - if filepath.Base(f) == base { - fileExists = true - } - } - } - // The overlay could have included an entirely new package or an - // ad-hoc package. An ad-hoc package is one that we have manually - // constructed from inadequate `go list` results for a file= query. - // It will have the ID command-line-arguments. - if pkg == nil || pkg.ID == "command-line-arguments" { - // Try to find the module or gopath dir the file is contained in. - // Then for modules, add the module opath to the beginning. - pkgPath, ok, err := state.getPkgPath(dir) - if err != nil { - return nil, nil, err - } - if !ok { - break - } - var forTest string // only set for x tests - isXTest := strings.HasSuffix(pkgName, "_test") - if isXTest { - forTest = pkgPath - pkgPath += "_test" - } - id := pkgPath - if isTestFile { - if isXTest { - id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) - } else { - id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) - } - } - if pkg != nil { - // TODO(rstambler): We should change the package's path and ID - // here. The only issue is that this messes with the roots. - } else { - // Try to reclaim a package with the same ID, if it exists in the response. - for _, p := range response.dr.Packages { - if reclaimPackage(p, id, opath, contents) { - pkg = p - break - } - } - // Otherwise, create a new package. - if pkg == nil { - pkg = &Package{ - PkgPath: pkgPath, - ID: id, - Name: pkgName, - Imports: make(map[string]*Package), - } - response.addPackage(pkg) - havePkgs[pkg.PkgPath] = id - // Add the production package's sources for a test variant. - if isTestFile && !isXTest && testVariantOf != nil { - pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) - // Add the package under test and its imports to the test variant. - pkg.forTest = testVariantOf.PkgPath - for k, v := range testVariantOf.Imports { - pkg.Imports[k] = &Package{ID: v.ID} - } - } - if isXTest { - pkg.forTest = forTest - } - } - } - } - if !fileExists { - pkg.GoFiles = append(pkg.GoFiles, opath) - // TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior - // if the file will be ignored due to its build tags. - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) - modifiedPkgsSet[pkg.ID] = true - } - imports, err := extractImports(opath, contents) - if err != nil { - // Let the parser or type checker report errors later. - continue - } - for _, imp := range imports { - // TODO(rstambler): If the package is an x test and the import has - // a test variant, make sure to replace it. - if _, found := pkg.Imports[imp]; found { - continue - } - overlayAddsImports = true - id, ok := havePkgs[imp] - if !ok { - var err error - id, err = state.resolveImport(dir, imp) - if err != nil { - return nil, nil, err - } - } - pkg.Imports[imp] = &Package{ID: id} - // Add dependencies to the non-test variant version of this package as well. - if testVariantOf != nil { - testVariantOf.Imports[imp] = &Package{ID: id} - } - } - } - - // toPkgPath guesses the package path given the id. - toPkgPath := func(sourceDir, id string) (string, error) { - if i := strings.IndexByte(id, ' '); i >= 0 { - return state.resolveImport(sourceDir, id[:i]) - } - return state.resolveImport(sourceDir, id) - } - - // Now that new packages have been created, do another pass to determine - // the new set of missing packages. - for _, pkg := range response.dr.Packages { - for _, imp := range pkg.Imports { - if len(pkg.GoFiles) == 0 { - return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) - } - pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) - if err != nil { - return nil, nil, err - } - if _, ok := havePkgs[pkgPath]; !ok { - needPkgsSet[pkgPath] = true - } - } - } - - if overlayAddsImports { - needPkgs = make([]string, 0, len(needPkgsSet)) - for pkg := range needPkgsSet { - needPkgs = append(needPkgs, pkg) - } - } - modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) - for pkg := range modifiedPkgsSet { - modifiedPkgs = append(modifiedPkgs, pkg) - } - return modifiedPkgs, needPkgs, err -} - -// resolveImport finds the ID of a package given its import path. -// In particular, it will find the right vendored copy when in GOPATH mode. -func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { - env, err := state.getEnv() - if err != nil { - return "", err - } - if env["GOMOD"] != "" { - return importPath, nil - } - - searchDir := sourceDir - for { - vendorDir := filepath.Join(searchDir, "vendor") - exists, ok := state.vendorDirs[vendorDir] - if !ok { - info, err := os.Stat(vendorDir) - exists = err == nil && info.IsDir() - state.vendorDirs[vendorDir] = exists - } - - if exists { - vendoredPath := filepath.Join(vendorDir, importPath) - if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { - // We should probably check for .go files here, but shame on anyone who fools us. - path, ok, err := state.getPkgPath(vendoredPath) - if err != nil { - return "", err - } - if ok { - return path, nil - } - } - } - - // We know we've hit the top of the filesystem when we Dir / and get /, - // or C:\ and get C:\, etc. - next := filepath.Dir(searchDir) - if next == searchDir { - break - } - searchDir = next - } - return importPath, nil -} - -func hasTestFiles(p *Package) bool { - for _, f := range p.GoFiles { - if strings.HasSuffix(f, "_test.go") { - return true - } - } - return false -} - // determineRootDirs returns a mapping from absolute directories that could // contain code to their corresponding import path prefixes. func (state *golistState) determineRootDirs() (map[string]string, error) { @@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { } return m, nil } - -func extractImports(filename string, contents []byte) ([]string, error) { - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? - if err != nil { - return nil, err - } - var res []string - for _, imp := range f.Imports { - quotedPath := imp.Path.Value - path, err := strconv.Unquote(quotedPath) - if err != nil { - return nil, err - } - res = append(res, path) - } - return res, nil -} - -// reclaimPackage attempts to reuse a package that failed to load in an overlay. -// -// If the package has errors and has no Name, GoFiles, or Imports, -// then it's possible that it doesn't yet exist on disk. -func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - if pkg.ID != id { - return false - } - if len(pkg.Errors) != 1 { - return false - } - if pkg.Name != "" || pkg.ExportFile != "" { - return false - } - if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { - return false - } - if len(pkg.Imports) > 0 { - return false - } - pkgName, ok := extractPackageName(filename, contents) - if !ok { - return false - } - pkg.Name = pkgName - pkg.Errors = nil - return true -} - -func extractPackageName(filename string, contents []byte) (string, bool) { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset? - if err != nil { - return "", false - } - return f.Name.Name, true -} - -// commonDir returns the directory that all files are in, "" if files is empty, -// or an error if they aren't in the same directory. -func commonDir(files []string) (string, error) { - seen := make(map[string]bool) - for _, f := range files { - seen[filepath.Dir(f)] = true - } - if len(seen) > 1 { - return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen) - } - for k := range seen { - // seen has only one element; return it. - return k, nil - } - return "", nil // no files -} - -// It is possible that the files in the disk directory dir have a different package -// name from newName, which is deduced from the overlays. If they all have a different -// package name, and they all have the same package name, then that name becomes -// the package name. -// It returns true if it changes the package name, false otherwise. -func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { - names := make(map[string]int) - for _, p := range pkgsOfDir { - names[p.Name]++ - } - if len(names) != 1 { - // some files are in different packages - return - } - var oldName string - for k := range names { - oldName = k - } - if newName == oldName { - return - } - // We might have a case where all of the package names in the directory are - // the same, but the overlay file is for an x test, which belongs to its - // own package. If the x test does not yet exist on disk, we may not yet - // have its package name on disk, but we should not rename the packages. - // - // We use a heuristic to determine if this file belongs to an x test: - // The test file should have a package name whose package name has a _test - // suffix or looks like "newName_test". - maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") - if isTestFile && maybeXTest { - return - } - for _, p := range pkgsOfDir { - p.Name = newName - } -} - -// This function is copy-pasted from -// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360. -// It should be deleted when we remove support for overlays from go/packages. -// -// NOTE: This does not handle any ./... or ./ style queries, as this function -// doesn't know the working directory. -// -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Unfortunately, there are two special cases. Quoting "go help packages": -// -// First, /... at the end of the pattern can match an empty string, -// so that net/... matches both net and packages in its subdirectories, like net/http. -// Second, any slash-separated pattern element containing a wildcard never -// participates in a match of the "vendor" element in the path of a vendored -// package, so that ./... does not match packages in subdirectories of -// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. -// Note, however, that a directory named vendor that itself contains code -// is not a vendored package: cmd/vendor would be a command named vendor, -// and the pattern cmd/... matches it. -func matchPattern(pattern string) func(name string) bool { - // Convert pattern to regular expression. - // The strategy for the trailing /... is to nest it in an explicit ? expression. - // The strategy for the vendor exclusion is to change the unmatchable - // vendor strings to a disallowed code point (vendorChar) and to use - // "(anything but that codepoint)*" as the implementation of the ... wildcard. - // This is a bit complicated but the obvious alternative, - // namely a hand-written search like in most shell glob matchers, - // is too easy to make accidentally exponential. - // Using package regexp guarantees linear-time matching. - - const vendorChar = "\x00" - - if strings.Contains(pattern, vendorChar) { - return func(name string) bool { return false } - } - - re := regexp.QuoteMeta(pattern) - re = replaceVendor(re, vendorChar) - switch { - case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): - re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` - case re == vendorChar+`/\.\.\.`: - re = `(/vendor|/` + vendorChar + `/\.\.\.)` - case strings.HasSuffix(re, `/\.\.\.`): - re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` - } - re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) - - reg := regexp.MustCompile(`^` + re + `$`) - - return func(name string) bool { - if strings.Contains(name, vendorChar) { - return false - } - return reg.MatchString(replaceVendor(name, vendorChar)) - } -} - -// replaceVendor returns the result of replacing -// non-trailing vendor path elements in x with repl. -func replaceVendor(x, repl string) string { - if !strings.Contains(x, "vendor") { - return x - } - elem := strings.Split(x, "/") - for i := 0; i < len(elem)-1; i++ { - if elem[i] == "vendor" { - elem[i] = repl - } - } - return strings.Join(elem, "/") -} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 0f1505b80..81e9e6a72 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -16,7 +16,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -28,8 +27,8 @@ import ( "golang.org/x/tools/go/gcexportdata" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" - "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/versions" ) // A LoadMode controls the amount of detail to return when loading. @@ -220,8 +219,10 @@ type driverResponse struct { // lists of multiple drivers, go/packages will fall back to the next driver. NotHandled bool - // Sizes, if not nil, is the types.Sizes to use when type checking. - Sizes *types.StdSizes + // Compiler and Arch are the arguments pass of types.SizesFor + // to get a types.Sizes to use when type checking. + Compiler string + Arch string // Roots is the set of package IDs that make up the root packages. // We have to encode this separately because when we encode a single package @@ -257,31 +258,52 @@ type driverResponse struct { // proceeding with further analysis. The PrintErrors function is // provided for convenient display of all errors. func Load(cfg *Config, patterns ...string) ([]*Package, error) { - l := newLoader(cfg) - response, err := defaultDriver(&l.Config, patterns...) + ld := newLoader(cfg) + response, external, err := defaultDriver(&ld.Config, patterns...) if err != nil { return nil, err } - l.sizes = response.Sizes - return l.refine(response) + + ld.sizes = types.SizesFor(response.Compiler, response.Arch) + if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 { + // Type size information is needed but unavailable. + if external { + // An external driver may fail to populate the Compiler/GOARCH fields, + // especially since they are relatively new (see #63700). + // Provide a sensible fallback in this case. + ld.sizes = types.SizesFor("gc", runtime.GOARCH) + if ld.sizes == nil { // gccgo-only arch + ld.sizes = types.SizesFor("gc", "amd64") + } + } else { + // Go list should never fail to deliver accurate size information. + // Reject the whole Load since the error is the same for every package. + return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q", + response.Compiler, response.Arch) + } + } + + return ld.refine(response) } // defaultDriver is a driver that implements go/packages' fallback behavior. // It will try to request to an external driver, if one exists. If there's // no external driver, or the driver returns a response with NotHandled set, // defaultDriver will fall back to the go list driver. -func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { - driver := findExternalDriver(cfg) - if driver == nil { - driver = goListDriver - } - response, err := driver(cfg, patterns...) - if err != nil { - return response, err - } else if response.NotHandled { - return goListDriver(cfg, patterns...) +// The boolean result indicates that an external driver handled the request. +func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) { + if driver := findExternalDriver(cfg); driver != nil { + response, err := driver(cfg, patterns...) + if err != nil { + return nil, false, err + } else if !response.NotHandled { + return response, true, nil + } + // (fall through) } - return response, nil + + response, err := goListDriver(cfg, patterns...) + return response, false, err } // A Package describes a loaded Go package. @@ -308,6 +330,9 @@ type Package struct { TypeErrors []types.Error // GoFiles lists the absolute file paths of the package's Go source files. + // It may include files that should not be compiled, for example because + // they contain non-matching build tags, are documentary pseudo-files such as + // unsafe/unsafe.go or builtin/builtin.go, or are subject to cgo preprocessing. GoFiles []string // CompiledGoFiles lists the absolute file paths of the package's source @@ -407,12 +432,6 @@ func init() { packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { return p.(*Package).depsErrors } - packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner { - return config.(*Config).gocmdRunner - } - packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) { - config.(*Config).gocmdRunner = runner - } packagesinternal.SetModFile = func(config interface{}, value string) { config.(*Config).modFile = value } @@ -549,7 +568,7 @@ type loaderPackage struct { type loader struct { pkgs map[string]*loaderPackage Config - sizes types.Sizes + sizes types.Sizes // non-nil if needed by mode parseCache map[string]*parseValue parseCacheMu sync.Mutex exportMu sync.Mutex // enforces mutual exclusion of exportdata operations @@ -627,7 +646,7 @@ func newLoader(cfg *Config) *loader { return ld } -// refine connects the supplied packages into a graph and then adds type and +// refine connects the supplied packages into a graph and then adds type // and syntax information as requested by the LoadMode. func (ld *loader) refine(response *driverResponse) ([]*Package, error) { roots := response.Roots @@ -674,39 +693,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } } - // Materialize the import graph. - - const ( - white = 0 // new - grey = 1 // in progress - black = 2 // complete - ) - - // visit traverses the import graph, depth-first, - // and materializes the graph as Packages.Imports. - // - // Valid imports are saved in the Packages.Import map. - // Invalid imports (cycles and missing nodes) are saved in the importErrors map. - // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. - // - // visit returns whether the package needs src or has a transitive - // dependency on a package that does. These are the only packages - // for which we load source code. - var stack []*loaderPackage - var visit func(lpkg *loaderPackage) bool - var srcPkgs []*loaderPackage - visit = func(lpkg *loaderPackage) bool { - switch lpkg.color { - case black: - return lpkg.needsrc - case grey: - panic("internal error: grey node") - } - lpkg.color = grey - stack = append(stack, lpkg) // push - stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports - // If NeedImports isn't set, the imports fields will all be zeroed out. - if ld.Mode&NeedImports != 0 { + if ld.Mode&NeedImports != 0 { + // Materialize the import graph. + + const ( + white = 0 // new + grey = 1 // in progress + black = 2 // complete + ) + + // visit traverses the import graph, depth-first, + // and materializes the graph as Packages.Imports. + // + // Valid imports are saved in the Packages.Import map. + // Invalid imports (cycles and missing nodes) are saved in the importErrors map. + // Thus, even in the presence of both kinds of errors, + // the Import graph remains a DAG. + // + // visit returns whether the package needs src or has a transitive + // dependency on a package that does. These are the only packages + // for which we load source code. + var stack []*loaderPackage + var visit func(lpkg *loaderPackage) bool + visit = func(lpkg *loaderPackage) bool { + switch lpkg.color { + case black: + return lpkg.needsrc + case grey: + panic("internal error: grey node") + } + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports lpkg.Imports = make(map[string]*Package, len(stubs)) for importPath, ipkg := range stubs { var importErr error @@ -730,40 +748,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } lpkg.Imports[importPath] = imp.Package } - } - if lpkg.needsrc { - srcPkgs = append(srcPkgs, lpkg) - } - if ld.Mode&NeedTypesSizes != 0 { - lpkg.TypesSizes = ld.sizes - } - stack = stack[:len(stack)-1] // pop - lpkg.color = black - return lpkg.needsrc - } + // Complete type information is required for the + // immediate dependencies of each source package. + if lpkg.needsrc && ld.Mode&NeedTypes != 0 { + for _, ipkg := range lpkg.Imports { + ld.pkgs[ipkg.ID].needtypes = true + } + } - if ld.Mode&NeedImports == 0 { - // We do this to drop the stub import packages that we are not even going to try to resolve. - for _, lpkg := range initial { - lpkg.Imports = nil + // NeedTypeSizes causes TypeSizes to be set even + // on packages for which types aren't needed. + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes + } + stack = stack[:len(stack)-1] // pop + lpkg.color = black + + return lpkg.needsrc } - } else { + // For each initial package, create its import DAG. for _, lpkg := range initial { visit(lpkg) } - } - if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { - for _, lpkg := range srcPkgs { - // Complete type information is required for the - // immediate dependencies of each source package. - for _, ipkg := range lpkg.Imports { - imp := ld.pkgs[ipkg.ID] - imp.needtypes = true - } + + } else { + // !NeedImports: drop the stub (ID-only) import packages + // that we are not even going to try to resolve. + for _, lpkg := range initial { + lpkg.Imports = nil } } + // Load type data and syntax if needed, starting at // the initial packages (roots of the import DAG). if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { @@ -997,10 +1014,11 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), + Instances: make(map[*ast.Ident]types.Instance), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } - typeparams.InitInstanceInfo(lpkg.TypesInfo) + versions.InitFileVersions(lpkg.TypesInfo) lpkg.TypesSizes = ld.sizes importer := importerFunc(func(path string) (*types.Package, error) { @@ -1038,7 +1056,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, Error: appendError, - Sizes: ld.sizes, + Sizes: ld.sizes, // may be nil + } + if lpkg.Module != nil && lpkg.Module.GoVersion != "" { + typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion) } if (ld.Mode & typecheckCgo) != 0 { if !typesinternal.SetUsesCgo(tc) { @@ -1119,7 +1140,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { var err error if src == nil { ioLimit <- true // wait - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) <-ioLimit // signal } if err != nil { diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go new file mode 100644 index 000000000..11d5c8c3a --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -0,0 +1,752 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package objectpath defines a naming scheme for types.Objects +// (that is, named entities in Go programs) relative to their enclosing +// package. +// +// Type-checker objects are canonical, so they are usually identified by +// their address in memory (a pointer), but a pointer has meaning only +// within one address space. By contrast, objectpath names allow the +// identity of an object to be sent from one program to another, +// establishing a correspondence between types.Object variables that are +// distinct but logically equivalent. +// +// A single object may have multiple paths. In this example, +// +// type A struct{ X int } +// type B A +// +// the field X has two paths due to its membership of both A and B. +// The For(obj) function always returns one of these paths, arbitrarily +// but consistently. +package objectpath + +import ( + "fmt" + "go/types" + "strconv" + "strings" + + "golang.org/x/tools/internal/typeparams" +) + +// A Path is an opaque name that identifies a types.Object +// relative to its package. Conceptually, the name consists of a +// sequence of destructuring operations applied to the package scope +// to obtain the original object. +// The name does not include the package itself. +type Path string + +// Encoding +// +// An object path is a textual and (with training) human-readable encoding +// of a sequence of destructuring operators, starting from a types.Package. +// The sequences represent a path through the package/object/type graph. +// We classify these operators by their type: +// +// PO package->object Package.Scope.Lookup +// OT object->type Object.Type +// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] +// TO type->object Type.{At,Field,Method,Obj} [AFMO] +// +// All valid paths start with a package and end at an object +// and thus may be defined by the regular language: +// +// objectpath = PO (OT TT* TO)* +// +// The concrete encoding follows directly: +// - The only PO operator is Package.Scope.Lookup, which requires an identifier. +// - The only OT operator is Object.Type, +// which we encode as '.' because dot cannot appear in an identifier. +// - The TT operators are encoded as [EKPRUTC]; +// one of these (TypeParam) requires an integer operand, +// which is encoded as a string of decimal digits. +// - The TO operators are encoded as [AFMO]; +// three of these (At,Field,Method) require an integer operand, +// which is encoded as a string of decimal digits. +// These indices are stable across different representations +// of the same package, even source and export data. +// The indices used are implementation specific and may not correspond to +// the argument to the go/types function. +// +// In the example below, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// field X has the path "T.UM0.RA1.F0", +// representing the following sequence of operations: +// +// p.Lookup("T") T +// .Type().Underlying().Method(0). f +// .Type().Results().At(1) b +// .Type().Field(0) X +// +// The encoding is not maximally compact---every R or P is +// followed by an A, for example---but this simplifies the +// encoder and decoder. +const ( + // object->type operators + opType = '.' // .Type() (Object) + + // type->type operators + opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) + opKey = 'K' // .Key() (Map) + opParams = 'P' // .Params() (Signature) + opResults = 'R' // .Results() (Signature) + opUnderlying = 'U' // .Underlying() (Named) + opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) + opConstraint = 'C' // .Constraint() (TypeParam) + + // type->object operators + opAt = 'A' // .At(i) (Tuple) + opField = 'F' // .Field(i) (Struct) + opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) + opObj = 'O' // .Obj() (Named, TypeParam) +) + +// For is equivalent to new(Encoder).For(obj). +// +// It may be more efficient to reuse a single Encoder across several calls. +func For(obj types.Object) (Path, error) { + return new(Encoder).For(obj) +} + +// An Encoder amortizes the cost of encoding the paths of multiple objects. +// The zero value of an Encoder is ready to use. +type Encoder struct { + scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects +} + +// For returns the path to an object relative to its package, +// or an error if the object is not accessible from the package's Scope. +// +// The For function guarantees to return a path only for the following objects: +// - package-level types +// - exported package-level non-types +// - methods +// - parameter and result variables +// - struct fields +// These objects are sufficient to define the API of their package. +// The objects described by a package's export data are drawn from this set. +// +// The set of objects accessible from a package's Scope depends on +// whether the package was produced by type-checking syntax, or +// reading export data; the latter may have a smaller Scope since +// export data trims objects that are not reachable from an exported +// declaration. For example, the For function will return a path for +// an exported method of an unexported type that is not reachable +// from any public declaration; this path will cause the Object +// function to fail if called on a package loaded from export data. +// TODO(adonovan): is this a bug or feature? Should this package +// compute accessibility in the same way? +// +// For does not return a path for predeclared names, imported package +// names, local names, and unexported package-level names (except +// types). +// +// Example: given this definition, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// For(X) would return a path that denotes the following sequence of operations: +// +// p.Scope().Lookup("T") (TypeName T) +// .Type().Underlying().Method(0). (method Func f) +// .Type().Results().At(1) (field Var b) +// .Type().Field(0) (field Var X) +// +// where p is the package (*types.Package) to which X belongs. +func (enc *Encoder) For(obj types.Object) (Path, error) { + pkg := obj.Pkg() + + // This table lists the cases of interest. + // + // Object Action + // ------ ------ + // nil reject + // builtin reject + // pkgname reject + // label reject + // var + // package-level accept + // func param/result accept + // local reject + // struct field accept + // const + // package-level accept + // local reject + // func + // package-level accept + // init functions reject + // concrete method accept + // interface method accept + // type + // package-level accept + // local reject + // + // The only accessible package-level objects are members of pkg itself. + // + // The cases are handled in four steps: + // + // 1. reject nil and builtin + // 2. accept package-level objects + // 3. reject obviously invalid objects + // 4. search the API for the path to the param/result/field/method. + + // 1. reference to nil or builtin? + if pkg == nil { + return "", fmt.Errorf("predeclared %s has no path", obj) + } + scope := pkg.Scope() + + // 2. package-level object? + if scope.Lookup(obj.Name()) == obj { + // Only exported objects (and non-exported types) have a path. + // Non-exported types may be referenced by other objects. + if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { + return "", fmt.Errorf("no path for non-exported %v", obj) + } + return Path(obj.Name()), nil + } + + // 3. Not a package-level object. + // Reject obviously non-viable cases. + switch obj := obj.(type) { + case *types.TypeName: + if _, ok := obj.Type().(*types.TypeParam); !ok { + // With the exception of type parameters, only package-level type names + // have a path. + return "", fmt.Errorf("no path for %v", obj) + } + case *types.Const, // Only package-level constants have a path. + *types.Label, // Labels are function-local. + *types.PkgName: // PkgNames are file-local. + return "", fmt.Errorf("no path for %v", obj) + + case *types.Var: + // Could be: + // - a field (obj.IsField()) + // - a func parameter or result + // - a local var. + // Sadly there is no way to distinguish + // a param/result from a local + // so we must proceed to the find. + + case *types.Func: + // A func, if not package-level, must be a method. + if recv := obj.Type().(*types.Signature).Recv(); recv == nil { + return "", fmt.Errorf("func is not a method: %v", obj) + } + + if path, ok := enc.concreteMethod(obj); ok { + // Fast path for concrete methods that avoids looping over scope. + return path, nil + } + + default: + panic(obj) + } + + // 4. Search the API for the path to the var (field/param/result) or method. + + // First inspect package-level named types. + // In the presence of path aliases, these give + // the best paths because non-types may + // refer to types, but not the reverse. + empty := make([]byte, 0, 48) // initial space + objs := enc.scopeObjects(scope) + for _, o := range objs { + tname, ok := o.(*types.TypeName) + if !ok { + continue // handle non-types in second pass + } + + path := append(empty, o.Name()...) + path = append(path, opType) + + T := o.Type() + + if tname.IsAlias() { + // type alias + if r := find(obj, T, path, nil); r != nil { + return Path(r), nil + } + } else { + if named, _ := T.(*types.Named); named != nil { + if r := findTypeParam(obj, named.TypeParams(), path, nil); r != nil { + // generic named type + return Path(r), nil + } + } + // defined (named) type + if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil { + return Path(r), nil + } + } + } + + // Then inspect everything else: + // non-types, and declared methods of defined types. + for _, o := range objs { + path := append(empty, o.Name()...) + if _, ok := o.(*types.TypeName); !ok { + if o.Exported() { + // exported non-type (const, var, func) + if r := find(obj, o.Type(), append(path, opType), nil); r != nil { + return Path(r), nil + } + } + continue + } + + // Inspect declared methods of defined types. + if T, ok := o.Type().(*types.Named); ok { + path = append(path, opType) + // The method index here is always with respect + // to the underlying go/types data structures, + // which ultimately derives from source order + // and must be preserved by export data. + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return Path(path2), nil // found declared method + } + if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { + return Path(r), nil + } + } + } + } + + return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) +} + +func appendOpArg(path []byte, op byte, arg int) []byte { + path = append(path, op) + path = strconv.AppendInt(path, int64(arg), 10) + return path +} + +// concreteMethod returns the path for meth, which must have a non-nil receiver. +// The second return value indicates success and may be false if the method is +// an interface method or if it is an instantiated method. +// +// This function is just an optimization that avoids the general scope walking +// approach. You are expected to fall back to the general approach if this +// function fails. +func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { + // Concrete methods can only be declared on package-scoped named types. For + // that reason we can skip the expensive walk over the package scope: the + // path will always be package -> named type -> method. We can trivially get + // the type name from the receiver, and only have to look over the type's + // methods to find the method index. + // + // Methods on generic types require special consideration, however. Consider + // the following package: + // + // L1: type S[T any] struct{} + // L2: func (recv S[A]) Foo() { recv.Bar() } + // L3: func (recv S[B]) Bar() { } + // L4: type Alias = S[int] + // L5: func _[T any]() { var s S[int]; s.Foo() } + // + // The receivers of methods on generic types are instantiations. L2 and L3 + // instantiate S with the type-parameters A and B, which are scoped to the + // respective methods. L4 and L5 each instantiate S with int. Each of these + // instantiations has its own method set, full of methods (and thus objects) + // with receivers whose types are the respective instantiations. In other + // words, we have + // + // S[A].Foo, S[A].Bar + // S[B].Foo, S[B].Bar + // S[int].Foo, S[int].Bar + // + // We may thus be trying to produce object paths for any of these objects. + // + // S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo + // and S.Bar, which are the paths that this function naturally produces. + // + // S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that + // don't correspond to the origin methods. For S[int], this is significant. + // The most precise object path for S[int].Foo, for example, is Alias.Foo, + // not S.Foo. Our function, however, would produce S.Foo, which would + // resolve to a different object. + // + // For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are + // still the correct paths, since only the origin methods have meaningful + // paths. But this is likely only true for trivial cases and has edge cases. + // Since this function is only an optimization, we err on the side of giving + // up, deferring to the slower but definitely correct algorithm. Most users + // of objectpath will only be giving us origin methods, anyway, as referring + // to instantiated methods is usually not useful. + + if typeparams.OriginMethod(meth) != meth { + return "", false + } + + recvT := meth.Type().(*types.Signature).Recv().Type() + if ptr, ok := recvT.(*types.Pointer); ok { + recvT = ptr.Elem() + } + + named, ok := recvT.(*types.Named) + if !ok { + return "", false + } + + if types.IsInterface(named) { + // Named interfaces don't have to be package-scoped + // + // TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface + // methods, too, I think. + return "", false + } + + // Preallocate space for the name, opType, opMethod, and some digits. + name := named.Obj().Name() + path := make([]byte, 0, len(name)+8) + path = append(path, name...) + path = append(path, opType) + + // Method indices are w.r.t. the go/types data structures, + // ultimately deriving from source order, + // which is preserved by export data. + for i := 0; i < named.NumMethods(); i++ { + if named.Method(i) == meth { + path = appendOpArg(path, opMethod, i) + return Path(path), true + } + } + + // Due to golang/go#59944, go/types fails to associate the receiver with + // certain methods on cgo types. + // + // TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go + // versions gopls supports. + return "", false + // panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named))) +} + +// find finds obj within type T, returning the path to it, or nil if not found. +// +// The seen map is used to short circuit cycles through type parameters. If +// nil, it will be allocated as necessary. +func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { + switch T := T.(type) { + case *types.Basic, *types.Named: + // Named types belonging to pkg were handled already, + // so T must belong to another package. No path. + return nil + case *types.Pointer: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Slice: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Array: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Chan: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Map: + if r := find(obj, T.Key(), append(path, opKey), seen); r != nil { + return r + } + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Signature: + if r := findTypeParam(obj, T.TypeParams(), path, seen); r != nil { + return r + } + if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { + return r + } + return find(obj, T.Results(), append(path, opResults), seen) + case *types.Struct: + for i := 0; i < T.NumFields(); i++ { + fld := T.Field(i) + path2 := appendOpArg(path, opField, i) + if fld == obj { + return path2 // found field var + } + if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *types.Tuple: + for i := 0; i < T.Len(); i++ { + v := T.At(i) + path2 := appendOpArg(path, opAt, i) + if v == obj { + return path2 // found param/result var + } + if r := find(obj, v.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *types.Interface: + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return path2 // found interface method + } + if r := find(obj, m.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *types.TypeParam: + name := T.Obj() + if name == obj { + return append(path, opObj) + } + if seen[name] { + return nil + } + if seen == nil { + seen = make(map[*types.TypeName]bool) + } + seen[name] = true + if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil { + return r + } + return nil + } + panic(T) +} + +func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { + for i := 0; i < list.Len(); i++ { + tparam := list.At(i) + path2 := appendOpArg(path, opTypeParam, i) + if r := find(obj, tparam, path2, seen); r != nil { + return r + } + } + return nil +} + +// Object returns the object denoted by path p within the package pkg. +func Object(pkg *types.Package, p Path) (types.Object, error) { + pathstr := string(p) + if pathstr == "" { + return nil, fmt.Errorf("empty path") + } + + var pkgobj, suffix string + if dot := strings.IndexByte(pathstr, opType); dot < 0 { + pkgobj = pathstr + } else { + pkgobj = pathstr[:dot] + suffix = pathstr[dot:] // suffix starts with "." + } + + obj := pkg.Scope().Lookup(pkgobj) + if obj == nil { + return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) + } + + // abstraction of *types.{Pointer,Slice,Array,Chan,Map} + type hasElem interface { + Elem() types.Type + } + // abstraction of *types.{Named,Signature} + type hasTypeParams interface { + TypeParams() *types.TypeParamList + } + // abstraction of *types.{Named,TypeParam} + type hasObj interface { + Obj() *types.TypeName + } + + // The loop state is the pair (t, obj), + // exactly one of which is non-nil, initially obj. + // All suffixes start with '.' (the only object->type operation), + // followed by optional type->type operations, + // then a type->object operation. + // The cycle then repeats. + var t types.Type + for suffix != "" { + code := suffix[0] + suffix = suffix[1:] + + // Codes [AFM] have an integer operand. + var index int + switch code { + case opAt, opField, opMethod, opTypeParam: + rest := strings.TrimLeft(suffix, "0123456789") + numerals := suffix[:len(suffix)-len(rest)] + suffix = rest + i, err := strconv.Atoi(numerals) + if err != nil { + return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) + } + index = int(i) + case opObj: + // no operand + default: + // The suffix must end with a type->object operation. + if suffix == "" { + return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) + } + } + + if code == opType { + if t != nil { + return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) + } + t = obj.Type() + obj = nil + continue + } + + if t == nil { + return nil, fmt.Errorf("invalid path: code %q in object context", code) + } + + // Inv: t != nil, obj == nil + + switch code { + case opElem: + hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) + } + t = hasElem.Elem() + + case opKey: + mapType, ok := t.(*types.Map) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) + } + t = mapType.Key() + + case opParams: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Params() + + case opResults: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Results() + + case opUnderlying: + named, ok := t.(*types.Named) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t) + } + t = named.Underlying() + + case opTypeParam: + hasTypeParams, ok := t.(hasTypeParams) // Named, Signature + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t) + } + tparams := hasTypeParams.TypeParams() + if n := tparams.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + t = tparams.At(index) + + case opConstraint: + tparam, ok := t.(*types.TypeParam) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) + } + t = tparam.Constraint() + + case opAt: + tuple, ok := t.(*types.Tuple) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t) + } + if n := tuple.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + obj = tuple.At(index) + t = nil + + case opField: + structType, ok := t.(*types.Struct) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) + } + if n := structType.NumFields(); index >= n { + return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) + } + obj = structType.Field(index) + t = nil + + case opMethod: + switch t := t.(type) { + case *types.Interface: + if index >= t.NumMethods() { + return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) + } + obj = t.Method(index) // Id-ordered + + case *types.Named: + if index >= t.NumMethods() { + return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) + } + obj = t.Method(index) + + default: + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) + } + t = nil + + case opObj: + hasObj, ok := t.(hasObj) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t) + } + obj = hasObj.Obj() + t = nil + + default: + return nil, fmt.Errorf("invalid path: unknown code %q", code) + } + } + + if obj.Pkg() != pkg { + return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) + } + + return obj, nil // success +} + +// scopeObjects is a memoization of scope objects. +// Callers must not modify the result. +func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object { + m := enc.scopeMemo + if m == nil { + m = make(map[*types.Scope][]types.Object) + enc.scopeMemo = m + } + objs, ok := m[scope] + if !ok { + names := scope.Names() // allocates and sorts + objs = make([]types.Object, len(names)) + for i, name := range names { + objs[i] = scope.Lookup(name) + } + m[scope] = objs + } + return objs +} diff --git a/vendor/golang.org/x/tools/imports/forward.go b/vendor/golang.org/x/tools/imports/forward.go new file mode 100644 index 000000000..cb6db8893 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/forward.go @@ -0,0 +1,77 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package imports implements a Go pretty-printer (like package "go/format") +// that also adds or removes import statements as necessary. +package imports // import "golang.org/x/tools/imports" + +import ( + "log" + "os" + + "golang.org/x/tools/internal/gocommand" + intimp "golang.org/x/tools/internal/imports" +) + +// Options specifies options for processing files. +type Options struct { + Fragment bool // Accept fragment of a source file (no package statement) + AllErrors bool // Report all errors (not just the first 10 on different lines) + + Comments bool // Print comments (true if nil *Options provided) + TabIndent bool // Use tabs for indent (true if nil *Options provided) + TabWidth int // Tab width (8 if nil *Options provided) + + FormatOnly bool // Disable the insertion and deletion of imports +} + +// Debug controls verbose logging. +var Debug = false + +// LocalPrefix is a comma-separated string of import path prefixes, which, if +// set, instructs Process to sort the import paths with the given prefixes +// into another group after 3rd-party packages. +var LocalPrefix string + +// Process formats and adjusts imports for the provided file. +// If opt is nil the defaults are used, and if src is nil the source +// is read from the filesystem. +// +// Note that filename's directory influences which imports can be chosen, +// so it is important that filename be accurate. +// To process data “as if†it were in filename, pass the data as a non-nil src. +func Process(filename string, src []byte, opt *Options) ([]byte, error) { + var err error + if src == nil { + src, err = os.ReadFile(filename) + if err != nil { + return nil, err + } + } + if opt == nil { + opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} + } + intopt := &intimp.Options{ + Env: &intimp.ProcessEnv{ + GocmdRunner: &gocommand.Runner{}, + }, + LocalPrefix: LocalPrefix, + AllErrors: opt.AllErrors, + Comments: opt.Comments, + FormatOnly: opt.FormatOnly, + Fragment: opt.Fragment, + TabIndent: opt.TabIndent, + TabWidth: opt.TabWidth, + } + if Debug { + intopt.Env.Logf = log.Printf + } + return intimp.Process(filename, src, intopt) +} + +// VendorlessPath returns the devendorized version of the import path ipath. +// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". +func VendorlessPath(ipath string) string { + return intimp.VendorlessPath(ipath) +} diff --git a/vendor/golang.org/x/tools/internal/event/keys/util.go b/vendor/golang.org/x/tools/internal/event/keys/util.go new file mode 100644 index 000000000..c0e8e731c --- /dev/null +++ b/vendor/golang.org/x/tools/internal/event/keys/util.go @@ -0,0 +1,21 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package keys + +import ( + "sort" + "strings" +) + +// Join returns a canonical join of the keys in S: +// a sorted comma-separated string list. +func Join[S ~[]T, T ~string](s S) string { + strs := make([]string, 0, len(s)) + for _, v := range s { + strs = append(strs, string(v)) + } + sort.Strings(strs) + return strings.Join(strs, ",") +} diff --git a/vendor/golang.org/x/tools/internal/event/tag/tag.go b/vendor/golang.org/x/tools/internal/event/tag/tag.go new file mode 100644 index 000000000..581b26c20 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/event/tag/tag.go @@ -0,0 +1,59 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tag provides the labels used for telemetry throughout gopls. +package tag + +import ( + "golang.org/x/tools/internal/event/keys" +) + +var ( + // create the label keys we use + Method = keys.NewString("method", "") + StatusCode = keys.NewString("status.code", "") + StatusMessage = keys.NewString("status.message", "") + RPCID = keys.NewString("id", "") + RPCDirection = keys.NewString("direction", "") + File = keys.NewString("file", "") + Directory = keys.New("directory", "") + URI = keys.New("URI", "") + Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs + PackagePath = keys.NewString("package_path", "") + Query = keys.New("query", "") + Snapshot = keys.NewUInt64("snapshot", "") + Operation = keys.NewString("operation", "") + + Position = keys.New("position", "") + Category = keys.NewString("category", "") + PackageCount = keys.NewInt("packages", "") + Files = keys.New("files", "") + Port = keys.NewInt("port", "") + Type = keys.New("type", "") + HoverKind = keys.NewString("hoverkind", "") + + NewServer = keys.NewString("new_server", "A new server was added") + EndServer = keys.NewString("end_server", "A server was shut down") + + ServerID = keys.NewString("server", "The server ID an event is related to") + Logfile = keys.NewString("logfile", "") + DebugAddress = keys.NewString("debug_address", "") + GoplsPath = keys.NewString("gopls_path", "") + ClientID = keys.NewString("client_id", "") + + Level = keys.NewInt("level", "The logging level") +) + +var ( + // create the stats we measure + Started = keys.NewInt64("started", "Count of started RPCs.") + ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes) + SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes) + Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds) +) + +const ( + Inbound = "in" + Outbound = "out" +) diff --git a/vendor/golang.org/x/tools/internal/gcimporter/bexport.go b/vendor/golang.org/x/tools/internal/gcimporter/bexport.go deleted file mode 100644 index 30582ed6d..000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/bexport.go +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Binary package export. -// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; -// see that file for specification of the format. - -package gcimporter - -import ( - "bytes" - "encoding/binary" - "fmt" - "go/constant" - "go/token" - "go/types" - "math" - "math/big" - "sort" - "strings" -) - -// If debugFormat is set, each integer and string value is preceded by a marker -// and position information in the encoding. This mechanism permits an importer -// to recognize immediately when it is out of sync. The importer recognizes this -// mode automatically (i.e., it can import export data produced with debugging -// support even if debugFormat is not set at the time of import). This mode will -// lead to massively larger export data (by a factor of 2 to 3) and should only -// be enabled during development and debugging. -// -// NOTE: This flag is the first flag to enable if importing dies because of -// (suspected) format errors, and whenever a change is made to the format. -const debugFormat = false // default: false - -// Current export format version. Increase with each format change. -// -// Note: The latest binary (non-indexed) export format is at version 6. -// This exporter is still at level 4, but it doesn't matter since -// the binary importer can handle older versions just fine. -// -// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE -// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE -// 4: type name objects support type aliases, uses aliasTag -// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) -// 2: removed unused bool in ODCL export (compiler only) -// 1: header format change (more regular), export package for _ struct fields -// 0: Go1.7 encoding -const exportVersion = 4 - -// trackAllTypes enables cycle tracking for all types, not just named -// types. The existing compiler invariants assume that unnamed types -// that are not completely set up are not used, or else there are spurious -// errors. -// If disabled, only named types are tracked, possibly leading to slightly -// less efficient encoding in rare cases. It also prevents the export of -// some corner-case type declarations (but those are not handled correctly -// with with the textual export format either). -// TODO(gri) enable and remove once issues caused by it are fixed -const trackAllTypes = false - -type exporter struct { - fset *token.FileSet - out bytes.Buffer - - // object -> index maps, indexed in order of serialization - strIndex map[string]int - pkgIndex map[*types.Package]int - typIndex map[types.Type]int - - // position encoding - posInfoFormat bool - prevFile string - prevLine int - - // debugging support - written int // bytes written - indent int // for trace -} - -// internalError represents an error generated inside this package. -type internalError string - -func (e internalError) Error() string { return "gcimporter: " + string(e) } - -func internalErrorf(format string, args ...interface{}) error { - return internalError(fmt.Sprintf(format, args...)) -} - -// BExportData returns binary export data for pkg. -// If no file set is provided, position info will be missing. -func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { - if !debug { - defer func() { - if e := recover(); e != nil { - if ierr, ok := e.(internalError); ok { - err = ierr - return - } - // Not an internal error; panic again. - panic(e) - } - }() - } - - p := exporter{ - fset: fset, - strIndex: map[string]int{"": 0}, // empty string is mapped to 0 - pkgIndex: make(map[*types.Package]int), - typIndex: make(map[types.Type]int), - posInfoFormat: true, // TODO(gri) might become a flag, eventually - } - - // write version info - // The version string must start with "version %d" where %d is the version - // number. Additional debugging information may follow after a blank; that - // text is ignored by the importer. - p.rawStringln(fmt.Sprintf("version %d", exportVersion)) - var debug string - if debugFormat { - debug = "debug" - } - p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly - p.bool(trackAllTypes) - p.bool(p.posInfoFormat) - - // --- generic export data --- - - // populate type map with predeclared "known" types - for index, typ := range predeclared() { - p.typIndex[typ] = index - } - if len(p.typIndex) != len(predeclared()) { - return nil, internalError("duplicate entries in type map?") - } - - // write package data - p.pkg(pkg, true) - if trace { - p.tracef("\n") - } - - // write objects - objcount := 0 - scope := pkg.Scope() - for _, name := range scope.Names() { - if !token.IsExported(name) { - continue - } - if trace { - p.tracef("\n") - } - p.obj(scope.Lookup(name)) - objcount++ - } - - // indicate end of list - if trace { - p.tracef("\n") - } - p.tag(endTag) - - // for self-verification only (redundant) - p.int(objcount) - - if trace { - p.tracef("\n") - } - - // --- end of export data --- - - return p.out.Bytes(), nil -} - -func (p *exporter) pkg(pkg *types.Package, emptypath bool) { - if pkg == nil { - panic(internalError("unexpected nil pkg")) - } - - // if we saw the package before, write its index (>= 0) - if i, ok := p.pkgIndex[pkg]; ok { - p.index('P', i) - return - } - - // otherwise, remember the package, write the package tag (< 0) and package data - if trace { - p.tracef("P%d = { ", len(p.pkgIndex)) - defer p.tracef("} ") - } - p.pkgIndex[pkg] = len(p.pkgIndex) - - p.tag(packageTag) - p.string(pkg.Name()) - if emptypath { - p.string("") - } else { - p.string(pkg.Path()) - } -} - -func (p *exporter) obj(obj types.Object) { - switch obj := obj.(type) { - case *types.Const: - p.tag(constTag) - p.pos(obj) - p.qualifiedName(obj) - p.typ(obj.Type()) - p.value(obj.Val()) - - case *types.TypeName: - if obj.IsAlias() { - p.tag(aliasTag) - p.pos(obj) - p.qualifiedName(obj) - } else { - p.tag(typeTag) - } - p.typ(obj.Type()) - - case *types.Var: - p.tag(varTag) - p.pos(obj) - p.qualifiedName(obj) - p.typ(obj.Type()) - - case *types.Func: - p.tag(funcTag) - p.pos(obj) - p.qualifiedName(obj) - sig := obj.Type().(*types.Signature) - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) - - default: - panic(internalErrorf("unexpected object %v (%T)", obj, obj)) - } -} - -func (p *exporter) pos(obj types.Object) { - if !p.posInfoFormat { - return - } - - file, line := p.fileLine(obj) - if file == p.prevFile { - // common case: write line delta - // delta == 0 means different file or no line change - delta := line - p.prevLine - p.int(delta) - if delta == 0 { - p.int(-1) // -1 means no file change - } - } else { - // different file - p.int(0) - // Encode filename as length of common prefix with previous - // filename, followed by (possibly empty) suffix. Filenames - // frequently share path prefixes, so this can save a lot - // of space and make export data size less dependent on file - // path length. The suffix is unlikely to be empty because - // file names tend to end in ".go". - n := commonPrefixLen(p.prevFile, file) - p.int(n) // n >= 0 - p.string(file[n:]) // write suffix only - p.prevFile = file - p.int(line) - } - p.prevLine = line -} - -func (p *exporter) fileLine(obj types.Object) (file string, line int) { - if p.fset != nil { - pos := p.fset.Position(obj.Pos()) - file = pos.Filename - line = pos.Line - } - return -} - -func commonPrefixLen(a, b string) int { - if len(a) > len(b) { - a, b = b, a - } - // len(a) <= len(b) - i := 0 - for i < len(a) && a[i] == b[i] { - i++ - } - return i -} - -func (p *exporter) qualifiedName(obj types.Object) { - p.string(obj.Name()) - p.pkg(obj.Pkg(), false) -} - -func (p *exporter) typ(t types.Type) { - if t == nil { - panic(internalError("nil type")) - } - - // Possible optimization: Anonymous pointer types *T where - // T is a named type are common. We could canonicalize all - // such types *T to a single type PT = *T. This would lead - // to at most one *T entry in typIndex, and all future *T's - // would be encoded as the respective index directly. Would - // save 1 byte (pointerTag) per *T and reduce the typIndex - // size (at the cost of a canonicalization map). We can do - // this later, without encoding format change. - - // if we saw the type before, write its index (>= 0) - if i, ok := p.typIndex[t]; ok { - p.index('T', i) - return - } - - // otherwise, remember the type, write the type tag (< 0) and type data - if trackAllTypes { - if trace { - p.tracef("T%d = {>\n", len(p.typIndex)) - defer p.tracef("<\n} ") - } - p.typIndex[t] = len(p.typIndex) - } - - switch t := t.(type) { - case *types.Named: - if !trackAllTypes { - // if we don't track all types, track named types now - p.typIndex[t] = len(p.typIndex) - } - - p.tag(namedTag) - p.pos(t.Obj()) - p.qualifiedName(t.Obj()) - p.typ(t.Underlying()) - if !types.IsInterface(t) { - p.assocMethods(t) - } - - case *types.Array: - p.tag(arrayTag) - p.int64(t.Len()) - p.typ(t.Elem()) - - case *types.Slice: - p.tag(sliceTag) - p.typ(t.Elem()) - - case *dddSlice: - p.tag(dddTag) - p.typ(t.elem) - - case *types.Struct: - p.tag(structTag) - p.fieldList(t) - - case *types.Pointer: - p.tag(pointerTag) - p.typ(t.Elem()) - - case *types.Signature: - p.tag(signatureTag) - p.paramList(t.Params(), t.Variadic()) - p.paramList(t.Results(), false) - - case *types.Interface: - p.tag(interfaceTag) - p.iface(t) - - case *types.Map: - p.tag(mapTag) - p.typ(t.Key()) - p.typ(t.Elem()) - - case *types.Chan: - p.tag(chanTag) - p.int(int(3 - t.Dir())) // hack - p.typ(t.Elem()) - - default: - panic(internalErrorf("unexpected type %T: %s", t, t)) - } -} - -func (p *exporter) assocMethods(named *types.Named) { - // Sort methods (for determinism). - var methods []*types.Func - for i := 0; i < named.NumMethods(); i++ { - methods = append(methods, named.Method(i)) - } - sort.Sort(methodsByName(methods)) - - p.int(len(methods)) - - if trace && methods != nil { - p.tracef("associated methods {>\n") - } - - for i, m := range methods { - if trace && i > 0 { - p.tracef("\n") - } - - p.pos(m) - name := m.Name() - p.string(name) - if !exported(name) { - p.pkg(m.Pkg(), false) - } - - sig := m.Type().(*types.Signature) - p.paramList(types.NewTuple(sig.Recv()), false) - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) - p.int(0) // dummy value for go:nointerface pragma - ignored by importer - } - - if trace && methods != nil { - p.tracef("<\n} ") - } -} - -type methodsByName []*types.Func - -func (x methodsByName) Len() int { return len(x) } -func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } - -func (p *exporter) fieldList(t *types.Struct) { - if trace && t.NumFields() > 0 { - p.tracef("fields {>\n") - defer p.tracef("<\n} ") - } - - p.int(t.NumFields()) - for i := 0; i < t.NumFields(); i++ { - if trace && i > 0 { - p.tracef("\n") - } - p.field(t.Field(i)) - p.string(t.Tag(i)) - } -} - -func (p *exporter) field(f *types.Var) { - if !f.IsField() { - panic(internalError("field expected")) - } - - p.pos(f) - p.fieldName(f) - p.typ(f.Type()) -} - -func (p *exporter) iface(t *types.Interface) { - // TODO(gri): enable importer to load embedded interfaces, - // then emit Embeddeds and ExplicitMethods separately here. - p.int(0) - - n := t.NumMethods() - if trace && n > 0 { - p.tracef("methods {>\n") - defer p.tracef("<\n} ") - } - p.int(n) - for i := 0; i < n; i++ { - if trace && i > 0 { - p.tracef("\n") - } - p.method(t.Method(i)) - } -} - -func (p *exporter) method(m *types.Func) { - sig := m.Type().(*types.Signature) - if sig.Recv() == nil { - panic(internalError("method expected")) - } - - p.pos(m) - p.string(m.Name()) - if m.Name() != "_" && !token.IsExported(m.Name()) { - p.pkg(m.Pkg(), false) - } - - // interface method; no need to encode receiver. - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) -} - -func (p *exporter) fieldName(f *types.Var) { - name := f.Name() - - if f.Anonymous() { - // anonymous field - we distinguish between 3 cases: - // 1) field name matches base type name and is exported - // 2) field name matches base type name and is not exported - // 3) field name doesn't match base type name (alias name) - bname := basetypeName(f.Type()) - if name == bname { - if token.IsExported(name) { - name = "" // 1) we don't need to know the field name or package - } else { - name = "?" // 2) use unexported name "?" to force package export - } - } else { - // 3) indicate alias and export name as is - // (this requires an extra "@" but this is a rare case) - p.string("@") - } - } - - p.string(name) - if name != "" && !token.IsExported(name) { - p.pkg(f.Pkg(), false) - } -} - -func basetypeName(typ types.Type) string { - switch typ := deref(typ).(type) { - case *types.Basic: - return typ.Name() - case *types.Named: - return typ.Obj().Name() - default: - return "" // unnamed type - } -} - -func (p *exporter) paramList(params *types.Tuple, variadic bool) { - // use negative length to indicate unnamed parameters - // (look at the first parameter only since either all - // names are present or all are absent) - n := params.Len() - if n > 0 && params.At(0).Name() == "" { - n = -n - } - p.int(n) - for i := 0; i < params.Len(); i++ { - q := params.At(i) - t := q.Type() - if variadic && i == params.Len()-1 { - t = &dddSlice{t.(*types.Slice).Elem()} - } - p.typ(t) - if n > 0 { - name := q.Name() - p.string(name) - if name != "_" { - p.pkg(q.Pkg(), false) - } - } - p.string("") // no compiler-specific info - } -} - -func (p *exporter) value(x constant.Value) { - if trace { - p.tracef("= ") - } - - switch x.Kind() { - case constant.Bool: - tag := falseTag - if constant.BoolVal(x) { - tag = trueTag - } - p.tag(tag) - - case constant.Int: - if v, exact := constant.Int64Val(x); exact { - // common case: x fits into an int64 - use compact encoding - p.tag(int64Tag) - p.int64(v) - return - } - // uncommon case: large x - use float encoding - // (powers of 2 will be encoded efficiently with exponent) - p.tag(floatTag) - p.float(constant.ToFloat(x)) - - case constant.Float: - p.tag(floatTag) - p.float(x) - - case constant.Complex: - p.tag(complexTag) - p.float(constant.Real(x)) - p.float(constant.Imag(x)) - - case constant.String: - p.tag(stringTag) - p.string(constant.StringVal(x)) - - case constant.Unknown: - // package contains type errors - p.tag(unknownTag) - - default: - panic(internalErrorf("unexpected value %v (%T)", x, x)) - } -} - -func (p *exporter) float(x constant.Value) { - if x.Kind() != constant.Float { - panic(internalErrorf("unexpected constant %v, want float", x)) - } - // extract sign (there is no -0) - sign := constant.Sign(x) - if sign == 0 { - // x == 0 - p.int(0) - return - } - // x != 0 - - var f big.Float - if v, exact := constant.Float64Val(x); exact { - // float64 - f.SetFloat64(v) - } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { - // TODO(gri): add big.Rat accessor to constant.Value. - r := valueToRat(num) - f.SetRat(r.Quo(r, valueToRat(denom))) - } else { - // Value too large to represent as a fraction => inaccessible. - // TODO(gri): add big.Float accessor to constant.Value. - f.SetFloat64(math.MaxFloat64) // FIXME - } - - // extract exponent such that 0.5 <= m < 1.0 - var m big.Float - exp := f.MantExp(&m) - - // extract mantissa as *big.Int - // - set exponent large enough so mant satisfies mant.IsInt() - // - get *big.Int from mant - m.SetMantExp(&m, int(m.MinPrec())) - mant, acc := m.Int(nil) - if acc != big.Exact { - panic(internalError("internal error")) - } - - p.int(sign) - p.int(exp) - p.string(string(mant.Bytes())) -} - -func valueToRat(x constant.Value) *big.Rat { - // Convert little-endian to big-endian. - // I can't believe this is necessary. - bytes := constant.Bytes(x) - for i := 0; i < len(bytes)/2; i++ { - bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] - } - return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) -} - -func (p *exporter) bool(b bool) bool { - if trace { - p.tracef("[") - defer p.tracef("= %v] ", b) - } - - x := 0 - if b { - x = 1 - } - p.int(x) - return b -} - -// ---------------------------------------------------------------------------- -// Low-level encoders - -func (p *exporter) index(marker byte, index int) { - if index < 0 { - panic(internalError("invalid index < 0")) - } - if debugFormat { - p.marker('t') - } - if trace { - p.tracef("%c%d ", marker, index) - } - p.rawInt64(int64(index)) -} - -func (p *exporter) tag(tag int) { - if tag >= 0 { - panic(internalError("invalid tag >= 0")) - } - if debugFormat { - p.marker('t') - } - if trace { - p.tracef("%s ", tagString[-tag]) - } - p.rawInt64(int64(tag)) -} - -func (p *exporter) int(x int) { - p.int64(int64(x)) -} - -func (p *exporter) int64(x int64) { - if debugFormat { - p.marker('i') - } - if trace { - p.tracef("%d ", x) - } - p.rawInt64(x) -} - -func (p *exporter) string(s string) { - if debugFormat { - p.marker('s') - } - if trace { - p.tracef("%q ", s) - } - // if we saw the string before, write its index (>= 0) - // (the empty string is mapped to 0) - if i, ok := p.strIndex[s]; ok { - p.rawInt64(int64(i)) - return - } - // otherwise, remember string and write its negative length and bytes - p.strIndex[s] = len(p.strIndex) - p.rawInt64(-int64(len(s))) - for i := 0; i < len(s); i++ { - p.rawByte(s[i]) - } -} - -// marker emits a marker byte and position information which makes -// it easy for a reader to detect if it is "out of sync". Used for -// debugFormat format only. -func (p *exporter) marker(m byte) { - p.rawByte(m) - // Enable this for help tracking down the location - // of an incorrect marker when running in debugFormat. - if false && trace { - p.tracef("#%d ", p.written) - } - p.rawInt64(int64(p.written)) -} - -// rawInt64 should only be used by low-level encoders. -func (p *exporter) rawInt64(x int64) { - var tmp [binary.MaxVarintLen64]byte - n := binary.PutVarint(tmp[:], x) - for i := 0; i < n; i++ { - p.rawByte(tmp[i]) - } -} - -// rawStringln should only be used to emit the initial version string. -func (p *exporter) rawStringln(s string) { - for i := 0; i < len(s); i++ { - p.rawByte(s[i]) - } - p.rawByte('\n') -} - -// rawByte is the bottleneck interface to write to p.out. -// rawByte escapes b as follows (any encoding does that -// hides '$'): -// -// '$' => '|' 'S' -// '|' => '|' '|' -// -// Necessary so other tools can find the end of the -// export data by searching for "$$". -// rawByte should only be used by low-level encoders. -func (p *exporter) rawByte(b byte) { - switch b { - case '$': - // write '$' as '|' 'S' - b = 'S' - fallthrough - case '|': - // write '|' as '|' '|' - p.out.WriteByte('|') - p.written++ - } - p.out.WriteByte(b) - p.written++ -} - -// tracef is like fmt.Printf but it rewrites the format string -// to take care of indentation. -func (p *exporter) tracef(format string, args ...interface{}) { - if strings.ContainsAny(format, "<>\n") { - var buf bytes.Buffer - for i := 0; i < len(format); i++ { - // no need to deal with runes - ch := format[i] - switch ch { - case '>': - p.indent++ - continue - case '<': - p.indent-- - continue - } - buf.WriteByte(ch) - if ch == '\n' { - for j := p.indent; j > 0; j-- { - buf.WriteString(". ") - } - } - } - format = buf.String() - } - fmt.Printf(format, args...) -} - -// Debugging support. -// (tagString is only used when tracing is enabled) -var tagString = [...]string{ - // Packages - -packageTag: "package", - - // Types - -namedTag: "named type", - -arrayTag: "array", - -sliceTag: "slice", - -dddTag: "ddd", - -structTag: "struct", - -pointerTag: "pointer", - -signatureTag: "signature", - -interfaceTag: "interface", - -mapTag: "map", - -chanTag: "chan", - - // Values - -falseTag: "false", - -trueTag: "true", - -int64Tag: "int64", - -floatTag: "float", - -fractionTag: "fraction", - -complexTag: "complex", - -stringTag: "string", - -unknownTag: "unknown", - - // Type aliases - -aliasTag: "alias", -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go index b85de0147..d98b0db2a 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go @@ -2,340 +2,24 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go. +// This file contains the remaining vestiges of +// $GOROOT/src/go/internal/gcimporter/bimport.go. package gcimporter import ( - "encoding/binary" "fmt" - "go/constant" "go/token" "go/types" - "sort" - "strconv" - "strings" "sync" - "unicode" - "unicode/utf8" ) -type importer struct { - imports map[string]*types.Package - data []byte - importpath string - buf []byte // for reading strings - version int // export format version - - // object lists - strList []string // in order of appearance - pathList []string // in order of appearance - pkgList []*types.Package // in order of appearance - typList []types.Type // in order of appearance - interfaceList []*types.Interface // for delayed completion only - trackAllTypes bool - - // position encoding - posInfoFormat bool - prevFile string - prevLine int - fake fakeFileSet - - // debugging support - debugFormat bool - read int // bytes read -} - -// BImportData imports a package from the serialized package data -// and returns the number of bytes consumed and a reference to the package. -// If the export data version is not recognized or the format is otherwise -// compromised, an error is returned. -func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { - // catch panics and return them as errors - const currentVersion = 6 - version := -1 // unknown version - defer func() { - if e := recover(); e != nil { - // Return a (possibly nil or incomplete) package unchanged (see #16088). - if version > currentVersion { - err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) - } else { - err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) - } - } - }() - - p := importer{ - imports: imports, - data: data, - importpath: path, - version: version, - strList: []string{""}, // empty string is mapped to 0 - pathList: []string{""}, // empty string is mapped to 0 - fake: fakeFileSet{ - fset: fset, - files: make(map[string]*fileInfo), - }, - } - defer p.fake.setLines() // set lines for files in fset - - // read version info - var versionstr string - if b := p.rawByte(); b == 'c' || b == 'd' { - // Go1.7 encoding; first byte encodes low-level - // encoding format (compact vs debug). - // For backward-compatibility only (avoid problems with - // old installed packages). Newly compiled packages use - // the extensible format string. - // TODO(gri) Remove this support eventually; after Go1.8. - if b == 'd' { - p.debugFormat = true - } - p.trackAllTypes = p.rawByte() == 'a' - p.posInfoFormat = p.int() != 0 - versionstr = p.string() - if versionstr == "v1" { - version = 0 - } - } else { - // Go1.8 extensible encoding - // read version string and extract version number (ignore anything after the version number) - versionstr = p.rawStringln(b) - if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { - if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { - version = v - } - } - } - p.version = version - - // read version specific flags - extend as necessary - switch p.version { - // case currentVersion: - // ... - // fallthrough - case currentVersion, 5, 4, 3, 2, 1: - p.debugFormat = p.rawStringln(p.rawByte()) == "debug" - p.trackAllTypes = p.int() != 0 - p.posInfoFormat = p.int() != 0 - case 0: - // Go1.7 encoding format - nothing to do here - default: - errorf("unknown bexport format version %d (%q)", p.version, versionstr) - } - - // --- generic export data --- - - // populate typList with predeclared "known" types - p.typList = append(p.typList, predeclared()...) - - // read package data - pkg = p.pkg() - - // read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go) - objcount := 0 - for { - tag := p.tagOrIndex() - if tag == endTag { - break - } - p.obj(tag) - objcount++ - } - - // self-verification - if count := p.int(); count != objcount { - errorf("got %d objects; want %d", objcount, count) - } - - // ignore compiler-specific import data - - // complete interfaces - // TODO(gri) re-investigate if we still need to do this in a delayed fashion - for _, typ := range p.interfaceList { - typ.Complete() - } - - // record all referenced packages as imports - list := append(([]*types.Package)(nil), p.pkgList[1:]...) - sort.Sort(byPath(list)) - pkg.SetImports(list) - - // package was imported completely and without errors - pkg.MarkComplete() - - return p.read, pkg, nil -} - func errorf(format string, args ...interface{}) { panic(fmt.Sprintf(format, args...)) } -func (p *importer) pkg() *types.Package { - // if the package was seen before, i is its index (>= 0) - i := p.tagOrIndex() - if i >= 0 { - return p.pkgList[i] - } - - // otherwise, i is the package tag (< 0) - if i != packageTag { - errorf("unexpected package tag %d version %d", i, p.version) - } - - // read package data - name := p.string() - var path string - if p.version >= 5 { - path = p.path() - } else { - path = p.string() - } - if p.version >= 6 { - p.int() // package height; unused by go/types - } - - // we should never see an empty package name - if name == "" { - errorf("empty package name in import") - } - - // an empty path denotes the package we are currently importing; - // it must be the first package we see - if (path == "") != (len(p.pkgList) == 0) { - errorf("package path %q for pkg index %d", path, len(p.pkgList)) - } - - // if the package was imported before, use that one; otherwise create a new one - if path == "" { - path = p.importpath - } - pkg := p.imports[path] - if pkg == nil { - pkg = types.NewPackage(path, name) - p.imports[path] = pkg - } else if pkg.Name() != name { - errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path) - } - p.pkgList = append(p.pkgList, pkg) - - return pkg -} - -// objTag returns the tag value for each object kind. -func objTag(obj types.Object) int { - switch obj.(type) { - case *types.Const: - return constTag - case *types.TypeName: - return typeTag - case *types.Var: - return varTag - case *types.Func: - return funcTag - default: - errorf("unexpected object: %v (%T)", obj, obj) // panics - panic("unreachable") - } -} - -func sameObj(a, b types.Object) bool { - // Because unnamed types are not canonicalized, we cannot simply compare types for - // (pointer) identity. - // Ideally we'd check equality of constant values as well, but this is good enough. - return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type()) -} - -func (p *importer) declare(obj types.Object) { - pkg := obj.Pkg() - if alt := pkg.Scope().Insert(obj); alt != nil { - // This can only trigger if we import a (non-type) object a second time. - // Excluding type aliases, this cannot happen because 1) we only import a package - // once; and b) we ignore compiler-specific export data which may contain - // functions whose inlined function bodies refer to other functions that - // were already imported. - // However, type aliases require reexporting the original type, so we need - // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, - // method importer.obj, switch case importing functions). - // TODO(gri) review/update this comment once the gc compiler handles type aliases. - if !sameObj(obj, alt) { - errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) - } - } -} - -func (p *importer) obj(tag int) { - switch tag { - case constTag: - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - val := p.value() - p.declare(types.NewConst(pos, pkg, name, typ, val)) - - case aliasTag: - // TODO(gri) verify type alias hookup is correct - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - p.declare(types.NewTypeName(pos, pkg, name, typ)) - - case typeTag: - p.typ(nil, nil) - - case varTag: - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - p.declare(types.NewVar(pos, pkg, name, typ)) - - case funcTag: - pos := p.pos() - pkg, name := p.qualifiedName() - params, isddd := p.paramList() - result, _ := p.paramList() - sig := types.NewSignature(nil, params, result, isddd) - p.declare(types.NewFunc(pos, pkg, name, sig)) - - default: - errorf("unexpected object tag %d", tag) - } -} - const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go -func (p *importer) pos() token.Pos { - if !p.posInfoFormat { - return token.NoPos - } - - file := p.prevFile - line := p.prevLine - delta := p.int() - line += delta - if p.version >= 5 { - if delta == deltaNewFile { - if n := p.int(); n >= 0 { - // file changed - file = p.path() - line = n - } - } - } else { - if delta == 0 { - if n := p.int(); n >= 0 { - // file changed - file = p.prevFile[:n] + p.string() - line = p.int() - } - } - } - p.prevFile = file - p.prevLine = line - - return p.fake.pos(file, line, 0) -} - // Synthesize a token.Pos type fakeFileSet struct { fset *token.FileSet @@ -389,205 +73,6 @@ var ( fakeLinesOnce sync.Once ) -func (p *importer) qualifiedName() (pkg *types.Package, name string) { - name = p.string() - pkg = p.pkg() - return -} - -func (p *importer) record(t types.Type) { - p.typList = append(p.typList, t) -} - -// A dddSlice is a types.Type representing ...T parameters. -// It only appears for parameter types and does not escape -// the importer. -type dddSlice struct { - elem types.Type -} - -func (t *dddSlice) Underlying() types.Type { return t } -func (t *dddSlice) String() string { return "..." + t.elem.String() } - -// parent is the package which declared the type; parent == nil means -// the package currently imported. The parent package is needed for -// exported struct fields and interface methods which don't contain -// explicit package information in the export data. -// -// A non-nil tname is used as the "owner" of the result type; i.e., -// the result type is the underlying type of tname. tname is used -// to give interface methods a named receiver type where possible. -func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type { - // if the type was seen before, i is its index (>= 0) - i := p.tagOrIndex() - if i >= 0 { - return p.typList[i] - } - - // otherwise, i is the type tag (< 0) - switch i { - case namedTag: - // read type object - pos := p.pos() - parent, name := p.qualifiedName() - scope := parent.Scope() - obj := scope.Lookup(name) - - // if the object doesn't exist yet, create and insert it - if obj == nil { - obj = types.NewTypeName(pos, parent, name, nil) - scope.Insert(obj) - } - - if _, ok := obj.(*types.TypeName); !ok { - errorf("pkg = %s, name = %s => %s", parent, name, obj) - } - - // associate new named type with obj if it doesn't exist yet - t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) - - // but record the existing type, if any - tname := obj.Type().(*types.Named) // tname is either t0 or the existing type - p.record(tname) - - // read underlying type - t0.SetUnderlying(p.typ(parent, t0)) - - // interfaces don't have associated methods - if types.IsInterface(t0) { - return tname - } - - // read associated methods - for i := p.int(); i > 0; i-- { - // TODO(gri) replace this with something closer to fieldName - pos := p.pos() - name := p.string() - if !exported(name) { - p.pkg() - } - - recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? - params, isddd := p.paramList() - result, _ := p.paramList() - p.int() // go:nointerface pragma - discarded - - sig := types.NewSignature(recv.At(0), params, result, isddd) - t0.AddMethod(types.NewFunc(pos, parent, name, sig)) - } - - return tname - - case arrayTag: - t := new(types.Array) - if p.trackAllTypes { - p.record(t) - } - - n := p.int64() - *t = *types.NewArray(p.typ(parent, nil), n) - return t - - case sliceTag: - t := new(types.Slice) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewSlice(p.typ(parent, nil)) - return t - - case dddTag: - t := new(dddSlice) - if p.trackAllTypes { - p.record(t) - } - - t.elem = p.typ(parent, nil) - return t - - case structTag: - t := new(types.Struct) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewStruct(p.fieldList(parent)) - return t - - case pointerTag: - t := new(types.Pointer) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewPointer(p.typ(parent, nil)) - return t - - case signatureTag: - t := new(types.Signature) - if p.trackAllTypes { - p.record(t) - } - - params, isddd := p.paramList() - result, _ := p.paramList() - *t = *types.NewSignature(nil, params, result, isddd) - return t - - case interfaceTag: - // Create a dummy entry in the type list. This is safe because we - // cannot expect the interface type to appear in a cycle, as any - // such cycle must contain a named type which would have been - // first defined earlier. - // TODO(gri) Is this still true now that we have type aliases? - // See issue #23225. - n := len(p.typList) - if p.trackAllTypes { - p.record(nil) - } - - var embeddeds []types.Type - for n := p.int(); n > 0; n-- { - p.pos() - embeddeds = append(embeddeds, p.typ(parent, nil)) - } - - t := newInterface(p.methodList(parent, tname), embeddeds) - p.interfaceList = append(p.interfaceList, t) - if p.trackAllTypes { - p.typList[n] = t - } - return t - - case mapTag: - t := new(types.Map) - if p.trackAllTypes { - p.record(t) - } - - key := p.typ(parent, nil) - val := p.typ(parent, nil) - *t = *types.NewMap(key, val) - return t - - case chanTag: - t := new(types.Chan) - if p.trackAllTypes { - p.record(t) - } - - dir := chanDir(p.int()) - val := p.typ(parent, nil) - *t = *types.NewChan(dir, val) - return t - - default: - errorf("unexpected type tag %d", i) // panics - panic("unreachable") - } -} - func chanDir(d int) types.ChanDir { // tag values must match the constants in cmd/compile/internal/gc/go.go switch d { @@ -603,394 +88,6 @@ func chanDir(d int) types.ChanDir { } } -func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { - if n := p.int(); n > 0 { - fields = make([]*types.Var, n) - tags = make([]string, n) - for i := range fields { - fields[i], tags[i] = p.field(parent) - } - } - return -} - -func (p *importer) field(parent *types.Package) (*types.Var, string) { - pos := p.pos() - pkg, name, alias := p.fieldName(parent) - typ := p.typ(parent, nil) - tag := p.string() - - anonymous := false - if name == "" { - // anonymous field - typ must be T or *T and T must be a type name - switch typ := deref(typ).(type) { - case *types.Basic: // basic types are named types - pkg = nil // // objects defined in Universe scope have no package - name = typ.Name() - case *types.Named: - name = typ.Obj().Name() - default: - errorf("named base type expected") - } - anonymous = true - } else if alias { - // anonymous field: we have an explicit name because it's an alias - anonymous = true - } - - return types.NewField(pos, pkg, name, typ, anonymous), tag -} - -func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { - if n := p.int(); n > 0 { - methods = make([]*types.Func, n) - for i := range methods { - methods[i] = p.method(parent, baseType) - } - } - return -} - -func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { - pos := p.pos() - pkg, name, _ := p.fieldName(parent) - // If we don't have a baseType, use a nil receiver. - // A receiver using the actual interface type (which - // we don't know yet) will be filled in when we call - // types.Interface.Complete. - var recv *types.Var - if baseType != nil { - recv = types.NewVar(token.NoPos, parent, "", baseType) - } - params, isddd := p.paramList() - result, _ := p.paramList() - sig := types.NewSignature(recv, params, result, isddd) - return types.NewFunc(pos, pkg, name, sig) -} - -func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { - name = p.string() - pkg = parent - if pkg == nil { - // use the imported package instead - pkg = p.pkgList[0] - } - if p.version == 0 && name == "_" { - // version 0 didn't export a package for _ fields - return - } - switch name { - case "": - // 1) field name matches base type name and is exported: nothing to do - case "?": - // 2) field name matches base type name and is not exported: need package - name = "" - pkg = p.pkg() - case "@": - // 3) field name doesn't match type name (alias) - name = p.string() - alias = true - fallthrough - default: - if !exported(name) { - pkg = p.pkg() - } - } - return -} - -func (p *importer) paramList() (*types.Tuple, bool) { - n := p.int() - if n == 0 { - return nil, false - } - // negative length indicates unnamed parameters - named := true - if n < 0 { - n = -n - named = false - } - // n > 0 - params := make([]*types.Var, n) - isddd := false - for i := range params { - params[i], isddd = p.param(named) - } - return types.NewTuple(params...), isddd -} - -func (p *importer) param(named bool) (*types.Var, bool) { - t := p.typ(nil, nil) - td, isddd := t.(*dddSlice) - if isddd { - t = types.NewSlice(td.elem) - } - - var pkg *types.Package - var name string - if named { - name = p.string() - if name == "" { - errorf("expected named parameter") - } - if name != "_" { - pkg = p.pkg() - } - if i := strings.Index(name, "·"); i > 0 { - name = name[:i] // cut off gc-specific parameter numbering - } - } - - // read and discard compiler-specific info - p.string() - - return types.NewVar(token.NoPos, pkg, name, t), isddd -} - -func exported(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(ch) -} - -func (p *importer) value() constant.Value { - switch tag := p.tagOrIndex(); tag { - case falseTag: - return constant.MakeBool(false) - case trueTag: - return constant.MakeBool(true) - case int64Tag: - return constant.MakeInt64(p.int64()) - case floatTag: - return p.float() - case complexTag: - re := p.float() - im := p.float() - return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) - case stringTag: - return constant.MakeString(p.string()) - case unknownTag: - return constant.MakeUnknown() - default: - errorf("unexpected value tag %d", tag) // panics - panic("unreachable") - } -} - -func (p *importer) float() constant.Value { - sign := p.int() - if sign == 0 { - return constant.MakeInt64(0) - } - - exp := p.int() - mant := []byte(p.string()) // big endian - - // remove leading 0's if any - for len(mant) > 0 && mant[0] == 0 { - mant = mant[1:] - } - - // convert to little endian - // TODO(gri) go/constant should have a more direct conversion function - // (e.g., once it supports a big.Float based implementation) - for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { - mant[i], mant[j] = mant[j], mant[i] - } - - // adjust exponent (constant.MakeFromBytes creates an integer value, - // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) - exp -= len(mant) << 3 - if len(mant) > 0 { - for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { - exp++ - } - } - - x := constant.MakeFromBytes(mant) - switch { - case exp < 0: - d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) - x = constant.BinaryOp(x, token.QUO, d) - case exp > 0: - x = constant.Shift(x, token.SHL, uint(exp)) - } - - if sign < 0 { - x = constant.UnaryOp(token.SUB, x, 0) - } - return x -} - -// ---------------------------------------------------------------------------- -// Low-level decoders - -func (p *importer) tagOrIndex() int { - if p.debugFormat { - p.marker('t') - } - - return int(p.rawInt64()) -} - -func (p *importer) int() int { - x := p.int64() - if int64(int(x)) != x { - errorf("exported integer too large") - } - return int(x) -} - -func (p *importer) int64() int64 { - if p.debugFormat { - p.marker('i') - } - - return p.rawInt64() -} - -func (p *importer) path() string { - if p.debugFormat { - p.marker('p') - } - // if the path was seen before, i is its index (>= 0) - // (the empty string is at index 0) - i := p.rawInt64() - if i >= 0 { - return p.pathList[i] - } - // otherwise, i is the negative path length (< 0) - a := make([]string, -i) - for n := range a { - a[n] = p.string() - } - s := strings.Join(a, "/") - p.pathList = append(p.pathList, s) - return s -} - -func (p *importer) string() string { - if p.debugFormat { - p.marker('s') - } - // if the string was seen before, i is its index (>= 0) - // (the empty string is at index 0) - i := p.rawInt64() - if i >= 0 { - return p.strList[i] - } - // otherwise, i is the negative string length (< 0) - if n := int(-i); n <= cap(p.buf) { - p.buf = p.buf[:n] - } else { - p.buf = make([]byte, n) - } - for i := range p.buf { - p.buf[i] = p.rawByte() - } - s := string(p.buf) - p.strList = append(p.strList, s) - return s -} - -func (p *importer) marker(want byte) { - if got := p.rawByte(); got != want { - errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) - } - - pos := p.read - if n := int(p.rawInt64()); n != pos { - errorf("incorrect position: got %d; want %d", n, pos) - } -} - -// rawInt64 should only be used by low-level decoders. -func (p *importer) rawInt64() int64 { - i, err := binary.ReadVarint(p) - if err != nil { - errorf("read error: %v", err) - } - return i -} - -// rawStringln should only be used to read the initial version string. -func (p *importer) rawStringln(b byte) string { - p.buf = p.buf[:0] - for b != '\n' { - p.buf = append(p.buf, b) - b = p.rawByte() - } - return string(p.buf) -} - -// needed for binary.ReadVarint in rawInt64 -func (p *importer) ReadByte() (byte, error) { - return p.rawByte(), nil -} - -// byte is the bottleneck interface for reading p.data. -// It unescapes '|' 'S' to '$' and '|' '|' to '|'. -// rawByte should only be used by low-level decoders. -func (p *importer) rawByte() byte { - b := p.data[0] - r := 1 - if b == '|' { - b = p.data[1] - r = 2 - switch b { - case 'S': - b = '$' - case '|': - // nothing to do - default: - errorf("unexpected escape sequence in export data") - } - } - p.data = p.data[r:] - p.read += r - return b - -} - -// ---------------------------------------------------------------------------- -// Export format - -// Tags. Must be < 0. -const ( - // Objects - packageTag = -(iota + 1) - constTag - typeTag - varTag - funcTag - endTag - - // Types - namedTag - arrayTag - sliceTag - dddTag - structTag - pointerTag - signatureTag - interfaceTag - mapTag - chanTag - - // Values - falseTag - trueTag - int64Tag - floatTag - fractionTag // not used by gc - complexTag - stringTag - nilTag // only used by gc (appears in exported inlined function bodies) - unknownTag // not used by gc (only appears in packages with errors) - - // Type aliases - aliasTag -) - var predeclOnce sync.Once var predecl []types.Type // initialized lazily diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index 0372fb3a6..2d078ccb1 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -7,6 +7,18 @@ // Package gcimporter provides various functions for reading // gc-generated object files that can be used to implement the // Importer interface defined by the Go 1.5 standard library package. +// +// The encoding is deterministic: if the encoder is applied twice to +// the same types.Package data structure, both encodings are equal. +// This property may be important to avoid spurious changes in +// applications such as build systems. +// +// However, the encoder is not necessarily idempotent. Importing an +// exported package may yield a types.Package that, while it +// represents the same set of Go types as the original, may differ in +// the details of its internal representation. Because of these +// differences, re-encoding the imported package may yield a +// different, but equally valid, encoding of the package. package gcimporter // import "golang.org/x/tools/internal/gcimporter" import ( @@ -17,7 +29,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -209,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func switch hdr { case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + data, err = io.ReadAll(buf) if err != nil { break } @@ -218,20 +229,17 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func // Or, define a new standard go/types/gcexportdata package. fset := token.NewFileSet() - // The indexed export format starts with an 'i'; the older - // binary export format starts with a 'c', 'd', or 'v' - // (from "version"). Select appropriate importer. + // Select appropriate importer. if len(data) > 0 { switch data[0] { - case 'i': - _, pkg, err := IImportData(fset, packages, data[1:], id) - return pkg, err + case 'v', 'c', 'd': // binary, till go1.10 + return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'v', 'c', 'd': - _, pkg, err := BImportData(fset, packages, data, id) + case 'i': // indexed, till go1.19 + _, pkg, err := IImportData(fset, packages, data[1:], id) return pkg, err - case 'u': + case 'u': // unified, from go1.20 _, pkg, err := UImportData(fset, packages, data[1:size], id) return pkg, err diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index ba53cdcdd..2ee8c7016 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -22,17 +22,22 @@ import ( "strconv" "strings" + "golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/typeparams" ) // IExportShallow encodes "shallow" export data for the specified package. // -// No promises are made about the encoding other than that it can be -// decoded by the same version of IIExportShallow. If you plan to save -// export data in the file system, be sure to include a cryptographic -// digest of the executable in the key to avoid version skew. -func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { +// No promises are made about the encoding other than that it can be decoded by +// the same version of IIExportShallow. If you plan to save export data in the +// file system, be sure to include a cryptographic digest of the executable in +// the key to avoid version skew. +// +// If the provided reportf func is non-nil, it will be used for reporting bugs +// encountered during export. +// TODO(rfindley): remove reportf when we are confident enough in the new +// objectpath encoding. +func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) { // In principle this operation can only fail if out.Write fails, // but that's impossible for bytes.Buffer---and as a matter of // fact iexportCommon doesn't even check for I/O errors. @@ -44,22 +49,30 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { return out.Bytes(), err } -// IImportShallow decodes "shallow" types.Package data encoded by IExportShallow -// in the same executable. This function cannot import data from +// IImportShallow decodes "shallow" types.Package data encoded by +// IExportShallow in the same executable. This function cannot import data from // cmd/compile or gcexportdata.Write. -func IImportShallow(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string, insert InsertType) (*types.Package, error) { +// +// The importer calls getPackages to obtain package symbols for all +// packages mentioned in the export data, including the one being +// decoded. +// +// If the provided reportf func is non-nil, it will be used for reporting bugs +// encountered during import. +// TODO(rfindley): remove reportf when we are confident enough in the new +// objectpath encoding. +func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) { const bundle = false - pkgs, err := iimportCommon(fset, imports, data, bundle, path, insert) + const shallow = true + pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf) if err != nil { return nil, err } return pkgs[0], nil } -// InsertType is the type of a function that creates a types.TypeName -// object for a named type and inserts it into the scope of the -// specified Package. -type InsertType = func(pkg *types.Package, name string) +// ReportFunc is the type of a function used to report formatted bugs. +type ReportFunc = func(string, ...interface{}) // Current bundled export format version. Increase with each format change. // 0: initial implementation @@ -313,8 +326,9 @@ type iexporter struct { out *bytes.Buffer version int - shallow bool // don't put types from other packages in the index - localpkg *types.Package // (nil in bundle mode) + shallow bool // don't put types from other packages in the index + objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated + localpkg *types.Package // (nil in bundle mode) // allPkgs tracks all packages that have been referenced by // the export data, so we can ensure to include them in the @@ -354,6 +368,17 @@ func (p *iexporter) trace(format string, args ...interface{}) { fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) } +// objectpathEncoder returns the lazily allocated objectpath.Encoder to use +// when encoding objects in other packages during shallow export. +// +// Using a shared Encoder amortizes some of cost of objectpath search. +func (p *iexporter) objectpathEncoder() *objectpath.Encoder { + if p.objEncoder == nil { + p.objEncoder = new(objectpath.Encoder) + } + return p.objEncoder +} + // stringOff returns the offset of s within the string section. // If not already present, it's added to the end. func (p *iexporter) stringOff(s string) uint64 { @@ -413,7 +438,6 @@ type exportWriter struct { p *iexporter data intWriter - currPkg *types.Package prevFile string prevLine int64 prevColumn int64 @@ -436,7 +460,6 @@ func (p *iexporter) doDecl(obj types.Object) { }() } w := p.newWriter() - w.setPkg(obj.Pkg(), false) switch obj := obj.(type) { case *types.Var: @@ -457,7 +480,7 @@ func (p *iexporter) doDecl(obj types.Object) { } // Function. - if typeparams.ForSignature(sig).Len() == 0 { + if sig.TypeParams().Len() == 0 { w.tag('F') } else { w.tag('G') @@ -470,7 +493,7 @@ func (p *iexporter) doDecl(obj types.Object) { // // While importing the type parameters, tparamList computes and records // their export name, so that it can be later used when writing the index. - if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 { + if tparams := sig.TypeParams(); tparams.Len() > 0 { w.tparamList(obj.Name(), tparams, obj.Pkg()) } w.signature(sig) @@ -483,14 +506,14 @@ func (p *iexporter) doDecl(obj types.Object) { case *types.TypeName: t := obj.Type() - if tparam, ok := t.(*typeparams.TypeParam); ok { + if tparam, ok := t.(*types.TypeParam); ok { w.tag('P') w.pos(obj.Pos()) constraint := tparam.Constraint() if p.version >= iexportVersionGo1_18 { implicit := false if iface, _ := constraint.(*types.Interface); iface != nil { - implicit = typeparams.IsImplicit(iface) + implicit = iface.IsImplicit() } w.bool(implicit) } @@ -511,17 +534,17 @@ func (p *iexporter) doDecl(obj types.Object) { panic(internalErrorf("%s is not a defined type", t)) } - if typeparams.ForNamed(named).Len() == 0 { + if named.TypeParams().Len() == 0 { w.tag('T') } else { w.tag('U') } w.pos(obj.Pos()) - if typeparams.ForNamed(named).Len() > 0 { + if named.TypeParams().Len() > 0 { // While importing the type parameters, tparamList computes and records // their export name, so that it can be later used when writing the index. - w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg()) + w.tparamList(obj.Name(), named.TypeParams(), obj.Pkg()) } underlying := obj.Type().Underlying() @@ -541,7 +564,7 @@ func (p *iexporter) doDecl(obj types.Object) { // Receiver type parameters are type arguments of the receiver type, so // their name must be qualified before exporting recv. - if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 { + if rparams := sig.RecvTypeParams(); rparams.Len() > 0 { prefix := obj.Name() + "." + m.Name() for i := 0; i < rparams.Len(); i++ { rparam := rparams.At(i) @@ -673,6 +696,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) { w.pkg(obj.Pkg()) } +// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass +// it in explicitly into signatures and structs that may use it for +// constructing fields. func (w *exportWriter) typ(t types.Type, pkg *types.Package) { w.data.uint64(w.p.typOff(t, pkg)) } @@ -713,19 +739,19 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { } switch t := t.(type) { case *types.Named: - if targs := typeparams.NamedTypeArgs(t); targs.Len() > 0 { + if targs := t.TypeArgs(); targs.Len() > 0 { w.startType(instanceType) // TODO(rfindley): investigate if this position is correct, and if it // matters. w.pos(t.Obj().Pos()) w.typeList(targs, pkg) - w.typ(typeparams.NamedTypeOrigin(t), pkg) + w.typ(t.Origin(), pkg) return } w.startType(definedType) w.qualifiedType(t.Obj()) - case *typeparams.TypeParam: + case *types.TypeParam: w.startType(typeParamType) w.qualifiedType(t.Obj()) @@ -764,30 +790,53 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { case *types.Signature: w.startType(signatureType) - w.setPkg(pkg, true) + w.pkg(pkg) w.signature(t) case *types.Struct: w.startType(structType) n := t.NumFields() + // Even for struct{} we must emit some qualifying package, because that's + // what the compiler does, and thus that's what the importer expects. + fieldPkg := pkg if n > 0 { - w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects - } else { - w.setPkg(pkg, true) + fieldPkg = t.Field(0).Pkg() + } + if fieldPkg == nil { + // TODO(rfindley): improve this very hacky logic. + // + // The importer expects a package to be set for all struct types, even + // those with no fields. A better encoding might be to set NumFields + // before pkg. setPkg panics with a nil package, which may be possible + // to reach with invalid packages (and perhaps valid packages, too?), so + // (arbitrarily) set the localpkg if available. + // + // Alternatively, we may be able to simply guarantee that pkg != nil, by + // reconsidering the encoding of constant values. + if w.p.shallow { + fieldPkg = w.p.localpkg + } else { + panic(internalErrorf("no package to set for empty struct")) + } } + w.pkg(fieldPkg) w.uint64(uint64(n)) + for i := 0; i < n; i++ { f := t.Field(i) + if w.p.shallow { + w.objectPath(f) + } w.pos(f.Pos()) w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg - w.typ(f.Type(), pkg) + w.typ(f.Type(), fieldPkg) w.bool(f.Anonymous()) w.string(t.Tag(i)) // note (or tag) } case *types.Interface: w.startType(interfaceType) - w.setPkg(pkg, true) + w.pkg(pkg) n := t.NumEmbeddeds() w.uint64(uint64(n)) @@ -802,17 +851,23 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { w.typ(ft, tPkg) } + // See comment for struct fields. In shallow mode we change the encoding + // for interface methods that are promoted from other packages. + n = t.NumExplicitMethods() w.uint64(uint64(n)) for i := 0; i < n; i++ { m := t.ExplicitMethod(i) + if w.p.shallow { + w.objectPath(m) + } w.pos(m.Pos()) w.string(m.Name()) sig, _ := m.Type().(*types.Signature) w.signature(sig) } - case *typeparams.Union: + case *types.Union: w.startType(unionType) nt := t.Len() w.uint64(uint64(nt)) @@ -827,12 +882,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { } } -func (w *exportWriter) setPkg(pkg *types.Package, write bool) { - if write { - w.pkg(pkg) +// objectPath writes the package and objectPath to use to look up obj in a +// different package, when encoding in "shallow" mode. +// +// When doing a shallow import, the importer creates only the local package, +// and requests package symbols for dependencies from the client. +// However, certain types defined in the local package may hold objects defined +// (perhaps deeply) within another package. +// +// For example, consider the following: +// +// package a +// func F() chan * map[string] struct { X int } +// +// package b +// import "a" +// var B = a.F() +// +// In this example, the type of b.B holds fields defined in package a. +// In order to have the correct canonical objects for the field defined in the +// type of B, they are encoded as objectPaths and later looked up in the +// importer. The same problem applies to interface methods. +func (w *exportWriter) objectPath(obj types.Object) { + if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg { + // obj.Pkg() may be nil for the builtin error.Error. + // In this case, or if obj is declared in the local package, no need to + // encode. + w.string("") + return } - - w.currPkg = pkg + objectPath, err := w.p.objectpathEncoder().For(obj) + if err != nil { + // Fall back to the empty string, which will cause the importer to create a + // new object, which matches earlier behavior. Creating a new object is + // sufficient for many purposes (such as type checking), but causes certain + // references algorithms to fail (golang/go#60819). However, we didn't + // notice this problem during months of gopls@v0.12.0 testing. + // + // TODO(golang/go#61674): this workaround is insufficient, as in the case + // where the field forwarded from an instantiated type that may not appear + // in the export data of the original package: + // + // // package a + // type A[P any] struct{ F P } + // + // // package b + // type B a.A[int] + // + // We need to update references algorithms not to depend on this + // de-duplication, at which point we may want to simply remove the + // workaround here. + w.string("") + return + } + w.string(string(objectPath)) + w.pkg(obj.Pkg()) } func (w *exportWriter) signature(sig *types.Signature) { @@ -843,14 +947,14 @@ func (w *exportWriter) signature(sig *types.Signature) { } } -func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) { +func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) { w.uint64(uint64(ts.Len())) for i := 0; i < ts.Len(); i++ { w.typ(ts.At(i), pkg) } } -func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) { +func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) { ll := uint64(list.Len()) w.uint64(ll) for i := 0; i < list.Len(); i++ { @@ -868,7 +972,7 @@ const blankMarker = "$" // differs from its actual object name: it is prefixed with a qualifier, and // blank type parameter names are disambiguated by their index in the type // parameter list. -func tparamExportName(prefix string, tparam *typeparams.TypeParam) string { +func tparamExportName(prefix string, tparam *types.TypeParam) string { assert(prefix != "") name := tparam.Obj().Name() if name == "_" { @@ -913,6 +1017,17 @@ func (w *exportWriter) value(typ types.Type, v constant.Value) { w.int64(int64(v.Kind())) } + if v.Kind() == constant.Unknown { + // golang/go#60605: treat unknown constant values as if they have invalid type + // + // This loses some fidelity over the package type-checked from source, but that + // is acceptable. + // + // TODO(rfindley): we should switch on the recorded constant kind rather + // than the constant type + return + } + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { case types.IsBoolean: w.bool(constant.BoolVal(v)) @@ -969,6 +1084,16 @@ func constantToFloat(x constant.Value) *big.Float { return &f } +func valueToRat(x constant.Value) *big.Rat { + // Convert little-endian to big-endian. + // I can't believe this is necessary. + bytes := constant.Bytes(x) + for i := 0; i < len(bytes)/2; i++ { + bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] + } + return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) +} + // mpint exports a multi-precision integer. // // For unsigned types, small values are written out as a single @@ -1178,3 +1303,19 @@ func (q *objQueue) popHead() types.Object { q.head++ return obj } + +// internalError represents an error generated inside this package. +type internalError string + +func (e internalError) Error() string { return "gcimporter: " + string(e) } + +// TODO(adonovan): make this call panic, so that it's symmetric with errorf. +// Otherwise it's easy to forget to do anything with the error. +// +// TODO(adonovan): also, consider switching the names "errorf" and +// "internalErrorf" as the former is used for bugs, whose cause is +// internal inconsistency, whereas the latter is used for ordinary +// situations like bad input, whose cause is external. +func internalErrorf(format string, args ...interface{}) error { + return internalError(fmt.Sprintf(format, args...)) +} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index 448f903e8..9bde15e3b 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/go/types/objectpath" ) type intReader struct { @@ -85,7 +85,7 @@ const ( // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { - pkgs, err := iimportCommon(fset, imports, data, false, path, nil) + pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil) if err != nil { return 0, nil, err } @@ -94,10 +94,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // IImportBundle imports a set of packages from the serialized package bundle. func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { - return iimportCommon(fset, imports, data, true, "", nil) + return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil) } -func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) { +// A GetPackagesFunc function obtains the non-nil symbols for a set of +// packages, creating and recursively importing them as needed. An +// implementation should store each package symbol is in the Pkg +// field of the items array. +// +// Any error causes importing to fail. This can be used to quickly read +// the import manifest of an export data file without fully decoding it. +type GetPackagesFunc = func(items []GetPackagesItem) error + +// A GetPackagesItem is a request from the importer for the package +// symbol of the specified name and path. +type GetPackagesItem struct { + Name, Path string + Pkg *types.Package // to be filled in by GetPackagesFunc call + + // private importer state + pathOffset uint64 + nameIndex map[string]uint64 +} + +// GetPackagesFromMap returns a GetPackagesFunc that retrieves +// packages from the given map of package path to package. +// +// The returned function may mutate m: each requested package that is not +// found is created with types.NewPackage and inserted into m. +func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc { + return func(items []GetPackagesItem) error { + for i, item := range items { + pkg, ok := m[item.Path] + if !ok { + pkg = types.NewPackage(item.Path, item.Name) + m[item.Path] = pkg + } + items[i].Pkg = pkg + } + return nil + } +} + +func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) { const currentVersion = iexportVersionCurrent version := int64(-1) if !debug { @@ -108,7 +147,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data } else if version > currentVersion { err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) } else { - err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + err = fmt.Errorf("internal error while importing %q (%v); please report an issue", path, e) } } }() @@ -117,11 +156,8 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data r := &intReader{bytes.NewReader(data), path} if bundle { - bundleVersion := r.uint64() - switch bundleVersion { - case bundleVersion: - default: - errorf("unknown bundle format version %d", bundleVersion) + if v := r.uint64(); v != bundleVersion { + errorf("unknown bundle format version %d", v) } } @@ -139,7 +175,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data sLen := int64(r.uint64()) var fLen int64 var fileOffset []uint64 - if insert != nil { + if shallow { // Shallow mode uses a different position encoding. fLen = int64(r.uint64()) fileOffset = make([]uint64, r.uint64()) @@ -158,7 +194,8 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data p := iimporter{ version: int(version), ipath: path, - insert: insert, + shallow: shallow, + reportf: reportf, stringData: stringData, stringCache: make(map[uint64]string), @@ -185,8 +222,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data p.typCache[uint64(i)] = pt } - pkgList := make([]*types.Package, r.uint64()) - for i := range pkgList { + // Gather the relevant packages from the manifest. + items := make([]GetPackagesItem, r.uint64()) + for i := range items { pkgPathOff := r.uint64() pkgPath := p.stringAt(pkgPathOff) pkgName := p.stringAt(r.uint64()) @@ -195,30 +233,42 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data if pkgPath == "" { pkgPath = path } - pkg := imports[pkgPath] - if pkg == nil { - pkg = types.NewPackage(pkgPath, pkgName) - imports[pkgPath] = pkg - } else if pkg.Name() != pkgName { - errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) - } - if i == 0 && !bundle { - p.localpkg = pkg - } - - p.pkgCache[pkgPathOff] = pkg + items[i].Name = pkgName + items[i].Path = pkgPath + items[i].pathOffset = pkgPathOff // Read index for package. nameIndex := make(map[string]uint64) nSyms := r.uint64() - // In shallow mode we don't expect an index for other packages. - assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil) + // In shallow mode, only the current package (i=0) has an index. + assert(!(shallow && i > 0 && nSyms != 0)) for ; nSyms > 0; nSyms-- { name := p.stringAt(r.uint64()) nameIndex[name] = r.uint64() } - p.pkgIndex[pkg] = nameIndex + items[i].nameIndex = nameIndex + } + + // Request packages all at once from the client, + // enabling a parallel implementation. + if err := getPackages(items); err != nil { + return nil, err // don't wrap this error + } + + // Check the results and complete the index. + pkgList := make([]*types.Package, len(items)) + for i, item := range items { + pkg := item.Pkg + if pkg == nil { + errorf("internal error: getPackages returned nil package for %q", item.Path) + } else if pkg.Path() != item.Path { + errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path) + } else if pkg.Name() != item.Name { + errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name) + } + p.pkgCache[item.pathOffset] = pkg + p.pkgIndex[pkg] = item.nameIndex pkgList[i] = pkg } @@ -270,18 +320,25 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data // Therefore, we defer calling SetConstraint there, and call it here instead // after all types are complete. for _, d := range p.later { - typeparams.SetTypeParamConstraint(d.t, d.constraint) + d.t.SetConstraint(d.constraint) } for _, typ := range p.interfaceList { typ.Complete() } + // Workaround for golang/go#61561. See the doc for instanceList for details. + for _, typ := range p.instanceList { + if iface, _ := typ.Underlying().(*types.Interface); iface != nil { + iface.Complete() + } + } + return pkgs, nil } type setConstraintArgs struct { - t *typeparams.TypeParam + t *types.TypeParam constraint types.Type } @@ -289,8 +346,8 @@ type iimporter struct { version int ipath string - localpkg *types.Package - insert func(pkg *types.Package, name string) // "shallow" mode only + shallow bool + reportf ReportFunc // if non-nil, used to report bugs stringData []byte stringCache map[uint64]string @@ -307,6 +364,12 @@ type iimporter struct { fake fakeFileSet interfaceList []*types.Interface + // Workaround for the go/types bug golang/go#61561: instances produced during + // instantiation may contain incomplete interfaces. Here we only complete the + // underlying type of the instance, which is the most common case but doesn't + // handle parameterized interface literals defined deeper in the type. + instanceList []types.Type // instances for later completion (see golang/go#61561) + // Arguments for calls to SetConstraint that are deferred due to recursive types later []setConstraintArgs @@ -338,13 +401,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) { off, ok := p.pkgIndex[pkg][name] if !ok { - // In "shallow" mode, call back to the application to - // find the object and insert it into the package scope. - if p.insert != nil { - assert(pkg != p.localpkg) - p.insert(pkg, name) // "can't fail" - return - } + // In deep mode, the index should be complete. In shallow + // mode, we should have already recursively loaded necessary + // dependencies so the above Lookup succeeds. errorf("%v.%v not in index", pkg, name) } @@ -489,7 +548,7 @@ func (r *importReader) obj(name string) { r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) case 'F', 'G': - var tparams []*typeparams.TypeParam + var tparams []*types.TypeParam if tag == 'G' { tparams = r.tparamList() } @@ -506,7 +565,7 @@ func (r *importReader) obj(name string) { r.declare(obj) if tag == 'U' { tparams := r.tparamList() - typeparams.SetForNamed(named, tparams) + named.SetTypeParams(tparams) } underlying := r.p.typAt(r.uint64(), named).Underlying() @@ -523,12 +582,12 @@ func (r *importReader) obj(name string) { // typeparams being used in the method sig/body). base := baseType(recv.Type()) assert(base != nil) - targs := typeparams.NamedTypeArgs(base) - var rparams []*typeparams.TypeParam + targs := base.TypeArgs() + var rparams []*types.TypeParam if targs.Len() > 0 { - rparams = make([]*typeparams.TypeParam, targs.Len()) + rparams = make([]*types.TypeParam, targs.Len()) for i := range rparams { - rparams[i] = targs.At(i).(*typeparams.TypeParam) + rparams[i] = targs.At(i).(*types.TypeParam) } } msig := r.signature(recv, rparams, nil) @@ -546,7 +605,7 @@ func (r *importReader) obj(name string) { } name0 := tparamName(name) tn := types.NewTypeName(pos, r.currPkg, name0, nil) - t := typeparams.NewTypeParam(tn, nil) + t := types.NewTypeParam(tn, nil) // To handle recursive references to the typeparam within its // bound, save the partial type in tparamIndex before reading the bounds. @@ -562,7 +621,7 @@ func (r *importReader) obj(name string) { if iface == nil { errorf("non-interface constraint marked implicit") } - typeparams.MarkImplicit(iface) + iface.MarkImplicit() } // The constraint type may not be complete, if we // are in the middle of a type recursion involving type @@ -711,7 +770,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) { } func (r *importReader) pos() token.Pos { - if r.p.insert != nil { // shallow mode + if r.p.shallow { + // precise offsets are encoded only in shallow mode return r.posv2() } if r.p.version >= iexportVersionPosCol { @@ -812,13 +872,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { fields := make([]*types.Var, r.uint64()) tags := make([]string, len(fields)) for i := range fields { + var field *types.Var + if r.p.shallow { + field, _ = r.objectPathObject().(*types.Var) + } + fpos := r.pos() fname := r.ident() ftyp := r.typ() emb := r.bool() tag := r.string() - fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + // Either this is not a shallow import, the field is local, or the + // encoded objectPath failed to produce an object (a bug). + // + // Even in this last, buggy case, fall back on creating a new field. As + // discussed in iexport.go, this is not correct, but mostly works and is + // preferable to failing (for now at least). + if field == nil { + field = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + } + + fields[i] = field tags[i] = tag } return types.NewStruct(fields, tags) @@ -834,6 +909,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { methods := make([]*types.Func, r.uint64()) for i := range methods { + var method *types.Func + if r.p.shallow { + method, _ = r.objectPathObject().(*types.Func) + } + mpos := r.pos() mname := r.ident() @@ -843,9 +923,12 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { if base != nil { recv = types.NewVar(token.NoPos, r.currPkg, "", base) } - msig := r.signature(recv, nil, nil) - methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) + + if method == nil { + method = types.NewFunc(mpos, r.currPkg, mname, msig) + } + methods[i] = method } typ := newInterface(methods, embeddeds) @@ -882,18 +965,21 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { // The imported instantiated type doesn't include any methods, so // we must always use the methods of the base (orig) type. // TODO provide a non-nil *Environment - t, _ := typeparams.Instantiate(nil, baseType, targs, false) + t, _ := types.Instantiate(nil, baseType, targs, false) + + // Workaround for golang/go#61561. See the doc for instanceList for details. + r.p.instanceList = append(r.p.instanceList, t) return t case unionType: if r.p.version < iexportVersionGenerics { errorf("unexpected instantiation type") } - terms := make([]*typeparams.Term, r.uint64()) + terms := make([]*types.Term, r.uint64()) for i := range terms { - terms[i] = typeparams.NewTerm(r.bool(), r.typ()) + terms[i] = types.NewTerm(r.bool(), r.typ()) } - return typeparams.NewUnion(terms) + return types.NewUnion(terms) } } @@ -901,23 +987,43 @@ func (r *importReader) kind() itag { return itag(r.uint64()) } -func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { +// objectPathObject is the inverse of exportWriter.objectPath. +// +// In shallow mode, certain fields and methods may need to be looked up in an +// imported package. See the doc for exportWriter.objectPath for a full +// explanation. +func (r *importReader) objectPathObject() types.Object { + objPath := objectpath.Path(r.string()) + if objPath == "" { + return nil + } + pkg := r.pkg() + obj, err := objectpath.Object(pkg, objPath) + if err != nil { + if r.p.reportf != nil { + r.p.reportf("failed to find object for objectPath %q: %v", objPath, err) + } + } + return obj +} + +func (r *importReader) signature(recv *types.Var, rparams []*types.TypeParam, tparams []*types.TypeParam) *types.Signature { params := r.paramList() results := r.paramList() variadic := params.Len() > 0 && r.bool() - return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic) + return types.NewSignatureType(recv, rparams, tparams, params, results, variadic) } -func (r *importReader) tparamList() []*typeparams.TypeParam { +func (r *importReader) tparamList() []*types.TypeParam { n := r.uint64() if n == 0 { return nil } - xs := make([]*typeparams.TypeParam, n) + xs := make([]*types.TypeParam, n) for i := range xs { // Note: the standard library importer is tolerant of nil types here, // though would panic in SetTypeParams. - xs[i] = r.typ().(*typeparams.TypeParam) + xs[i] = r.typ().(*types.TypeParam) } return xs } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go index b285a11ce..b977435f6 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go @@ -10,8 +10,10 @@ package gcimporter import ( + "fmt" "go/token" "go/types" + "sort" "strings" "golang.org/x/tools/internal/pkgbits" @@ -62,6 +64,14 @@ type typeInfo struct { } func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + if !debug { + defer func() { + if x := recover(); x != nil { + err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x) + } + }() + } + s := string(data) s = s[:strings.LastIndex(s, "\n$$\n")] input := pkgbits.NewPkgDecoder(path, s) @@ -121,6 +131,16 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st iface.Complete() } + // Imports() of pkg are all of the transitive packages that were loaded. + var imps []*types.Package + for _, imp := range pr.pkgs { + if imp != nil && imp != pkg { + imps = append(imps, imp) + } + } + sort.Sort(byPath(imps)) + pkg.SetImports(imps) + pkg.MarkComplete() return pkg } @@ -260,39 +280,9 @@ func (r *reader) doPkg() *types.Package { pkg := types.NewPackage(path, name) r.p.imports[path] = pkg - imports := make([]*types.Package, r.Len()) - for i := range imports { - imports[i] = r.pkg() - } - pkg.SetImports(flattenImports(imports)) - return pkg } -// flattenImports returns the transitive closure of all imported -// packages rooted from pkgs. -func flattenImports(pkgs []*types.Package) []*types.Package { - var res []*types.Package - seen := make(map[*types.Package]struct{}) - for _, pkg := range pkgs { - if _, ok := seen[pkg]; ok { - continue - } - seen[pkg] = struct{}{} - res = append(res, pkg) - - // pkg.Imports() is already flattened. - for _, pkg := range pkg.Imports() { - if _, ok := seen[pkg]; ok { - continue - } - seen[pkg] = struct{}{} - res = append(res, pkg) - } - } - return res -} - // @@@ Types func (r *reader) typ() types.Type { diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index d50551693..55312522d 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -8,10 +8,13 @@ package gocommand import ( "bytes" "context" + "errors" "fmt" "io" "log" "os" + "os/exec" + "reflect" "regexp" "runtime" "strconv" @@ -19,9 +22,10 @@ import ( "sync" "time" - exec "golang.org/x/sys/execabs" - "golang.org/x/tools/internal/event" + "golang.org/x/tools/internal/event/keys" + "golang.org/x/tools/internal/event/label" + "golang.org/x/tools/internal/event/tag" ) // An Runner will run go command invocations and serialize @@ -51,9 +55,19 @@ func (runner *Runner) initialize() { // 1.14: go: updating go.mod: existing contents have changed since last read var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) +// verb is an event label for the go command verb. +var verb = keys.NewString("verb", "go command verb") + +func invLabels(inv Invocation) []label.Label { + return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)} +} + // Run is a convenience wrapper around RunRaw. // It returns only stdout and a "friendly" error. func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { + ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...) + defer done() + stdout, _, friendly, _ := runner.RunRaw(ctx, inv) return stdout, friendly } @@ -61,13 +75,19 @@ func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, e // RunPiped runs the invocation serially, always waiting for any concurrent // invocations to complete first. func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { + ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...) + defer done() + _, err := runner.runPiped(ctx, inv, stdout, stderr) return err } // RunRaw runs the invocation, serializing requests only if they fight over // go.mod changes. +// Postcondition: both error results have same nilness. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { + ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) + defer done() // Make sure the runner is always initialized. runner.initialize() @@ -75,23 +95,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) // If we encounter a load concurrency error, we need to retry serially. - if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { - return stdout, stderr, friendlyErr, err + if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) { + event.Error(ctx, "Load concurrency error, will retry serially", err) + + // Run serially by calling runPiped. + stdout.Reset() + stderr.Reset() + friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) } - event.Error(ctx, "Load concurrency error, will retry serially", err) - // Run serially by calling runPiped. - stdout.Reset() - stderr.Reset() - friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { // Wait for 1 worker to become available. select { case <-ctx.Done(): - return nil, nil, nil, ctx.Err() + return nil, nil, ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: defer func() { <-runner.inFlight }() } @@ -101,6 +122,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { // Make sure the runner is always initialized. runner.initialize() @@ -109,7 +131,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde // runPiped commands. select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.serialized <- struct{}{}: defer func() { <-runner.serialized }() } @@ -119,7 +141,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde for i := 0; i < maxInFlight; i++ { select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: // Make sure we always "return" any workers we took. defer func() { <-runner.inFlight }() @@ -152,6 +174,7 @@ type Invocation struct { Logf func(format string, args ...interface{}) } +// Postcondition: both error results have same nilness. func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { rawError = i.run(ctx, stdout, stderr) if rawError != nil { @@ -215,6 +238,18 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { cmd := exec.Command("go", goArgs...) cmd.Stdout = stdout cmd.Stderr = stderr + + // cmd.WaitDelay was added only in go1.20 (see #50436). + if waitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); waitDelay.IsValid() { + // https://go.dev/issue/59541: don't wait forever copying stderr + // after the command has exited. + // After CL 484741 we copy stdout manually, so we we'll stop reading that as + // soon as ctx is done. However, we also don't want to wait around forever + // for stderr. Give a much-longer-than-reasonable delay and then assume that + // something has wedged in the kernel or runtime. + waitDelay.Set(reflect.ValueOf(30 * time.Second)) + } + // On darwin the cwd gets resolved to the real path, which breaks anything that // expects the working directory to keep the original path, including the // go command when dealing with modules. @@ -229,6 +264,7 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir) cmd.Dir = i.WorkingDir } + defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) return runCmdContext(ctx, cmd) @@ -242,10 +278,85 @@ var DebugHangingGoCommands = false // runCmdContext is like exec.CommandContext except it sends os.Interrupt // before os.Kill. -func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { - if err := cmd.Start(); err != nil { +func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { + // If cmd.Stdout is not an *os.File, the exec package will create a pipe and + // copy it to the Writer in a goroutine until the process has finished and + // either the pipe reaches EOF or command's WaitDelay expires. + // + // However, the output from 'go list' can be quite large, and we don't want to + // keep reading (and allocating buffers) if we've already decided we don't + // care about the output. We don't want to wait for the process to finish, and + // we don't wait to wait for the WaitDelay to expire either. + // + // Instead, if cmd.Stdout requires a copying goroutine we explicitly replace + // it with a pipe (which is an *os.File), which we can close in order to stop + // copying output as soon as we realize we don't care about it. + var stdoutW *os.File + if cmd.Stdout != nil { + if _, ok := cmd.Stdout.(*os.File); !ok { + var stdoutR *os.File + stdoutR, stdoutW, err = os.Pipe() + if err != nil { + return err + } + prevStdout := cmd.Stdout + cmd.Stdout = stdoutW + + stdoutErr := make(chan error, 1) + go func() { + _, err := io.Copy(prevStdout, stdoutR) + if err != nil { + err = fmt.Errorf("copying stdout: %w", err) + } + stdoutErr <- err + }() + defer func() { + // We started a goroutine to copy a stdout pipe. + // Wait for it to finish, or terminate it if need be. + var err2 error + select { + case err2 = <-stdoutErr: + stdoutR.Close() + case <-ctx.Done(): + stdoutR.Close() + // Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close + // should cause the Read call in io.Copy to unblock and return + // immediately, but we still need to receive from stdoutErr to confirm + // that it has happened. + <-stdoutErr + err2 = ctx.Err() + } + if err == nil { + err = err2 + } + }() + + // Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the + // same writer, and have a type that can be compared with ==, at most + // one goroutine at a time will call Write.†+ // + // Since we're starting a goroutine that writes to cmd.Stdout, we must + // also update cmd.Stderr so that it still holds. + func() { + defer func() { recover() }() + if cmd.Stderr == prevStdout { + cmd.Stderr = cmd.Stdout + } + }() + } + } + + err = cmd.Start() + if stdoutW != nil { + // The child process has inherited the pipe file, + // so close the copy held in this process. + stdoutW.Close() + stdoutW = nil + } + if err != nil { return err } + resChan := make(chan error, 1) go func() { resChan <- cmd.Wait() @@ -253,11 +364,14 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { // If we're interested in debugging hanging Go commands, stop waiting after a // minute and panic with interesting information. - if DebugHangingGoCommands { + debug := DebugHangingGoCommands + if debug { + timer := time.NewTimer(1 * time.Minute) + defer timer.Stop() select { case err := <-resChan: return err - case <-time.After(1 * time.Minute): + case <-timer.C: HandleHangingGoCommand(cmd.Process) case <-ctx.Done(): } @@ -270,30 +384,25 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { } // Cancelled. Interrupt and see if it ends voluntarily. - cmd.Process.Signal(os.Interrupt) - select { - case err := <-resChan: - return err - case <-time.After(time.Second): + if err := cmd.Process.Signal(os.Interrupt); err == nil { + // (We used to wait only 1s but this proved + // fragile on loaded builder machines.) + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + select { + case err := <-resChan: + return err + case <-timer.C: + } } // Didn't shut down in response to interrupt. Kill it hard. // TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT // on certain platforms, such as unix. - if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands { - // Don't panic here as this reliably fails on windows with EINVAL. + if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug { log.Printf("error killing the Go command: %v", err) } - // See above: don't wait indefinitely if we're debugging hanging Go commands. - if DebugHangingGoCommands { - select { - case err := <-resChan: - return err - case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill - HandleHangingGoCommand(cmd.Process) - } - } return <-resChan } diff --git a/vendor/golang.org/x/tools/internal/gocommand/version.go b/vendor/golang.org/x/tools/internal/gocommand/version.go index 307a76d47..446c5846a 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/version.go +++ b/vendor/golang.org/x/tools/internal/gocommand/version.go @@ -23,21 +23,11 @@ import ( func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) { inv.Verb = "list" inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`} - inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off") - // Unset any unneeded flags, and remove them from BuildFlags, if they're - // present. - inv.ModFile = "" + inv.BuildFlags = nil // This is not a build command. inv.ModFlag = "" - var buildFlags []string - for _, flag := range inv.BuildFlags { - // Flags can be prefixed by one or two dashes. - f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-") - if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") { - continue - } - buildFlags = append(buildFlags, flag) - } - inv.BuildFlags = buildFlags + inv.ModFile = "" + inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off") + stdoutBytes, err := r.Run(ctx, inv) if err != nil { return 0, err diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go new file mode 100644 index 000000000..52f74e643 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -0,0 +1,331 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gopathwalk is like filepath.Walk but specialized for finding Go +// packages, particularly in $GOPATH and $GOROOT. +package gopathwalk + +import ( + "bufio" + "bytes" + "io/fs" + "log" + "os" + "path/filepath" + "strings" + "time" +) + +// Options controls the behavior of a Walk call. +type Options struct { + // If Logf is non-nil, debug logging is enabled through this function. + Logf func(format string, args ...interface{}) + // Search module caches. Also disables legacy goimports ignore rules. + ModulesEnabled bool +} + +// RootType indicates the type of a Root. +type RootType int + +const ( + RootUnknown RootType = iota + RootGOROOT + RootGOPATH + RootCurrentModule + RootModuleCache + RootOther +) + +// A Root is a starting point for a Walk. +type Root struct { + Path string + Type RootType +} + +// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called with the absolute +// paths of the containing source directory and the package directory. +func Walk(roots []Root, add func(root Root, dir string), opts Options) { + WalkSkip(roots, add, func(Root, string) bool { return false }, opts) +} + +// WalkSkip walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called with the absolute +// paths of the containing source directory and the package directory. +// For each directory that will be scanned, skip will be called +// with the absolute paths of the containing source directory and the directory. +// If skip returns false on a directory it will be processed. +func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) { + for _, root := range roots { + walkDir(root, add, skip, opts) + } +} + +// walkDir creates a walker and starts fastwalk with this walker. +func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) { + if _, err := os.Stat(root.Path); os.IsNotExist(err) { + if opts.Logf != nil { + opts.Logf("skipping nonexistent directory: %v", root.Path) + } + return + } + start := time.Now() + if opts.Logf != nil { + opts.Logf("scanning %s", root.Path) + } + + w := &walker{ + root: root, + add: add, + skip: skip, + opts: opts, + added: make(map[string]bool), + } + w.init() + + // Add a trailing path separator to cause filepath.WalkDir to traverse symlinks. + path := root.Path + if len(path) == 0 { + path = "." + string(filepath.Separator) + } else if !os.IsPathSeparator(path[len(path)-1]) { + path = path + string(filepath.Separator) + } + + if err := filepath.WalkDir(path, w.walk); err != nil { + logf := opts.Logf + if logf == nil { + logf = log.Printf + } + logf("scanning directory %v: %v", root.Path, err) + } + + if opts.Logf != nil { + opts.Logf("scanned %s in %v", root.Path, time.Since(start)) + } +} + +// walker is the callback for fastwalk.Walk. +type walker struct { + root Root // The source directory to scan. + add func(Root, string) // The callback that will be invoked for every possible Go package dir. + skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true. + opts Options // Options passed to Walk by the user. + + pathSymlinks []os.FileInfo + ignoredDirs []string + + added map[string]bool +} + +// init initializes the walker based on its Options +func (w *walker) init() { + var ignoredPaths []string + if w.root.Type == RootModuleCache { + ignoredPaths = []string{"cache"} + } + if !w.opts.ModulesEnabled && w.root.Type == RootGOPATH { + ignoredPaths = w.getIgnoredDirs(w.root.Path) + ignoredPaths = append(ignoredPaths, "v", "mod") + } + + for _, p := range ignoredPaths { + full := filepath.Join(w.root.Path, p) + w.ignoredDirs = append(w.ignoredDirs, full) + if w.opts.Logf != nil { + w.opts.Logf("Directory added to ignore list: %s", full) + } + } +} + +// getIgnoredDirs reads an optional config file at /.goimportsignore +// of relative directories to ignore when scanning for go files. +// The provided path is one of the $GOPATH entries with "src" appended. +func (w *walker) getIgnoredDirs(path string) []string { + file := filepath.Join(path, ".goimportsignore") + slurp, err := os.ReadFile(file) + if w.opts.Logf != nil { + if err != nil { + w.opts.Logf("%v", err) + } else { + w.opts.Logf("Read %s", file) + } + } + if err != nil { + return nil + } + + var ignoredDirs []string + bs := bufio.NewScanner(bytes.NewReader(slurp)) + for bs.Scan() { + line := strings.TrimSpace(bs.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + ignoredDirs = append(ignoredDirs, line) + } + return ignoredDirs +} + +// shouldSkipDir reports whether the file should be skipped or not. +func (w *walker) shouldSkipDir(dir string) bool { + for _, ignoredDir := range w.ignoredDirs { + if dir == ignoredDir { + return true + } + } + if w.skip != nil { + // Check with the user specified callback. + return w.skip(w.root, dir) + } + return false +} + +// walk walks through the given path. +// +// Errors are logged if w.opts.Logf is non-nil, but otherwise ignored: +// walk returns only nil or fs.SkipDir. +func (w *walker) walk(path string, d fs.DirEntry, err error) error { + if err != nil { + // We have no way to report errors back through Walk or WalkSkip, + // so just log and ignore them. + if w.opts.Logf != nil { + w.opts.Logf("%v", err) + } + if d == nil { + // Nothing more to do: the error prevents us from knowing + // what path even represents. + return nil + } + } + + if d.Type().IsRegular() { + if !strings.HasSuffix(path, ".go") { + return nil + } + + dir := filepath.Dir(path) + if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) { + // Doesn't make sense to have regular files + // directly in your $GOPATH/src or $GOROOT/src. + return nil + } + + if !w.added[dir] { + w.add(w.root, dir) + w.added[dir] = true + } + return nil + } + + if d.IsDir() { + base := filepath.Base(path) + if base == "" || base[0] == '.' || base[0] == '_' || + base == "testdata" || + (w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") || + (!w.opts.ModulesEnabled && base == "node_modules") { + return fs.SkipDir + } + if w.shouldSkipDir(path) { + return fs.SkipDir + } + return nil + } + + if d.Type()&os.ModeSymlink != 0 { + // TODO(bcmills): 'go list all' itself ignores symlinks within GOROOT/src + // and GOPATH/src. Do we really need to traverse them here? If so, why? + + fi, err := os.Stat(path) + if err != nil || !fi.IsDir() { + // Not a directory. Just walk the file (or broken link) and be done. + return w.walk(path, fs.FileInfoToDirEntry(fi), err) + } + + // Avoid walking symlink cycles: if we have already followed a symlink to + // this directory as a parent of itself, don't follow it again. + // + // This doesn't catch the first time through a cycle, but it also minimizes + // the number of extra stat calls we make if we *don't* encounter a cycle. + // Since we don't actually expect to encounter symlink cycles in practice, + // this seems like the right tradeoff. + for _, parent := range w.pathSymlinks { + if os.SameFile(fi, parent) { + return nil + } + } + + w.pathSymlinks = append(w.pathSymlinks, fi) + defer func() { + w.pathSymlinks = w.pathSymlinks[:len(w.pathSymlinks)-1] + }() + + // On some platforms the OS (or the Go os package) sometimes fails to + // resolve directory symlinks before a trailing slash + // (even though POSIX requires it to do so). + // + // On macOS that failure may be caused by a known libc/kernel bug; + // see https://go.dev/issue/59586. + // + // On Windows before Go 1.21, it may be caused by a bug in + // os.Lstat (fixed in https://go.dev/cl/463177). + // + // Since we need to handle this explicitly on broken platforms anyway, + // it is simplest to just always do that and not rely on POSIX pathname + // resolution to walk the directory (such as by calling WalkDir with + // a trailing slash appended to the path). + // + // Instead, we make a sequence of walk calls — directly and through + // recursive calls to filepath.WalkDir — simulating what WalkDir would do + // if the symlink were a regular directory. + + // First we call walk on the path as a directory + // (instead of a symlink). + err = w.walk(path, fs.FileInfoToDirEntry(fi), nil) + if err == fs.SkipDir { + return nil + } else if err != nil { + // This should be impossible, but handle it anyway in case + // walk is changed to return other errors. + return err + } + + // Now read the directory and walk its entries. + ents, err := os.ReadDir(path) + if err != nil { + // Report the ReadDir error, as filepath.WalkDir would do. + err = w.walk(path, fs.FileInfoToDirEntry(fi), err) + if err == fs.SkipDir { + return nil + } else if err != nil { + return err // Again, should be impossible. + } + // Fall through and iterate over whatever entries we did manage to get. + } + + for _, d := range ents { + nextPath := filepath.Join(path, d.Name()) + if d.IsDir() { + // We want to walk the whole directory tree rooted at nextPath, + // not just the single entry for the directory. + err := filepath.WalkDir(nextPath, w.walk) + if err != nil && w.opts.Logf != nil { + w.opts.Logf("%v", err) + } + } else { + err := w.walk(nextPath, d, nil) + if err == fs.SkipDir { + // Skip the rest of the entries in the parent directory of nextPath + // (that is, path itself). + break + } else if err != nil { + return err // Again, should be impossible. + } + } + } + return nil + } + + // Not a file, regular directory, or symlink; skip. + return nil +} diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go new file mode 100644 index 000000000..dd369c072 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -0,0 +1,1769 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package imports + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io/fs" + "io/ioutil" + "os" + "path" + "path/filepath" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/internal/event" + "golang.org/x/tools/internal/gocommand" + "golang.org/x/tools/internal/gopathwalk" +) + +// importToGroup is a list of functions which map from an import path to +// a group number. +var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){ + func(localPrefix, importPath string) (num int, ok bool) { + if localPrefix == "" { + return + } + for _, p := range strings.Split(localPrefix, ",") { + if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath { + return 3, true + } + } + return + }, + func(_, importPath string) (num int, ok bool) { + if strings.HasPrefix(importPath, "appengine") { + return 2, true + } + return + }, + func(_, importPath string) (num int, ok bool) { + firstComponent := strings.Split(importPath, "/")[0] + if strings.Contains(firstComponent, ".") { + return 1, true + } + return + }, +} + +func importGroup(localPrefix, importPath string) int { + for _, fn := range importToGroup { + if n, ok := fn(localPrefix, importPath); ok { + return n + } + } + return 0 +} + +type ImportFixType int + +const ( + AddImport ImportFixType = iota + DeleteImport + SetImportName +) + +type ImportFix struct { + // StmtInfo represents the import statement this fix will add, remove, or change. + StmtInfo ImportInfo + // IdentName is the identifier that this fix will add or remove. + IdentName string + // FixType is the type of fix this is (AddImport, DeleteImport, SetImportName). + FixType ImportFixType + Relevance float64 // see pkg +} + +// An ImportInfo represents a single import statement. +type ImportInfo struct { + ImportPath string // import path, e.g. "crypto/rand". + Name string // import name, e.g. "crand", or "" if none. +} + +// A packageInfo represents what's known about a package. +type packageInfo struct { + name string // real package name, if known. + exports map[string]bool // known exports. +} + +// parseOtherFiles parses all the Go files in srcDir except filename, including +// test files if filename looks like a test. +func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File { + // This could use go/packages but it doesn't buy much, and it fails + // with https://golang.org/issue/26296 in LoadFiles mode in some cases. + considerTests := strings.HasSuffix(filename, "_test.go") + + fileBase := filepath.Base(filename) + packageFileInfos, err := os.ReadDir(srcDir) + if err != nil { + return nil + } + + var files []*ast.File + for _, fi := range packageFileInfos { + if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { + continue + } + if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { + continue + } + + f, err := parser.ParseFile(fset, filepath.Join(srcDir, fi.Name()), nil, 0) + if err != nil { + continue + } + + files = append(files, f) + } + + return files +} + +// addGlobals puts the names of package vars into the provided map. +func addGlobals(f *ast.File, globals map[string]bool) { + for _, decl := range f.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + + for _, spec := range genDecl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + globals[valueSpec.Names[0].Name] = true + } + } +} + +// collectReferences builds a map of selector expressions, from +// left hand side (X) to a set of right hand sides (Sel). +func collectReferences(f *ast.File) references { + refs := references{} + + var visitor visitFn + visitor = func(node ast.Node) ast.Visitor { + if node == nil { + return visitor + } + switch v := node.(type) { + case *ast.SelectorExpr: + xident, ok := v.X.(*ast.Ident) + if !ok { + break + } + if xident.Obj != nil { + // If the parser can resolve it, it's not a package ref. + break + } + if !ast.IsExported(v.Sel.Name) { + // Whatever this is, it's not exported from a package. + break + } + pkgName := xident.Name + r := refs[pkgName] + if r == nil { + r = make(map[string]bool) + refs[pkgName] = r + } + r[v.Sel.Name] = true + } + return visitor + } + ast.Walk(visitor, f) + return refs +} + +// collectImports returns all the imports in f. +// Unnamed imports (., _) and "C" are ignored. +func collectImports(f *ast.File) []*ImportInfo { + var imports []*ImportInfo + for _, imp := range f.Imports { + var name string + if imp.Name != nil { + name = imp.Name.Name + } + if imp.Path.Value == `"C"` || name == "_" || name == "." { + continue + } + path := strings.Trim(imp.Path.Value, `"`) + imports = append(imports, &ImportInfo{ + Name: name, + ImportPath: path, + }) + } + return imports +} + +// findMissingImport searches pass's candidates for an import that provides +// pkg, containing all of syms. +func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo { + for _, candidate := range p.candidates { + pkgInfo, ok := p.knownPackages[candidate.ImportPath] + if !ok { + continue + } + if p.importIdentifier(candidate) != pkg { + continue + } + + allFound := true + for right := range syms { + if !pkgInfo.exports[right] { + allFound = false + break + } + } + + if allFound { + return candidate + } + } + return nil +} + +// references is set of references found in a Go file. The first map key is the +// left hand side of a selector expression, the second key is the right hand +// side, and the value should always be true. +type references map[string]map[string]bool + +// A pass contains all the inputs and state necessary to fix a file's imports. +// It can be modified in some ways during use; see comments below. +type pass struct { + // Inputs. These must be set before a call to load, and not modified after. + fset *token.FileSet // fset used to parse f and its siblings. + f *ast.File // the file being fixed. + srcDir string // the directory containing f. + env *ProcessEnv // the environment to use for go commands, etc. + loadRealPackageNames bool // if true, load package names from disk rather than guessing them. + otherFiles []*ast.File // sibling files. + + // Intermediate state, generated by load. + existingImports map[string][]*ImportInfo + allRefs references + missingRefs references + + // Inputs to fix. These can be augmented between successive fix calls. + lastTry bool // indicates that this is the last call and fix should clean up as best it can. + candidates []*ImportInfo // candidate imports in priority order. + knownPackages map[string]*packageInfo // information about all known packages. +} + +// loadPackageNames saves the package names for everything referenced by imports. +func (p *pass) loadPackageNames(imports []*ImportInfo) error { + if p.env.Logf != nil { + p.env.Logf("loading package names for %v packages", len(imports)) + defer func() { + p.env.Logf("done loading package names for %v packages", len(imports)) + }() + } + var unknown []string + for _, imp := range imports { + if _, ok := p.knownPackages[imp.ImportPath]; ok { + continue + } + unknown = append(unknown, imp.ImportPath) + } + + resolver, err := p.env.GetResolver() + if err != nil { + return err + } + + names, err := resolver.loadPackageNames(unknown, p.srcDir) + if err != nil { + return err + } + + for path, name := range names { + p.knownPackages[path] = &packageInfo{ + name: name, + exports: map[string]bool{}, + } + } + return nil +} + +// importIdentifier returns the identifier that imp will introduce. It will +// guess if the package name has not been loaded, e.g. because the source +// is not available. +func (p *pass) importIdentifier(imp *ImportInfo) string { + if imp.Name != "" { + return imp.Name + } + known := p.knownPackages[imp.ImportPath] + if known != nil && known.name != "" { + return known.name + } + return ImportPathToAssumedName(imp.ImportPath) +} + +// load reads in everything necessary to run a pass, and reports whether the +// file already has all the imports it needs. It fills in p.missingRefs with the +// file's missing symbols, if any, or removes unused imports if not. +func (p *pass) load() ([]*ImportFix, bool) { + p.knownPackages = map[string]*packageInfo{} + p.missingRefs = references{} + p.existingImports = map[string][]*ImportInfo{} + + // Load basic information about the file in question. + p.allRefs = collectReferences(p.f) + + // Load stuff from other files in the same package: + // global variables so we know they don't need resolving, and imports + // that we might want to mimic. + globals := map[string]bool{} + for _, otherFile := range p.otherFiles { + // Don't load globals from files that are in the same directory + // but a different package. Using them to suggest imports is OK. + if p.f.Name.Name == otherFile.Name.Name { + addGlobals(otherFile, globals) + } + p.candidates = append(p.candidates, collectImports(otherFile)...) + } + + // Resolve all the import paths we've seen to package names, and store + // f's imports by the identifier they introduce. + imports := collectImports(p.f) + if p.loadRealPackageNames { + err := p.loadPackageNames(append(imports, p.candidates...)) + if err != nil { + if p.env.Logf != nil { + p.env.Logf("loading package names: %v", err) + } + return nil, false + } + } + for _, imp := range imports { + p.existingImports[p.importIdentifier(imp)] = append(p.existingImports[p.importIdentifier(imp)], imp) + } + + // Find missing references. + for left, rights := range p.allRefs { + if globals[left] { + continue + } + _, ok := p.existingImports[left] + if !ok { + p.missingRefs[left] = rights + continue + } + } + if len(p.missingRefs) != 0 { + return nil, false + } + + return p.fix() +} + +// fix attempts to satisfy missing imports using p.candidates. If it finds +// everything, or if p.lastTry is true, it updates fixes to add the imports it found, +// delete anything unused, and update import names, and returns true. +func (p *pass) fix() ([]*ImportFix, bool) { + // Find missing imports. + var selected []*ImportInfo + for left, rights := range p.missingRefs { + if imp := p.findMissingImport(left, rights); imp != nil { + selected = append(selected, imp) + } + } + + if !p.lastTry && len(selected) != len(p.missingRefs) { + return nil, false + } + + // Found everything, or giving up. Add the new imports and remove any unused. + var fixes []*ImportFix + for _, identifierImports := range p.existingImports { + for _, imp := range identifierImports { + // We deliberately ignore globals here, because we can't be sure + // they're in the same package. People do things like put multiple + // main packages in the same directory, and we don't want to + // remove imports if they happen to have the same name as a var in + // a different package. + if _, ok := p.allRefs[p.importIdentifier(imp)]; !ok { + fixes = append(fixes, &ImportFix{ + StmtInfo: *imp, + IdentName: p.importIdentifier(imp), + FixType: DeleteImport, + }) + continue + } + + // An existing import may need to update its import name to be correct. + if name := p.importSpecName(imp); name != imp.Name { + fixes = append(fixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: name, + ImportPath: imp.ImportPath, + }, + IdentName: p.importIdentifier(imp), + FixType: SetImportName, + }) + } + } + } + // Collecting fixes involved map iteration, so sort for stability. See + // golang/go#59976. + sortFixes(fixes) + + // collect selected fixes in a separate slice, so that it can be sorted + // separately. Note that these fixes must occur after fixes to existing + // imports. TODO(rfindley): figure out why. + var selectedFixes []*ImportFix + for _, imp := range selected { + selectedFixes = append(selectedFixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: p.importSpecName(imp), + ImportPath: imp.ImportPath, + }, + IdentName: p.importIdentifier(imp), + FixType: AddImport, + }) + } + sortFixes(selectedFixes) + + return append(fixes, selectedFixes...), true +} + +func sortFixes(fixes []*ImportFix) { + sort.Slice(fixes, func(i, j int) bool { + fi, fj := fixes[i], fixes[j] + if fi.StmtInfo.ImportPath != fj.StmtInfo.ImportPath { + return fi.StmtInfo.ImportPath < fj.StmtInfo.ImportPath + } + if fi.StmtInfo.Name != fj.StmtInfo.Name { + return fi.StmtInfo.Name < fj.StmtInfo.Name + } + if fi.IdentName != fj.IdentName { + return fi.IdentName < fj.IdentName + } + return fi.FixType < fj.FixType + }) +} + +// importSpecName gets the import name of imp in the import spec. +// +// When the import identifier matches the assumed import name, the import name does +// not appear in the import spec. +func (p *pass) importSpecName(imp *ImportInfo) string { + // If we did not load the real package names, or the name is already set, + // we just return the existing name. + if !p.loadRealPackageNames || imp.Name != "" { + return imp.Name + } + + ident := p.importIdentifier(imp) + if ident == ImportPathToAssumedName(imp.ImportPath) { + return "" // ident not needed since the assumed and real names are the same. + } + return ident +} + +// apply will perform the fixes on f in order. +func apply(fset *token.FileSet, f *ast.File, fixes []*ImportFix) { + for _, fix := range fixes { + switch fix.FixType { + case DeleteImport: + astutil.DeleteNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case AddImport: + astutil.AddNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case SetImportName: + // Find the matching import path and change the name. + for _, spec := range f.Imports { + path := strings.Trim(spec.Path.Value, `"`) + if path == fix.StmtInfo.ImportPath { + spec.Name = &ast.Ident{ + Name: fix.StmtInfo.Name, + NamePos: spec.Pos(), + } + } + } + } + } +} + +// assumeSiblingImportsValid assumes that siblings' use of packages is valid, +// adding the exports they use. +func (p *pass) assumeSiblingImportsValid() { + for _, f := range p.otherFiles { + refs := collectReferences(f) + imports := collectImports(f) + importsByName := map[string]*ImportInfo{} + for _, imp := range imports { + importsByName[p.importIdentifier(imp)] = imp + } + for left, rights := range refs { + if imp, ok := importsByName[left]; ok { + if m, ok := stdlib[imp.ImportPath]; ok { + // We have the stdlib in memory; no need to guess. + rights = copyExports(m) + } + p.addCandidate(imp, &packageInfo{ + // no name; we already know it. + exports: rights, + }) + } + } + } +} + +// addCandidate adds a candidate import to p, and merges in the information +// in pkg. +func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) { + p.candidates = append(p.candidates, imp) + if existing, ok := p.knownPackages[imp.ImportPath]; ok { + if existing.name == "" { + existing.name = pkg.name + } + for export := range pkg.exports { + existing.exports[export] = true + } + } else { + p.knownPackages[imp.ImportPath] = pkg + } +} + +// fixImports adds and removes imports from f so that all its references are +// satisfied and there are no unused imports. +// +// This is declared as a variable rather than a function so goimports can +// easily be extended by adding a file with an init function. +var fixImports = fixImportsDefault + +func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) error { + fixes, err := getFixes(context.Background(), fset, f, filename, env) + if err != nil { + return err + } + apply(fset, f, fixes) + return err +} + +// getFixes gets the import fixes that need to be made to f in order to fix the imports. +// It does not modify the ast. +func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) { + abs, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + srcDir := filepath.Dir(abs) + if env.Logf != nil { + env.Logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) + } + + // First pass: looking only at f, and using the naive algorithm to + // derive package names from import paths, see if the file is already + // complete. We can't add any imports yet, because we don't know + // if missing references are actually package vars. + p := &pass{fset: fset, f: f, srcDir: srcDir, env: env} + if fixes, done := p.load(); done { + return fixes, nil + } + + otherFiles := parseOtherFiles(fset, srcDir, filename) + + // Second pass: add information from other files in the same package, + // like their package vars and imports. + p.otherFiles = otherFiles + if fixes, done := p.load(); done { + return fixes, nil + } + + // Now we can try adding imports from the stdlib. + p.assumeSiblingImportsValid() + addStdlibCandidates(p, p.missingRefs) + if fixes, done := p.fix(); done { + return fixes, nil + } + + // Third pass: get real package names where we had previously used + // the naive algorithm. + p = &pass{fset: fset, f: f, srcDir: srcDir, env: env} + p.loadRealPackageNames = true + p.otherFiles = otherFiles + if fixes, done := p.load(); done { + return fixes, nil + } + + if err := addStdlibCandidates(p, p.missingRefs); err != nil { + return nil, err + } + p.assumeSiblingImportsValid() + if fixes, done := p.fix(); done { + return fixes, nil + } + + // Go look for candidates in $GOPATH, etc. We don't necessarily load + // the real exports of sibling imports, so keep assuming their contents. + if err := addExternalCandidates(ctx, p, p.missingRefs, filename); err != nil { + return nil, err + } + + p.lastTry = true + fixes, _ := p.fix() + return fixes, nil +} + +// MaxRelevance is the highest relevance, used for the standard library. +// Chosen arbitrarily to match pre-existing gopls code. +const MaxRelevance = 7.0 + +// getCandidatePkgs works with the passed callback to find all acceptable packages. +// It deduplicates by import path, and uses a cached stdlib rather than reading +// from disk. +func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filename, filePkg string, env *ProcessEnv) error { + notSelf := func(p *pkg) bool { + return p.packageName != filePkg || p.dir != filepath.Dir(filename) + } + goenv, err := env.goEnv() + if err != nil { + return err + } + + var mu sync.Mutex // to guard asynchronous access to dupCheck + dupCheck := map[string]struct{}{} + + // Start off with the standard library. + for importPath, exports := range stdlib { + p := &pkg{ + dir: filepath.Join(goenv["GOROOT"], "src", importPath), + importPathShort: importPath, + packageName: path.Base(importPath), + relevance: MaxRelevance, + } + dupCheck[importPath] = struct{}{} + if notSelf(p) && wrappedCallback.dirFound(p) && wrappedCallback.packageNameLoaded(p) { + wrappedCallback.exportsLoaded(p, exports) + } + } + + scanFilter := &scanCallback{ + rootFound: func(root gopathwalk.Root) bool { + // Exclude goroot results -- getting them is relatively expensive, not cached, + // and generally redundant with the in-memory version. + return root.Type != gopathwalk.RootGOROOT && wrappedCallback.rootFound(root) + }, + dirFound: wrappedCallback.dirFound, + packageNameLoaded: func(pkg *pkg) bool { + mu.Lock() + defer mu.Unlock() + if _, ok := dupCheck[pkg.importPathShort]; ok { + return false + } + dupCheck[pkg.importPathShort] = struct{}{} + return notSelf(pkg) && wrappedCallback.packageNameLoaded(pkg) + }, + exportsLoaded: func(pkg *pkg, exports []string) { + // If we're an x_test, load the package under test's test variant. + if strings.HasSuffix(filePkg, "_test") && pkg.dir == filepath.Dir(filename) { + var err error + _, exports, err = loadExportsFromFiles(ctx, env, pkg.dir, true) + if err != nil { + return + } + } + wrappedCallback.exportsLoaded(pkg, exports) + }, + } + resolver, err := env.GetResolver() + if err != nil { + return err + } + return resolver.scan(ctx, scanFilter) +} + +func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]float64, error) { + result := make(map[string]float64) + resolver, err := env.GetResolver() + if err != nil { + return nil, err + } + for _, path := range paths { + result[path] = resolver.scoreImportPath(ctx, path) + } + return result, nil +} + +func PrimeCache(ctx context.Context, env *ProcessEnv) error { + // Fully scan the disk for directories, but don't actually read any Go files. + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + return false + }, + packageNameLoaded: func(pkg *pkg) bool { + return false + }, + } + return getCandidatePkgs(ctx, callback, "", "", env) +} + +func candidateImportName(pkg *pkg) string { + if ImportPathToAssumedName(pkg.importPathShort) != pkg.packageName { + return pkg.packageName + } + return "" +} + +// GetAllCandidates calls wrapped for each package whose name starts with +// searchPrefix, and can be imported from filename with the package name filePkg. +// +// Beware that the wrapped function may be called multiple times concurrently. +// TODO(adonovan): encapsulate the concurrency. +func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + if !canUse(filename, pkg.dir) { + return false + } + // Try the assumed package name first, then a simpler path match + // in case of packages named vN, which are not uncommon. + return strings.HasPrefix(ImportPathToAssumedName(pkg.importPathShort), searchPrefix) || + strings.HasPrefix(path.Base(pkg.importPathShort), searchPrefix) + }, + packageNameLoaded: func(pkg *pkg) bool { + if !strings.HasPrefix(pkg.packageName, searchPrefix) { + return false + } + wrapped(ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }) + return false + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +// GetImportPaths calls wrapped for each package whose import path starts with +// searchPrefix, and can be imported from filename with the package name filePkg. +func GetImportPaths(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + if !canUse(filename, pkg.dir) { + return false + } + return strings.HasPrefix(pkg.importPathShort, searchPrefix) + }, + packageNameLoaded: func(pkg *pkg) bool { + wrapped(ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }) + return false + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +// A PackageExport is a package and its exports. +type PackageExport struct { + Fix *ImportFix + Exports []string +} + +// GetPackageExports returns all known packages with name pkg and their exports. +func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + return pkgIsCandidate(filename, references{searchPkg: nil}, pkg) + }, + packageNameLoaded: func(pkg *pkg) bool { + return pkg.packageName == searchPkg + }, + exportsLoaded: func(pkg *pkg, exports []string) { + sort.Strings(exports) + wrapped(PackageExport{ + Fix: &ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }, + Exports: exports, + }) + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +var requiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD", "GOMODCACHE", "GONOPROXY", "GONOSUMDB", "GOPATH", "GOPROXY", "GOROOT", "GOSUMDB", "GOWORK"} + +// ProcessEnv contains environment variables and settings that affect the use of +// the go command, the go/build package, etc. +type ProcessEnv struct { + GocmdRunner *gocommand.Runner + + BuildFlags []string + ModFlag string + ModFile string + + // SkipPathInScan returns true if the path should be skipped from scans of + // the RootCurrentModule root type. The function argument is a clean, + // absolute path. + SkipPathInScan func(string) bool + + // Env overrides the OS environment, and can be used to specify + // GOPROXY, GO111MODULE, etc. PATH cannot be set here, because + // exec.Command will not honor it. + // Specifying all of RequiredGoEnvVars avoids a call to `go env`. + Env map[string]string + + WorkingDir string + + // If Logf is non-nil, debug logging is enabled through this function. + Logf func(format string, args ...interface{}) + + initialized bool + + resolver Resolver +} + +func (e *ProcessEnv) goEnv() (map[string]string, error) { + if err := e.init(); err != nil { + return nil, err + } + return e.Env, nil +} + +func (e *ProcessEnv) matchFile(dir, name string) (bool, error) { + bctx, err := e.buildContext() + if err != nil { + return false, err + } + return bctx.MatchFile(dir, name) +} + +// CopyConfig copies the env's configuration into a new env. +func (e *ProcessEnv) CopyConfig() *ProcessEnv { + copy := &ProcessEnv{ + GocmdRunner: e.GocmdRunner, + initialized: e.initialized, + BuildFlags: e.BuildFlags, + Logf: e.Logf, + WorkingDir: e.WorkingDir, + resolver: nil, + Env: map[string]string{}, + } + for k, v := range e.Env { + copy.Env[k] = v + } + return copy +} + +func (e *ProcessEnv) init() error { + if e.initialized { + return nil + } + + foundAllRequired := true + for _, k := range requiredGoEnvVars { + if _, ok := e.Env[k]; !ok { + foundAllRequired = false + break + } + } + if foundAllRequired { + e.initialized = true + return nil + } + + if e.Env == nil { + e.Env = map[string]string{} + } + + goEnv := map[string]string{} + stdout, err := e.invokeGo(context.TODO(), "env", append([]string{"-json"}, requiredGoEnvVars...)...) + if err != nil { + return err + } + if err := json.Unmarshal(stdout.Bytes(), &goEnv); err != nil { + return err + } + for k, v := range goEnv { + e.Env[k] = v + } + e.initialized = true + return nil +} + +func (e *ProcessEnv) env() []string { + var env []string // the gocommand package will prepend os.Environ. + for k, v := range e.Env { + env = append(env, k+"="+v) + } + return env +} + +func (e *ProcessEnv) GetResolver() (Resolver, error) { + if e.resolver != nil { + return e.resolver, nil + } + if err := e.init(); err != nil { + return nil, err + } + if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 { + e.resolver = newGopathResolver(e) + return e.resolver, nil + } + e.resolver = newModuleResolver(e) + return e.resolver, nil +} + +func (e *ProcessEnv) buildContext() (*build.Context, error) { + ctx := build.Default + goenv, err := e.goEnv() + if err != nil { + return nil, err + } + ctx.GOROOT = goenv["GOROOT"] + ctx.GOPATH = goenv["GOPATH"] + + // As of Go 1.14, build.Context has a Dir field + // (see golang.org/issue/34860). + // Populate it only if present. + rc := reflect.ValueOf(&ctx).Elem() + dir := rc.FieldByName("Dir") + if dir.IsValid() && dir.Kind() == reflect.String { + dir.SetString(e.WorkingDir) + } + + // Since Go 1.11, go/build.Context.Import may invoke 'go list' depending on + // the value in GO111MODULE in the process's environment. We always want to + // run in GOPATH mode when calling Import, so we need to prevent this from + // happening. In Go 1.16, GO111MODULE defaults to "on", so this problem comes + // up more frequently. + // + // HACK: setting any of the Context I/O hooks prevents Import from invoking + // 'go list', regardless of GO111MODULE. This is undocumented, but it's + // unlikely to change before GOPATH support is removed. + ctx.ReadDir = ioutil.ReadDir + + return &ctx, nil +} + +func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) { + inv := gocommand.Invocation{ + Verb: verb, + Args: args, + BuildFlags: e.BuildFlags, + Env: e.env(), + Logf: e.Logf, + WorkingDir: e.WorkingDir, + } + return e.GocmdRunner.Run(ctx, inv) +} + +func addStdlibCandidates(pass *pass, refs references) error { + goenv, err := pass.env.goEnv() + if err != nil { + return err + } + add := func(pkg string) { + // Prevent self-imports. + if path.Base(pkg) == pass.f.Name.Name && filepath.Join(goenv["GOROOT"], "src", pkg) == pass.srcDir { + return + } + exports := copyExports(stdlib[pkg]) + pass.addCandidate( + &ImportInfo{ImportPath: pkg}, + &packageInfo{name: path.Base(pkg), exports: exports}) + } + for left := range refs { + if left == "rand" { + // Make sure we try crypto/rand before math/rand. + add("crypto/rand") + add("math/rand") + continue + } + for importPath := range stdlib { + if path.Base(importPath) == left { + add(importPath) + } + } + } + return nil +} + +// A Resolver does the build-system-specific parts of goimports. +type Resolver interface { + // loadPackageNames loads the package names in importPaths. + loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) + // scan works with callback to search for packages. See scanCallback for details. + scan(ctx context.Context, callback *scanCallback) error + // loadExports returns the set of exported symbols in the package at dir. + // loadExports may be called concurrently. + loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) + // scoreImportPath returns the relevance for an import path. + scoreImportPath(ctx context.Context, path string) float64 + + ClearForNewScan() +} + +// A scanCallback controls a call to scan and receives its results. +// In general, minor errors will be silently discarded; a user should not +// expect to receive a full series of calls for everything. +type scanCallback struct { + // rootFound is called before scanning a new root dir. If it returns true, + // the root will be scanned. Returning false will not necessarily prevent + // directories from that root making it to dirFound. + rootFound func(gopathwalk.Root) bool + // dirFound is called when a directory is found that is possibly a Go package. + // pkg will be populated with everything except packageName. + // If it returns true, the package's name will be loaded. + dirFound func(pkg *pkg) bool + // packageNameLoaded is called when a package is found and its name is loaded. + // If it returns true, the package's exports will be loaded. + packageNameLoaded func(pkg *pkg) bool + // exportsLoaded is called when a package's exports have been loaded. + exportsLoaded func(pkg *pkg, exports []string) +} + +func addExternalCandidates(ctx context.Context, pass *pass, refs references, filename string) error { + ctx, done := event.Start(ctx, "imports.addExternalCandidates") + defer done() + + var mu sync.Mutex + found := make(map[string][]pkgDistance) + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true // We want everything. + }, + dirFound: func(pkg *pkg) bool { + return pkgIsCandidate(filename, refs, pkg) + }, + packageNameLoaded: func(pkg *pkg) bool { + if _, want := refs[pkg.packageName]; !want { + return false + } + if pkg.dir == pass.srcDir && pass.f.Name.Name == pkg.packageName { + // The candidate is in the same directory and has the + // same package name. Don't try to import ourselves. + return false + } + if !canUse(filename, pkg.dir) { + return false + } + mu.Lock() + defer mu.Unlock() + found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(pass.srcDir, pkg.dir)}) + return false // We'll do our own loading after we sort. + }, + } + resolver, err := pass.env.GetResolver() + if err != nil { + return err + } + if err = resolver.scan(context.Background(), callback); err != nil { + return err + } + + // Search for imports matching potential package references. + type result struct { + imp *ImportInfo + pkg *packageInfo + } + results := make(chan result, len(refs)) + + ctx, cancel := context.WithCancel(context.TODO()) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + var ( + firstErr error + firstErrOnce sync.Once + ) + for pkgName, symbols := range refs { + wg.Add(1) + go func(pkgName string, symbols map[string]bool) { + defer wg.Done() + + found, err := findImport(ctx, pass, found[pkgName], pkgName, symbols, filename) + + if err != nil { + firstErrOnce.Do(func() { + firstErr = err + cancel() + }) + return + } + + if found == nil { + return // No matching package. + } + + imp := &ImportInfo{ + ImportPath: found.importPathShort, + } + + pkg := &packageInfo{ + name: pkgName, + exports: symbols, + } + results <- result{imp, pkg} + }(pkgName, symbols) + } + go func() { + wg.Wait() + close(results) + }() + + for result := range results { + pass.addCandidate(result.imp, result.pkg) + } + return firstErr +} + +// notIdentifier reports whether ch is an invalid identifier character. +func notIdentifier(ch rune) bool { + return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || + '0' <= ch && ch <= '9' || + ch == '_' || + ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch))) +} + +// ImportPathToAssumedName returns the assumed package name of an import path. +// It does this using only string parsing of the import path. +// It picks the last element of the path that does not look like a major +// version, and then picks the valid identifier off the start of that element. +// It is used to determine if a local rename should be added to an import for +// clarity. +// This function could be moved to a standard package and exported if we want +// for use in other tools. +func ImportPathToAssumedName(importPath string) string { + base := path.Base(importPath) + if strings.HasPrefix(base, "v") { + if _, err := strconv.Atoi(base[1:]); err == nil { + dir := path.Dir(importPath) + if dir != "." { + base = path.Base(dir) + } + } + } + base = strings.TrimPrefix(base, "go-") + if i := strings.IndexFunc(base, notIdentifier); i >= 0 { + base = base[:i] + } + return base +} + +// gopathResolver implements resolver for GOPATH workspaces. +type gopathResolver struct { + env *ProcessEnv + walked bool + cache *dirInfoCache + scanSema chan struct{} // scanSema prevents concurrent scans. +} + +func newGopathResolver(env *ProcessEnv) *gopathResolver { + r := &gopathResolver{ + env: env, + cache: &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + }, + scanSema: make(chan struct{}, 1), + } + r.scanSema <- struct{}{} + return r +} + +func (r *gopathResolver) ClearForNewScan() { + <-r.scanSema + r.cache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + r.walked = false + r.scanSema <- struct{}{} +} + +func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + names := map[string]string{} + bctx, err := r.env.buildContext() + if err != nil { + return nil, err + } + for _, path := range importPaths { + names[path] = importPathToName(bctx, path, srcDir) + } + return names, nil +} + +// importPathToName finds out the actual package name, as declared in its .go files. +func importPathToName(bctx *build.Context, importPath, srcDir string) string { + // Fast path for standard library without going to disk. + if _, ok := stdlib[importPath]; ok { + return path.Base(importPath) // stdlib packages always match their paths. + } + + buildPkg, err := bctx.Import(importPath, srcDir, build.FindOnly) + if err != nil { + return "" + } + pkgName, err := packageDirToName(buildPkg.Dir) + if err != nil { + return "" + } + return pkgName +} + +// packageDirToName is a faster version of build.Import if +// the only thing desired is the package name. Given a directory, +// packageDirToName then only parses one file in the package, +// trusting that the files in the directory are consistent. +func packageDirToName(dir string) (packageName string, err error) { + d, err := os.Open(dir) + if err != nil { + return "", err + } + names, err := d.Readdirnames(-1) + d.Close() + if err != nil { + return "", err + } + sort.Strings(names) // to have predictable behavior + var lastErr error + var nfile int + for _, name := range names { + if !strings.HasSuffix(name, ".go") { + continue + } + if strings.HasSuffix(name, "_test.go") { + continue + } + nfile++ + fullFile := filepath.Join(dir, name) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) + if err != nil { + lastErr = err + continue + } + pkgName := f.Name.Name + if pkgName == "documentation" { + // Special case from go/build.ImportDir, not + // handled by ctx.MatchFile. + continue + } + if pkgName == "main" { + // Also skip package main, assuming it's a +build ignore generator or example. + // Since you can't import a package main anyway, there's no harm here. + continue + } + return pkgName, nil + } + if lastErr != nil { + return "", lastErr + } + return "", fmt.Errorf("no importable package found in %d Go files", nfile) +} + +type pkg struct { + dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") + importPathShort string // vendorless import path ("net/http", "a/b") + packageName string // package name loaded from source if requested + relevance float64 // a weakly-defined score of how relevant a package is. 0 is most relevant. +} + +type pkgDistance struct { + pkg *pkg + distance int // relative distance to target +} + +// byDistanceOrImportPathShortLength sorts by relative distance breaking ties +// on the short import path length and then the import string itself. +type byDistanceOrImportPathShortLength []pkgDistance + +func (s byDistanceOrImportPathShortLength) Len() int { return len(s) } +func (s byDistanceOrImportPathShortLength) Less(i, j int) bool { + di, dj := s[i].distance, s[j].distance + if di == -1 { + return false + } + if dj == -1 { + return true + } + if di != dj { + return di < dj + } + + vi, vj := s[i].pkg.importPathShort, s[j].pkg.importPathShort + if len(vi) != len(vj) { + return len(vi) < len(vj) + } + return vi < vj +} +func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func distance(basepath, targetpath string) int { + p, err := filepath.Rel(basepath, targetpath) + if err != nil { + return -1 + } + if p == "." { + return 0 + } + return strings.Count(p, string(filepath.Separator)) + 1 +} + +func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error { + add := func(root gopathwalk.Root, dir string) { + // We assume cached directories have not changed. We can skip them and their + // children. + if _, ok := r.cache.Load(dir); ok { + return + } + + importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):]) + info := directoryPackageInfo{ + status: directoryScanned, + dir: dir, + rootType: root.Type, + nonCanonicalImportPath: VendorlessPath(importpath), + } + r.cache.Store(dir, info) + } + processDir := func(info directoryPackageInfo) { + // Skip this directory if we were not able to get the package information successfully. + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return + } + + p := &pkg{ + importPathShort: info.nonCanonicalImportPath, + dir: info.dir, + relevance: MaxRelevance - 1, + } + if info.rootType == gopathwalk.RootGOROOT { + p.relevance = MaxRelevance + } + + if !callback.dirFound(p) { + return + } + var err error + p.packageName, err = r.cache.CachePackageName(info) + if err != nil { + return + } + + if !callback.packageNameLoaded(p) { + return + } + if _, exports, err := r.loadExports(ctx, p, false); err == nil { + callback.exportsLoaded(p, exports) + } + } + stop := r.cache.ScanAndListen(ctx, processDir) + defer stop() + + goenv, err := r.env.goEnv() + if err != nil { + return err + } + var roots []gopathwalk.Root + roots = append(roots, gopathwalk.Root{Path: filepath.Join(goenv["GOROOT"], "src"), Type: gopathwalk.RootGOROOT}) + for _, p := range filepath.SplitList(goenv["GOPATH"]) { + roots = append(roots, gopathwalk.Root{Path: filepath.Join(p, "src"), Type: gopathwalk.RootGOPATH}) + } + // The callback is not necessarily safe to use in the goroutine below. Process roots eagerly. + roots = filterRoots(roots, callback.rootFound) + // We can't cancel walks, because we need them to finish to have a usable + // cache. Instead, run them in a separate goroutine and detach. + scanDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + return + case <-r.scanSema: + } + defer func() { r.scanSema <- struct{}{} }() + gopathwalk.Walk(roots, add, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: false}) + close(scanDone) + }() + select { + case <-ctx.Done(): + case <-scanDone: + } + return nil +} + +func (r *gopathResolver) scoreImportPath(ctx context.Context, path string) float64 { + if _, ok := stdlib[path]; ok { + return MaxRelevance + } + return MaxRelevance - 1 +} + +func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []gopathwalk.Root { + var result []gopathwalk.Root + for _, root := range roots { + if !include(root) { + continue + } + result = append(result, root) + } + return result +} + +func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) { + if info, ok := r.cache.Load(pkg.dir); ok && !includeTest { + return r.cache.CacheExports(ctx, r.env, info) + } + return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest) +} + +// VendorlessPath returns the devendorized version of the import path ipath. +// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". +func VendorlessPath(ipath string) string { + // Devendorize for use in import statement. + if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { + return ipath[i+len("/vendor/"):] + } + if strings.HasPrefix(ipath, "vendor/") { + return ipath[len("vendor/"):] + } + return ipath +} + +func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) { + // Look for non-test, buildable .go files which could provide exports. + all, err := os.ReadDir(dir) + if err != nil { + return "", nil, err + } + var files []fs.DirEntry + for _, fi := range all { + name := fi.Name() + if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) { + continue + } + match, err := env.matchFile(dir, fi.Name()) + if err != nil || !match { + continue + } + files = append(files, fi) + } + + if len(files) == 0 { + return "", nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir) + } + + var pkgName string + var exports []string + fset := token.NewFileSet() + for _, fi := range files { + select { + case <-ctx.Done(): + return "", nil, ctx.Err() + default: + } + + fullFile := filepath.Join(dir, fi.Name()) + f, err := parser.ParseFile(fset, fullFile, nil, 0) + if err != nil { + if env.Logf != nil { + env.Logf("error parsing %v: %v", fullFile, err) + } + continue + } + if f.Name.Name == "documentation" { + // Special case from go/build.ImportDir, not + // handled by MatchFile above. + continue + } + if includeTest && strings.HasSuffix(f.Name.Name, "_test") { + // x_test package. We want internal test files only. + continue + } + pkgName = f.Name.Name + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports = append(exports, name) + } + } + } + + if env.Logf != nil { + sortedExports := append([]string(nil), exports...) + sort.Strings(sortedExports) + env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, strings.Join(sortedExports, ", ")) + } + return pkgName, exports, nil +} + +// findImport searches for a package with the given symbols. +// If no package is found, findImport returns ("", false, nil) +func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgName string, symbols map[string]bool, filename string) (*pkg, error) { + // Sort the candidates by their import package length, + // assuming that shorter package names are better than long + // ones. Note that this sorts by the de-vendored name, so + // there's no "penalty" for vendoring. + sort.Sort(byDistanceOrImportPathShortLength(candidates)) + if pass.env.Logf != nil { + for i, c := range candidates { + pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir) + } + } + resolver, err := pass.env.GetResolver() + if err != nil { + return nil, err + } + + // Collect exports for packages with matching names. + rescv := make([]chan *pkg, len(candidates)) + for i := range candidates { + rescv[i] = make(chan *pkg, 1) + } + const maxConcurrentPackageImport = 4 + loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) + + ctx, cancel := context.WithCancel(ctx) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + + wg.Add(1) + go func() { + defer wg.Done() + for i, c := range candidates { + select { + case loadExportsSem <- struct{}{}: + case <-ctx.Done(): + return + } + + wg.Add(1) + go func(c pkgDistance, resc chan<- *pkg) { + defer func() { + <-loadExportsSem + wg.Done() + }() + + if pass.env.Logf != nil { + pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName) + } + // If we're an x_test, load the package under test's test variant. + includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir + _, exports, err := resolver.loadExports(ctx, c.pkg, includeTest) + if err != nil { + if pass.env.Logf != nil { + pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) + } + resc <- nil + return + } + + exportsMap := make(map[string]bool, len(exports)) + for _, sym := range exports { + exportsMap[sym] = true + } + + // If it doesn't have the right + // symbols, send nil to mean no match. + for symbol := range symbols { + if !exportsMap[symbol] { + resc <- nil + return + } + } + resc <- c.pkg + }(c, rescv[i]) + } + }() + + for _, resc := range rescv { + pkg := <-resc + if pkg == nil { + continue + } + return pkg, nil + } + return nil, nil +} + +// pkgIsCandidate reports whether pkg is a candidate for satisfying the +// finding which package pkgIdent in the file named by filename is trying +// to refer to. +// +// This check is purely lexical and is meant to be as fast as possible +// because it's run over all $GOPATH directories to filter out poor +// candidates in order to limit the CPU and I/O later parsing the +// exports in candidate packages. +// +// filename is the file being formatted. +// pkgIdent is the package being searched for, like "client" (if +// searching for "client.New") +func pkgIsCandidate(filename string, refs references, pkg *pkg) bool { + // Check "internal" and "vendor" visibility: + if !canUse(filename, pkg.dir) { + return false + } + + // Speed optimization to minimize disk I/O: + // the last two components on disk must contain the + // package name somewhere. + // + // This permits mismatch naming like directory + // "go-foo" being package "foo", or "pkg.v3" being "pkg", + // or directory "google.golang.org/api/cloudbilling/v1" + // being package "cloudbilling", but doesn't + // permit a directory "foo" to be package + // "bar", which is strongly discouraged + // anyway. There's no reason goimports needs + // to be slow just to accommodate that. + for pkgIdent := range refs { + lastTwo := lastTwoComponents(pkg.importPathShort) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { + lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + } + } + return false +} + +func hasHyphenOrUpperASCII(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + if b == '-' || ('A' <= b && b <= 'Z') { + return true + } + } + return false +} + +func lowerASCIIAndRemoveHyphen(s string) (ret string) { + buf := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == '-': + continue + case 'A' <= b && b <= 'Z': + buf = append(buf, b+('a'-'A')) + default: + buf = append(buf, b) + } + } + return string(buf) +} + +// canUse reports whether the package in dir is usable from filename, +// respecting the Go "internal" and "vendor" visibility rules. +func canUse(filename, dir string) bool { + // Fast path check, before any allocations. If it doesn't contain vendor + // or internal, it's not tricky: + // Note that this can false-negative on directories like "notinternal", + // but we check it correctly below. This is just a fast path. + if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { + return true + } + + dirSlash := filepath.ToSlash(dir) + if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { + return true + } + // Vendor or internal directory only visible from children of parent. + // That means the path from the current directory to the target directory + // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal + // or bar/vendor or bar/internal. + // After stripping all the leading ../, the only okay place to see vendor or internal + // is at the very beginning of the path. + absfile, err := filepath.Abs(filename) + if err != nil { + return false + } + absdir, err := filepath.Abs(dir) + if err != nil { + return false + } + rel, err := filepath.Rel(absfile, absdir) + if err != nil { + return false + } + relSlash := filepath.ToSlash(rel) + if i := strings.LastIndex(relSlash, "../"); i >= 0 { + relSlash = relSlash[i+len("../"):] + } + return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") +} + +// lastTwoComponents returns at most the last two path components +// of v, using either / or \ as the path separator. +func lastTwoComponents(v string) string { + nslash := 0 + for i := len(v) - 1; i >= 0; i-- { + if v[i] == '/' || v[i] == '\\' { + nslash++ + if nslash == 2 { + return v[i:] + } + } + } + return v +} + +type visitFn func(node ast.Node) ast.Visitor + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + return fn(node) +} + +func copyExports(pkg []string) map[string]bool { + m := make(map[string]bool, len(pkg)) + for _, v := range pkg { + m[v] = true + } + return m +} diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go new file mode 100644 index 000000000..58e637b90 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/imports.go @@ -0,0 +1,356 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run mkstdlib.go + +// Package imports implements a Go pretty-printer (like package "go/format") +// that also adds or removes import statements as necessary. +package imports + +import ( + "bufio" + "bytes" + "context" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/printer" + "go/token" + "io" + "regexp" + "strconv" + "strings" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/internal/event" +) + +// Options is golang.org/x/tools/imports.Options with extra internal-only options. +type Options struct { + Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state. + + // LocalPrefix is a comma-separated string of import path prefixes, which, if + // set, instructs Process to sort the import paths with the given prefixes + // into another group after 3rd-party packages. + LocalPrefix string + + Fragment bool // Accept fragment of a source file (no package statement) + AllErrors bool // Report all errors (not just the first 10 on different lines) + + Comments bool // Print comments (true if nil *Options provided) + TabIndent bool // Use tabs for indent (true if nil *Options provided) + TabWidth int // Tab width (8 if nil *Options provided) + + FormatOnly bool // Disable the insertion and deletion of imports +} + +// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env. +func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) { + fileSet := token.NewFileSet() + file, adjust, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + if !opt.FormatOnly { + if err := fixImports(fileSet, file, filename, opt.Env); err != nil { + return nil, err + } + } + return formatFile(fileSet, file, src, adjust, opt) +} + +// FixImports returns a list of fixes to the imports that, when applied, +// will leave the imports in the same state as Process. src and opt must +// be specified. +// +// Note that filename's directory influences which imports can be chosen, +// so it is important that filename be accurate. +func FixImports(ctx context.Context, filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { + ctx, done := event.Start(ctx, "imports.FixImports") + defer done() + + fileSet := token.NewFileSet() + file, _, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + return getFixes(ctx, fileSet, file, filename, opt.Env) +} + +// ApplyFixes applies all of the fixes to the file and formats it. extraMode +// is added in when parsing the file. src and opts must be specified, but no +// env is needed. +func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) { + // Don't use parse() -- we don't care about fragments or statement lists + // here, and we need to work with unparseable files. + fileSet := token.NewFileSet() + parserMode := parser.Mode(0) + if opt.Comments { + parserMode |= parser.ParseComments + } + if opt.AllErrors { + parserMode |= parser.AllErrors + } + parserMode |= extraMode + + file, err := parser.ParseFile(fileSet, filename, src, parserMode) + if file == nil { + return nil, err + } + + // Apply the fixes to the file. + apply(fileSet, file, fixes) + + return formatFile(fileSet, file, src, nil, opt) +} + +// formatFile formats the file syntax tree. +// It may mutate the token.FileSet. +// +// If an adjust function is provided, it is called after formatting +// with the original source (formatFile's src parameter) and the +// formatted file, and returns the postpocessed result. +func formatFile(fset *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) { + mergeImports(file) + sortImports(opt.LocalPrefix, fset.File(file.Pos()), file) + var spacesBefore []string // import paths we need spaces before + for _, impSection := range astutil.Imports(fset, file) { + // Within each block of contiguous imports, see if any + // import lines are in different group numbers. If so, + // we'll need to put a space between them so it's + // compatible with gofmt. + lastGroup := -1 + for _, importSpec := range impSection { + importPath, _ := strconv.Unquote(importSpec.Path.Value) + groupNum := importGroup(opt.LocalPrefix, importPath) + if groupNum != lastGroup && lastGroup != -1 { + spacesBefore = append(spacesBefore, importPath) + } + lastGroup = groupNum + } + + } + + printerMode := printer.UseSpaces + if opt.TabIndent { + printerMode |= printer.TabIndent + } + printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth} + + var buf bytes.Buffer + err := printConfig.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + out := buf.Bytes() + if adjust != nil { + out = adjust(src, out) + } + if len(spacesBefore) > 0 { + out, err = addImportSpaces(bytes.NewReader(out), spacesBefore) + if err != nil { + return nil, err + } + } + + out, err = format.Source(out) + if err != nil { + return nil, err + } + return out, nil +} + +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) { + parserMode := parser.Mode(0) + if opt.Comments { + parserMode |= parser.ParseComments + } + if opt.AllErrors { + parserMode |= parser.AllErrors + } + + // Try as whole source file. + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err == nil { + return file, nil, nil + } + // If the error is that the source file didn't begin with a + // package line and we accept fragmented input, fall through to + // try as a source fragment. Stop and return on any other error. + if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") { + return nil, nil, err + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that parse errors are on + // the correct line. + const prefix = "package main;" + psrc := append([]byte(prefix), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + // Gofmt will turn the ; into a \n. + // Do that ourselves now and update the file contents, + // so that positions and line numbers are correct going forward. + psrc[len(prefix)-1] = '\n' + fset.File(file.Package).SetLinesForContent(psrc) + + // If a main function exists, we will assume this is a main + // package and leave the file. + if containsMainFunc(file) { + return file, nil, nil + } + + adjust := func(orig, src []byte) []byte { + // Remove the package clause. + src = src[len(prefix):] + return matchSpace(orig, src) + } + return file, adjust, nil + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return nil, nil, err + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + src = src[len("package p\n\nfunc _() {"):] + src = src[:len(src)-len("}\n")] + // Gofmt has also indented the function body one level. + // Remove that indent. + src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) + return matchSpace(orig, src) + } + return file, adjust, nil + } + + // Failed, and out of options. + return nil, nil, err +} + +// containsMainFunc checks if a file contains a function declaration with the +// function signature 'func main()' +func containsMainFunc(file *ast.File) bool { + for _, decl := range file.Decls { + if f, ok := decl.(*ast.FuncDecl); ok { + if f.Name.Name != "main" { + continue + } + + if len(f.Type.Params.List) != 0 { + continue + } + + if f.Type.Results != nil && len(f.Type.Results.List) != 0 { + continue + } + + return true + } + } + + return false +} + +func cutSpace(b []byte) (before, middle, after []byte) { + i := 0 + for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { + i++ + } + j := len(b) + for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { + j-- + } + if i <= j { + return b[:i], b[i:j], b[j:] + } + return nil, nil, b[j:] +} + +// matchSpace reformats src to use the same space context as orig. +// 1. If orig begins with blank lines, matchSpace inserts them at the beginning of src. +// 2. matchSpace copies the indentation of the first non-blank line in orig +// to every non-blank line in src. +// 3. matchSpace copies the trailing space from orig and uses it in place +// of src's trailing space. +func matchSpace(orig []byte, src []byte) []byte { + before, _, after := cutSpace(orig) + i := bytes.LastIndex(before, []byte{'\n'}) + before, indent := before[:i+1], before[i+1:] + + _, src, _ = cutSpace(src) + + var b bytes.Buffer + b.Write(before) + for len(src) > 0 { + line := src + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, src = line[:i+1], line[i+1:] + } else { + src = nil + } + if len(line) > 0 && line[0] != '\n' { // not blank + b.Write(indent) + } + b.Write(line) + } + b.Write(after) + return b.Bytes() +} + +var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+?)"`) + +func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) { + var out bytes.Buffer + in := bufio.NewReader(r) + inImports := false + done := false + for { + s, err := in.ReadString('\n') + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + if !inImports && !done && strings.HasPrefix(s, "import") { + inImports = true + } + if inImports && (strings.HasPrefix(s, "var") || + strings.HasPrefix(s, "func") || + strings.HasPrefix(s, "const") || + strings.HasPrefix(s, "type")) { + done = true + inImports = false + } + if inImports && len(breaks) > 0 { + if m := impLine.FindStringSubmatch(s); m != nil { + if m[1] == breaks[0] { + out.WriteByte('\n') + breaks = breaks[1:] + } + } + } + + fmt.Fprint(&out, s) + } + return out.Bytes(), nil +} diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go new file mode 100644 index 000000000..5f4d435d3 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -0,0 +1,723 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package imports + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + + "golang.org/x/mod/module" + "golang.org/x/tools/internal/event" + "golang.org/x/tools/internal/gocommand" + "golang.org/x/tools/internal/gopathwalk" +) + +// ModuleResolver implements resolver for modules using the go command as little +// as feasible. +type ModuleResolver struct { + env *ProcessEnv + moduleCacheDir string + dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory. + roots []gopathwalk.Root + scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots. + scannedRoots map[gopathwalk.Root]bool + + initialized bool + mains []*gocommand.ModuleJSON + mainByDir map[string]*gocommand.ModuleJSON + modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path... + modsByDir []*gocommand.ModuleJSON // ...or number of path components in their Dir. + + // moduleCacheCache stores information about the module cache. + moduleCacheCache *dirInfoCache + otherCache *dirInfoCache +} + +func newModuleResolver(e *ProcessEnv) *ModuleResolver { + r := &ModuleResolver{ + env: e, + scanSema: make(chan struct{}, 1), + } + r.scanSema <- struct{}{} + return r +} + +func (r *ModuleResolver) init() error { + if r.initialized { + return nil + } + + goenv, err := r.env.goEnv() + if err != nil { + return err + } + inv := gocommand.Invocation{ + BuildFlags: r.env.BuildFlags, + ModFlag: r.env.ModFlag, + ModFile: r.env.ModFile, + Env: r.env.env(), + Logf: r.env.Logf, + WorkingDir: r.env.WorkingDir, + } + + vendorEnabled := false + var mainModVendor *gocommand.ModuleJSON + + // Module vendor directories are ignored in workspace mode: + // https://go.googlesource.com/proposal/+/master/design/45713-workspace.md + if len(r.env.Env["GOWORK"]) == 0 { + vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner) + if err != nil { + return err + } + } + + if mainModVendor != nil && vendorEnabled { + // Vendor mode is on, so all the non-Main modules are irrelevant, + // and we need to search /vendor for everything. + r.mains = []*gocommand.ModuleJSON{mainModVendor} + r.dummyVendorMod = &gocommand.ModuleJSON{ + Path: "", + Dir: filepath.Join(mainModVendor.Dir, "vendor"), + } + r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod} + r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod} + } else { + // Vendor mode is off, so run go list -m ... to find everything. + err := r.initAllMods() + // We expect an error when running outside of a module with + // GO111MODULE=on. Other errors are fatal. + if err != nil { + if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") { + return err + } + } + } + + if gmc := r.env.Env["GOMODCACHE"]; gmc != "" { + r.moduleCacheDir = gmc + } else { + gopaths := filepath.SplitList(goenv["GOPATH"]) + if len(gopaths) == 0 { + return fmt.Errorf("empty GOPATH") + } + r.moduleCacheDir = filepath.Join(gopaths[0], "/pkg/mod") + } + + sort.Slice(r.modsByModPath, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.modsByModPath[x].Path, "/") + } + return count(j) < count(i) // descending order + }) + sort.Slice(r.modsByDir, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.modsByDir[x].Dir, string(filepath.Separator)) + } + return count(j) < count(i) // descending order + }) + + r.roots = []gopathwalk.Root{ + {Path: filepath.Join(goenv["GOROOT"], "/src"), Type: gopathwalk.RootGOROOT}, + } + r.mainByDir = make(map[string]*gocommand.ModuleJSON) + for _, main := range r.mains { + r.roots = append(r.roots, gopathwalk.Root{Path: main.Dir, Type: gopathwalk.RootCurrentModule}) + r.mainByDir[main.Dir] = main + } + if vendorEnabled { + r.roots = append(r.roots, gopathwalk.Root{Path: r.dummyVendorMod.Dir, Type: gopathwalk.RootOther}) + } else { + addDep := func(mod *gocommand.ModuleJSON) { + if mod.Replace == nil { + // This is redundant with the cache, but we'll skip it cheaply enough. + r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache}) + } else { + r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther}) + } + } + // Walk dependent modules before scanning the full mod cache, direct deps first. + for _, mod := range r.modsByModPath { + if !mod.Indirect && !mod.Main { + addDep(mod) + } + } + for _, mod := range r.modsByModPath { + if mod.Indirect && !mod.Main { + addDep(mod) + } + } + r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache}) + } + + r.scannedRoots = map[gopathwalk.Root]bool{} + if r.moduleCacheCache == nil { + r.moduleCacheCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + } + if r.otherCache == nil { + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + } + r.initialized = true + return nil +} + +func (r *ModuleResolver) initAllMods() error { + stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-e", "-json", "...") + if err != nil { + return err + } + for dec := json.NewDecoder(stdout); dec.More(); { + mod := &gocommand.ModuleJSON{} + if err := dec.Decode(mod); err != nil { + return err + } + if mod.Dir == "" { + if r.env.Logf != nil { + r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path) + } + // Can't do anything with a module that's not downloaded. + continue + } + // golang/go#36193: the go command doesn't always clean paths. + mod.Dir = filepath.Clean(mod.Dir) + r.modsByModPath = append(r.modsByModPath, mod) + r.modsByDir = append(r.modsByDir, mod) + if mod.Main { + r.mains = append(r.mains, mod) + } + } + return nil +} + +func (r *ModuleResolver) ClearForNewScan() { + <-r.scanSema + r.scannedRoots = map[gopathwalk.Root]bool{} + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + r.scanSema <- struct{}{} +} + +func (r *ModuleResolver) ClearForNewMod() { + <-r.scanSema + *r = ModuleResolver{ + env: r.env, + moduleCacheCache: r.moduleCacheCache, + otherCache: r.otherCache, + scanSema: r.scanSema, + } + r.init() + r.scanSema <- struct{}{} +} + +// findPackage returns the module and directory that contains the package at +// the given import path, or returns nil, "" if no module is in scope. +func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) { + // This can't find packages in the stdlib, but that's harmless for all + // the existing code paths. + for _, m := range r.modsByModPath { + if !strings.HasPrefix(importPath, m.Path) { + continue + } + pathInModule := importPath[len(m.Path):] + pkgDir := filepath.Join(m.Dir, pathInModule) + if r.dirIsNestedModule(pkgDir, m) { + continue + } + + if info, ok := r.cacheLoad(pkgDir); ok { + if loaded, err := info.reachedStatus(nameLoaded); loaded { + if err != nil { + continue // No package in this dir. + } + return m, pkgDir + } + if scanned, err := info.reachedStatus(directoryScanned); scanned && err != nil { + continue // Dir is unreadable, etc. + } + // This is slightly wrong: a directory doesn't have to have an + // importable package to count as a package for package-to-module + // resolution. package main or _test files should count but + // don't. + // TODO(heschi): fix this. + if _, err := r.cachePackageName(info); err == nil { + return m, pkgDir + } + } + + // Not cached. Read the filesystem. + pkgFiles, err := os.ReadDir(pkgDir) + if err != nil { + continue + } + // A module only contains a package if it has buildable go + // files in that directory. If not, it could be provided by an + // outer module. See #29736. + for _, fi := range pkgFiles { + if ok, _ := r.env.matchFile(pkgDir, fi.Name()); ok { + return m, pkgDir + } + } + } + return nil, "" +} + +func (r *ModuleResolver) cacheLoad(dir string) (directoryPackageInfo, bool) { + if info, ok := r.moduleCacheCache.Load(dir); ok { + return info, ok + } + return r.otherCache.Load(dir) +} + +func (r *ModuleResolver) cacheStore(info directoryPackageInfo) { + if info.rootType == gopathwalk.RootModuleCache { + r.moduleCacheCache.Store(info.dir, info) + } else { + r.otherCache.Store(info.dir, info) + } +} + +func (r *ModuleResolver) cacheKeys() []string { + return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...) +} + +// cachePackageName caches the package name for a dir already in the cache. +func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) { + if info.rootType == gopathwalk.RootModuleCache { + return r.moduleCacheCache.CachePackageName(info) + } + return r.otherCache.CachePackageName(info) +} + +func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) { + if info.rootType == gopathwalk.RootModuleCache { + return r.moduleCacheCache.CacheExports(ctx, env, info) + } + return r.otherCache.CacheExports(ctx, env, info) +} + +// findModuleByDir returns the module that contains dir, or nil if no such +// module is in scope. +func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON { + // This is quite tricky and may not be correct. dir could be: + // - a package in the main module. + // - a replace target underneath the main module's directory. + // - a nested module in the above. + // - a replace target somewhere totally random. + // - a nested module in the above. + // - in the mod cache. + // - in /vendor/ in -mod=vendor mode. + // - nested module? Dunno. + // Rumor has it that replace targets cannot contain other replace targets. + // + // Note that it is critical here that modsByDir is sorted to have deeper dirs + // first. This ensures that findModuleByDir finds the innermost module. + // See also golang/go#56291. + for _, m := range r.modsByDir { + if !strings.HasPrefix(dir, m.Dir) { + continue + } + + if r.dirIsNestedModule(dir, m) { + continue + } + + return m + } + return nil +} + +// dirIsNestedModule reports if dir is contained in a nested module underneath +// mod, not actually in mod. +func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool { + if !strings.HasPrefix(dir, mod.Dir) { + return false + } + if r.dirInModuleCache(dir) { + // Nested modules in the module cache are pruned, + // so it cannot be a nested module. + return false + } + if mod != nil && mod == r.dummyVendorMod { + // The /vendor pseudomodule is flattened and doesn't actually count. + return false + } + modDir, _ := r.modInfo(dir) + if modDir == "" { + return false + } + return modDir != mod.Dir +} + +func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { + readModName := func(modFile string) string { + modBytes, err := os.ReadFile(modFile) + if err != nil { + return "" + } + return modulePath(modBytes) + } + + if r.dirInModuleCache(dir) { + if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 { + index := strings.Index(dir, matches[1]+"@"+matches[2]) + modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2]) + return modDir, readModName(filepath.Join(modDir, "go.mod")) + } + } + for { + if info, ok := r.cacheLoad(dir); ok { + return info.moduleDir, info.moduleName + } + f := filepath.Join(dir, "go.mod") + info, err := os.Stat(f) + if err == nil && !info.IsDir() { + return dir, readModName(f) + } + + d := filepath.Dir(dir) + if len(d) >= len(dir) { + return "", "" // reached top of file system, no go.mod + } + dir = d + } +} + +func (r *ModuleResolver) dirInModuleCache(dir string) bool { + if r.moduleCacheDir == "" { + return false + } + return strings.HasPrefix(dir, r.moduleCacheDir) +} + +func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + if err := r.init(); err != nil { + return nil, err + } + names := map[string]string{} + for _, path := range importPaths { + _, packageDir := r.findPackage(path) + if packageDir == "" { + continue + } + name, err := packageDirToName(packageDir) + if err != nil { + continue + } + names[path] = name + } + return names, nil +} + +func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error { + ctx, done := event.Start(ctx, "imports.ModuleResolver.scan") + defer done() + + if err := r.init(); err != nil { + return err + } + + processDir := func(info directoryPackageInfo) { + // Skip this directory if we were not able to get the package information successfully. + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return + } + pkg, err := r.canonicalize(info) + if err != nil { + return + } + + if !callback.dirFound(pkg) { + return + } + pkg.packageName, err = r.cachePackageName(info) + if err != nil { + return + } + + if !callback.packageNameLoaded(pkg) { + return + } + _, exports, err := r.loadExports(ctx, pkg, false) + if err != nil { + return + } + callback.exportsLoaded(pkg, exports) + } + + // Start processing everything in the cache, and listen for the new stuff + // we discover in the walk below. + stop1 := r.moduleCacheCache.ScanAndListen(ctx, processDir) + defer stop1() + stop2 := r.otherCache.ScanAndListen(ctx, processDir) + defer stop2() + + // We assume cached directories are fully cached, including all their + // children, and have not changed. We can skip them. + skip := func(root gopathwalk.Root, dir string) bool { + if r.env.SkipPathInScan != nil && root.Type == gopathwalk.RootCurrentModule { + if root.Path == dir { + return false + } + + if r.env.SkipPathInScan(filepath.Clean(dir)) { + return true + } + } + + info, ok := r.cacheLoad(dir) + if !ok { + return false + } + // This directory can be skipped as long as we have already scanned it. + // Packages with errors will continue to have errors, so there is no need + // to rescan them. + packageScanned, _ := info.reachedStatus(directoryScanned) + return packageScanned + } + + // Add anything new to the cache, and process it if we're still listening. + add := func(root gopathwalk.Root, dir string) { + r.cacheStore(r.scanDirForPackage(root, dir)) + } + + // r.roots and the callback are not necessarily safe to use in the + // goroutine below. Process them eagerly. + roots := filterRoots(r.roots, callback.rootFound) + // We can't cancel walks, because we need them to finish to have a usable + // cache. Instead, run them in a separate goroutine and detach. + scanDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + return + case <-r.scanSema: + } + defer func() { r.scanSema <- struct{}{} }() + // We have the lock on r.scannedRoots, and no other scans can run. + for _, root := range roots { + if ctx.Err() != nil { + return + } + + if r.scannedRoots[root] { + continue + } + gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: true}) + r.scannedRoots[root] = true + } + close(scanDone) + }() + select { + case <-ctx.Done(): + case <-scanDone: + } + return nil +} + +func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) float64 { + if _, ok := stdlib[path]; ok { + return MaxRelevance + } + mod, _ := r.findPackage(path) + return modRelevance(mod) +} + +func modRelevance(mod *gocommand.ModuleJSON) float64 { + var relevance float64 + switch { + case mod == nil: // out of scope + return MaxRelevance - 4 + case mod.Indirect: + relevance = MaxRelevance - 3 + case !mod.Main: + relevance = MaxRelevance - 2 + default: + relevance = MaxRelevance - 1 // main module ties with stdlib + } + + _, versionString, ok := module.SplitPathVersion(mod.Path) + if ok { + index := strings.Index(versionString, "v") + if index == -1 { + return relevance + } + if versionNumber, err := strconv.ParseFloat(versionString[index+1:], 64); err == nil { + relevance += versionNumber / 1000 + } + } + + return relevance +} + +// canonicalize gets the result of canonicalizing the packages using the results +// of initializing the resolver from 'go list -m'. +func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) { + // Packages in GOROOT are already canonical, regardless of the std/cmd modules. + if info.rootType == gopathwalk.RootGOROOT { + return &pkg{ + importPathShort: info.nonCanonicalImportPath, + dir: info.dir, + packageName: path.Base(info.nonCanonicalImportPath), + relevance: MaxRelevance, + }, nil + } + + importPath := info.nonCanonicalImportPath + mod := r.findModuleByDir(info.dir) + // Check if the directory is underneath a module that's in scope. + if mod != nil { + // It is. If dir is the target of a replace directive, + // our guessed import path is wrong. Use the real one. + if mod.Dir == info.dir { + importPath = mod.Path + } else { + dirInMod := info.dir[len(mod.Dir)+len("/"):] + importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + } + } else if !strings.HasPrefix(importPath, info.moduleName) { + // The module's name doesn't match the package's import path. It + // probably needs a replace directive we don't have. + return nil, fmt.Errorf("package in %q is not valid without a replace statement", info.dir) + } + + res := &pkg{ + importPathShort: importPath, + dir: info.dir, + relevance: modRelevance(mod), + } + // We may have discovered a package that has a different version + // in scope already. Canonicalize to that one if possible. + if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" { + res.dir = canonicalDir + } + return res, nil +} + +func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) { + if err := r.init(); err != nil { + return "", nil, err + } + if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest { + return r.cacheExports(ctx, r.env, info) + } + return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest) +} + +func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo { + subdir := "" + if dir != root.Path { + subdir = dir[len(root.Path)+len("/"):] + } + importPath := filepath.ToSlash(subdir) + if strings.HasPrefix(importPath, "vendor/") { + // Only enter vendor directories if they're explicitly requested as a root. + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("unwanted vendor directory"), + } + } + switch root.Type { + case gopathwalk.RootCurrentModule: + importPath = path.Join(r.mainByDir[root.Path].Path, filepath.ToSlash(subdir)) + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + if len(matches) == 0 { + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("invalid module cache path: %v", subdir), + } + } + modPath, err := module.UnescapePath(filepath.ToSlash(matches[1])) + if err != nil { + if r.env.Logf != nil { + r.env.Logf("decoding module cache path %q: %v", subdir, err) + } + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("decoding module cache path %q: %v", subdir, err), + } + } + importPath = path.Join(modPath, filepath.ToSlash(matches[3])) + } + + modDir, modName := r.modInfo(dir) + result := directoryPackageInfo{ + status: directoryScanned, + dir: dir, + rootType: root.Type, + nonCanonicalImportPath: importPath, + moduleDir: modDir, + moduleName: modName, + } + if root.Type == gopathwalk.RootGOROOT { + // stdlib packages are always in scope, despite the confusing go.mod + return result + } + return result +} + +// modCacheRegexp splits a path in a module cache into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// modulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +// +// Copied from cmd/go/internal/modfile. +func modulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/vendor/golang.org/x/tools/internal/imports/mod_cache.go b/vendor/golang.org/x/tools/internal/imports/mod_cache.go new file mode 100644 index 000000000..45690abbb --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/mod_cache.go @@ -0,0 +1,236 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package imports + +import ( + "context" + "fmt" + "sync" + + "golang.org/x/tools/internal/gopathwalk" +) + +// To find packages to import, the resolver needs to know about all of +// the packages that could be imported. This includes packages that are +// already in modules that are in (1) the current module, (2) replace targets, +// and (3) packages in the module cache. Packages in (1) and (2) may change over +// time, as the client may edit the current module and locally replaced modules. +// The module cache (which includes all of the packages in (3)) can only +// ever be added to. +// +// The resolver can thus save state about packages in the module cache +// and guarantee that this will not change over time. To obtain information +// about new modules added to the module cache, the module cache should be +// rescanned. +// +// It is OK to serve information about modules that have been deleted, +// as they do still exist. +// TODO(suzmue): can we share information with the caller about +// what module needs to be downloaded to import this package? + +type directoryPackageStatus int + +const ( + _ directoryPackageStatus = iota + directoryScanned + nameLoaded + exportsLoaded +) + +type directoryPackageInfo struct { + // status indicates the extent to which this struct has been filled in. + status directoryPackageStatus + // err is non-nil when there was an error trying to reach status. + err error + + // Set when status >= directoryScanned. + + // dir is the absolute directory of this package. + dir string + rootType gopathwalk.RootType + // nonCanonicalImportPath is the package's expected import path. It may + // not actually be importable at that path. + nonCanonicalImportPath string + + // Module-related information. + moduleDir string // The directory that is the module root of this dir. + moduleName string // The module name that contains this dir. + + // Set when status >= nameLoaded. + + packageName string // the package name, as declared in the source. + + // Set when status >= exportsLoaded. + + exports []string +} + +// reachedStatus returns true when info has a status at least target and any error associated with +// an attempt to reach target. +func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) { + if info.err == nil { + return info.status >= target, nil + } + if info.status == target { + return true, info.err + } + return true, nil +} + +// dirInfoCache is a concurrency safe map for storing information about +// directories that may contain packages. +// +// The information in this cache is built incrementally. Entries are initialized in scan. +// No new keys should be added in any other functions, as all directories containing +// packages are identified in scan. +// +// Other functions, including loadExports and findPackage, may update entries in this cache +// as they discover new things about the directory. +// +// The information in the cache is not expected to change for the cache's +// lifetime, so there is no protection against competing writes. Users should +// take care not to hold the cache across changes to the underlying files. +// +// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc) +type dirInfoCache struct { + mu sync.Mutex + // dirs stores information about packages in directories, keyed by absolute path. + dirs map[string]*directoryPackageInfo + listeners map[*int]cacheListener +} + +type cacheListener func(directoryPackageInfo) + +// ScanAndListen calls listener on all the items in the cache, and on anything +// newly added. The returned stop function waits for all in-flight callbacks to +// finish and blocks new ones. +func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() { + ctx, cancel := context.WithCancel(ctx) + + // Flushing out all the callbacks is tricky without knowing how many there + // are going to be. Setting an arbitrary limit makes it much easier. + const maxInFlight = 10 + sema := make(chan struct{}, maxInFlight) + for i := 0; i < maxInFlight; i++ { + sema <- struct{}{} + } + + cookie := new(int) // A unique ID we can use for the listener. + + // We can't hold mu while calling the listener. + d.mu.Lock() + var keys []string + for key := range d.dirs { + keys = append(keys, key) + } + d.listeners[cookie] = func(info directoryPackageInfo) { + select { + case <-ctx.Done(): + return + case <-sema: + } + listener(info) + sema <- struct{}{} + } + d.mu.Unlock() + + stop := func() { + cancel() + d.mu.Lock() + delete(d.listeners, cookie) + d.mu.Unlock() + for i := 0; i < maxInFlight; i++ { + <-sema + } + } + + // Process the pre-existing keys. + for _, k := range keys { + select { + case <-ctx.Done(): + return stop + default: + } + if v, ok := d.Load(k); ok { + listener(v) + } + } + + return stop +} + +// Store stores the package info for dir. +func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) { + d.mu.Lock() + _, old := d.dirs[dir] + d.dirs[dir] = &info + var listeners []cacheListener + for _, l := range d.listeners { + listeners = append(listeners, l) + } + d.mu.Unlock() + + if !old { + for _, l := range listeners { + l(info) + } + } +} + +// Load returns a copy of the directoryPackageInfo for absolute directory dir. +func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) { + d.mu.Lock() + defer d.mu.Unlock() + info, ok := d.dirs[dir] + if !ok { + return directoryPackageInfo{}, false + } + return *info, true +} + +// Keys returns the keys currently present in d. +func (d *dirInfoCache) Keys() (keys []string) { + d.mu.Lock() + defer d.mu.Unlock() + for key := range d.dirs { + keys = append(keys, key) + } + return keys +} + +func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) { + if loaded, err := info.reachedStatus(nameLoaded); loaded { + return info.packageName, err + } + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return "", fmt.Errorf("cannot read package name, scan error: %v", err) + } + info.packageName, info.err = packageDirToName(info.dir) + info.status = nameLoaded + d.Store(info.dir, info) + return info.packageName, info.err +} + +func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) { + if reached, _ := info.reachedStatus(exportsLoaded); reached { + return info.packageName, info.exports, info.err + } + if reached, err := info.reachedStatus(nameLoaded); reached && err != nil { + return "", nil, err + } + info.packageName, info.exports, info.err = loadExportsFromFiles(ctx, env, info.dir, false) + if info.err == context.Canceled || info.err == context.DeadlineExceeded { + return info.packageName, info.exports, info.err + } + // The cache structure wants things to proceed linearly. We can skip a + // step here, but only if we succeed. + if info.status == nameLoaded || info.err == nil { + info.status = exportsLoaded + } else { + info.status = nameLoaded + } + d.Store(info.dir, info) + return info.packageName, info.exports, info.err +} diff --git a/vendor/golang.org/x/tools/internal/imports/sortimports.go b/vendor/golang.org/x/tools/internal/imports/sortimports.go new file mode 100644 index 000000000..1a0a7ebd9 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/sortimports.go @@ -0,0 +1,297 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Hacked up copy of go/ast/import.go +// Modified to use a single token.File in preference to a FileSet. + +package imports + +import ( + "go/ast" + "go/token" + "log" + "sort" + "strconv" +) + +// sortImports sorts runs of consecutive import lines in import blocks in f. +// It also removes duplicate imports when it is possible to do so without data loss. +// +// It may mutate the token.File. +func sortImports(localPrefix string, tokFile *token.File, f *ast.File) { + for i, d := range f.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + break + } + + if len(d.Specs) == 0 { + // Empty import block, remove it. + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + } + + if !d.Lparen.IsValid() { + // Not a block: sorted by default. + continue + } + + // Identify and sort runs of specs on successive lines. + i := 0 + specs := d.Specs[:0] + for j, s := range d.Specs { + if j > i && tokFile.Line(s.Pos()) > 1+tokFile.Line(d.Specs[j-1].End()) { + // j begins a new run. End this one. + specs = append(specs, sortSpecs(localPrefix, tokFile, f, d.Specs[i:j])...) + i = j + } + } + specs = append(specs, sortSpecs(localPrefix, tokFile, f, d.Specs[i:])...) + d.Specs = specs + + // Deduping can leave a blank line before the rparen; clean that up. + // Ignore line directives. + if len(d.Specs) > 0 { + lastSpec := d.Specs[len(d.Specs)-1] + lastLine := tokFile.PositionFor(lastSpec.Pos(), false).Line + if rParenLine := tokFile.PositionFor(d.Rparen, false).Line; rParenLine > lastLine+1 { + tokFile.MergeLine(rParenLine - 1) // has side effects! + } + } + } +} + +// mergeImports merges all the import declarations into the first one. +// Taken from golang.org/x/tools/ast/astutil. +// This does not adjust line numbers properly +func mergeImports(f *ast.File) { + if len(f.Decls) <= 1 { + return + } + + // Merge all the import declarations into the first one. + var first *ast.GenDecl + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { + continue + } + if first == nil { + first = gen + continue // Don't touch the first one. + } + // We now know there is more than one package in this import + // declaration. Ensure that it ends up parenthesized. + first.Lparen = first.Pos() + // Move the imports of the other import declaration to the first one. + for _, spec := range gen.Specs { + spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + first.Specs = append(first.Specs, spec) + } + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } +} + +// declImports reports whether gen contains an import of path. +// Taken from golang.org/x/tools/ast/astutil. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +func importPath(s ast.Spec) string { + t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value) + if err == nil { + return t + } + return "" +} + +func importName(s ast.Spec) string { + n := s.(*ast.ImportSpec).Name + if n == nil { + return "" + } + return n.Name +} + +func importComment(s ast.Spec) string { + c := s.(*ast.ImportSpec).Comment + if c == nil { + return "" + } + return c.Text() +} + +// collapse indicates whether prev may be removed, leaving only next. +func collapse(prev, next ast.Spec) bool { + if importPath(next) != importPath(prev) || importName(next) != importName(prev) { + return false + } + return prev.(*ast.ImportSpec).Comment == nil +} + +type posSpan struct { + Start token.Pos + End token.Pos +} + +// sortSpecs sorts the import specs within each import decl. +// It may mutate the token.File. +func sortSpecs(localPrefix string, tokFile *token.File, f *ast.File, specs []ast.Spec) []ast.Spec { + // Can't short-circuit here even if specs are already sorted, + // since they might yet need deduplication. + // A lone import, however, may be safely ignored. + if len(specs) <= 1 { + return specs + } + + // Record positions for specs. + pos := make([]posSpan, len(specs)) + for i, s := range specs { + pos[i] = posSpan{s.Pos(), s.End()} + } + + // Identify comments in this range. + // Any comment from pos[0].Start to the final line counts. + lastLine := tokFile.Line(pos[len(pos)-1].End) + cstart := len(f.Comments) + cend := len(f.Comments) + for i, g := range f.Comments { + if g.Pos() < pos[0].Start { + continue + } + if i < cstart { + cstart = i + } + if tokFile.Line(g.End()) > lastLine { + cend = i + break + } + } + comments := f.Comments[cstart:cend] + + // Assign each comment to the import spec preceding it. + importComment := map[*ast.ImportSpec][]*ast.CommentGroup{} + specIndex := 0 + for _, g := range comments { + for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { + specIndex++ + } + s := specs[specIndex].(*ast.ImportSpec) + importComment[s] = append(importComment[s], g) + } + + // Sort the import specs by import path. + // Remove duplicates, when possible without data loss. + // Reassign the import paths to have the same position sequence. + // Reassign each comment to abut the end of its spec. + // Sort the comments by new position. + sort.Sort(byImportSpec{localPrefix, specs}) + + // Dedup. Thanks to our sorting, we can just consider + // adjacent pairs of imports. + deduped := specs[:0] + for i, s := range specs { + if i == len(specs)-1 || !collapse(s, specs[i+1]) { + deduped = append(deduped, s) + } else { + p := s.Pos() + tokFile.MergeLine(tokFile.Line(p)) // has side effects! + } + } + specs = deduped + + // Fix up comment positions + for i, s := range specs { + s := s.(*ast.ImportSpec) + if s.Name != nil { + s.Name.NamePos = pos[i].Start + } + s.Path.ValuePos = pos[i].Start + s.EndPos = pos[i].End + nextSpecPos := pos[i].End + + for _, g := range importComment[s] { + for _, c := range g.List { + c.Slash = pos[i].End + nextSpecPos = c.End() + } + } + if i < len(specs)-1 { + pos[i+1].Start = nextSpecPos + pos[i+1].End = nextSpecPos + } + } + + sort.Sort(byCommentPos(comments)) + + // Fixup comments can insert blank lines, because import specs are on different lines. + // We remove those blank lines here by merging import spec to the first import spec line. + firstSpecLine := tokFile.Line(specs[0].Pos()) + for _, s := range specs[1:] { + p := s.Pos() + line := tokFile.Line(p) + for previousLine := line - 1; previousLine >= firstSpecLine; { + // MergeLine can panic. Avoid the panic at the cost of not removing the blank line + // golang/go#50329 + if previousLine > 0 && previousLine < tokFile.LineCount() { + tokFile.MergeLine(previousLine) // has side effects! + previousLine-- + } else { + // try to gather some data to diagnose how this could happen + req := "Please report what the imports section of your go file looked like." + log.Printf("panic avoided: first:%d line:%d previous:%d max:%d. %s", + firstSpecLine, line, previousLine, tokFile.LineCount(), req) + } + } + } + return specs +} + +type byImportSpec struct { + localPrefix string + specs []ast.Spec // slice of *ast.ImportSpec +} + +func (x byImportSpec) Len() int { return len(x.specs) } +func (x byImportSpec) Swap(i, j int) { x.specs[i], x.specs[j] = x.specs[j], x.specs[i] } +func (x byImportSpec) Less(i, j int) bool { + ipath := importPath(x.specs[i]) + jpath := importPath(x.specs[j]) + + igroup := importGroup(x.localPrefix, ipath) + jgroup := importGroup(x.localPrefix, jpath) + if igroup != jgroup { + return igroup < jgroup + } + + if ipath != jpath { + return ipath < jpath + } + iname := importName(x.specs[i]) + jname := importName(x.specs[j]) + + if iname != jname { + return iname < jname + } + return importComment(x.specs[i]) < importComment(x.specs[j]) +} + +type byCommentPos []*ast.CommentGroup + +func (x byCommentPos) Len() int { return len(x) } +func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() } diff --git a/vendor/golang.org/x/tools/internal/imports/zstdlib.go b/vendor/golang.org/x/tools/internal/imports/zstdlib.go new file mode 100644 index 000000000..9f992c2be --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/zstdlib.go @@ -0,0 +1,11345 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by mkstdlib.go. DO NOT EDIT. + +package imports + +var stdlib = map[string][]string{ + "archive/tar": { + "ErrFieldTooLong", + "ErrHeader", + "ErrInsecurePath", + "ErrWriteAfterClose", + "ErrWriteTooLong", + "FileInfoHeader", + "Format", + "FormatGNU", + "FormatPAX", + "FormatUSTAR", + "FormatUnknown", + "Header", + "NewReader", + "NewWriter", + "Reader", + "TypeBlock", + "TypeChar", + "TypeCont", + "TypeDir", + "TypeFifo", + "TypeGNULongLink", + "TypeGNULongName", + "TypeGNUSparse", + "TypeLink", + "TypeReg", + "TypeRegA", + "TypeSymlink", + "TypeXGlobalHeader", + "TypeXHeader", + "Writer", + }, + "archive/zip": { + "Compressor", + "Decompressor", + "Deflate", + "ErrAlgorithm", + "ErrChecksum", + "ErrFormat", + "ErrInsecurePath", + "File", + "FileHeader", + "FileInfoHeader", + "NewReader", + "NewWriter", + "OpenReader", + "ReadCloser", + "Reader", + "RegisterCompressor", + "RegisterDecompressor", + "Store", + "Writer", + }, + "bufio": { + "ErrAdvanceTooFar", + "ErrBadReadCount", + "ErrBufferFull", + "ErrFinalToken", + "ErrInvalidUnreadByte", + "ErrInvalidUnreadRune", + "ErrNegativeAdvance", + "ErrNegativeCount", + "ErrTooLong", + "MaxScanTokenSize", + "NewReadWriter", + "NewReader", + "NewReaderSize", + "NewScanner", + "NewWriter", + "NewWriterSize", + "ReadWriter", + "Reader", + "ScanBytes", + "ScanLines", + "ScanRunes", + "ScanWords", + "Scanner", + "SplitFunc", + "Writer", + }, + "bytes": { + "Buffer", + "Clone", + "Compare", + "Contains", + "ContainsAny", + "ContainsFunc", + "ContainsRune", + "Count", + "Cut", + "CutPrefix", + "CutSuffix", + "Equal", + "EqualFold", + "ErrTooLarge", + "Fields", + "FieldsFunc", + "HasPrefix", + "HasSuffix", + "Index", + "IndexAny", + "IndexByte", + "IndexFunc", + "IndexRune", + "Join", + "LastIndex", + "LastIndexAny", + "LastIndexByte", + "LastIndexFunc", + "Map", + "MinRead", + "NewBuffer", + "NewBufferString", + "NewReader", + "Reader", + "Repeat", + "Replace", + "ReplaceAll", + "Runes", + "Split", + "SplitAfter", + "SplitAfterN", + "SplitN", + "Title", + "ToLower", + "ToLowerSpecial", + "ToTitle", + "ToTitleSpecial", + "ToUpper", + "ToUpperSpecial", + "ToValidUTF8", + "Trim", + "TrimFunc", + "TrimLeft", + "TrimLeftFunc", + "TrimPrefix", + "TrimRight", + "TrimRightFunc", + "TrimSpace", + "TrimSuffix", + }, + "cmp": { + "Compare", + "Less", + "Ordered", + }, + "compress/bzip2": { + "NewReader", + "StructuralError", + }, + "compress/flate": { + "BestCompression", + "BestSpeed", + "CorruptInputError", + "DefaultCompression", + "HuffmanOnly", + "InternalError", + "NewReader", + "NewReaderDict", + "NewWriter", + "NewWriterDict", + "NoCompression", + "ReadError", + "Reader", + "Resetter", + "WriteError", + "Writer", + }, + "compress/gzip": { + "BestCompression", + "BestSpeed", + "DefaultCompression", + "ErrChecksum", + "ErrHeader", + "Header", + "HuffmanOnly", + "NewReader", + "NewWriter", + "NewWriterLevel", + "NoCompression", + "Reader", + "Writer", + }, + "compress/lzw": { + "LSB", + "MSB", + "NewReader", + "NewWriter", + "Order", + "Reader", + "Writer", + }, + "compress/zlib": { + "BestCompression", + "BestSpeed", + "DefaultCompression", + "ErrChecksum", + "ErrDictionary", + "ErrHeader", + "HuffmanOnly", + "NewReader", + "NewReaderDict", + "NewWriter", + "NewWriterLevel", + "NewWriterLevelDict", + "NoCompression", + "Resetter", + "Writer", + }, + "container/heap": { + "Fix", + "Init", + "Interface", + "Pop", + "Push", + "Remove", + }, + "container/list": { + "Element", + "List", + "New", + }, + "container/ring": { + "New", + "Ring", + }, + "context": { + "AfterFunc", + "Background", + "CancelCauseFunc", + "CancelFunc", + "Canceled", + "Cause", + "Context", + "DeadlineExceeded", + "TODO", + "WithCancel", + "WithCancelCause", + "WithDeadline", + "WithDeadlineCause", + "WithTimeout", + "WithTimeoutCause", + "WithValue", + "WithoutCancel", + }, + "crypto": { + "BLAKE2b_256", + "BLAKE2b_384", + "BLAKE2b_512", + "BLAKE2s_256", + "Decrypter", + "DecrypterOpts", + "Hash", + "MD4", + "MD5", + "MD5SHA1", + "PrivateKey", + "PublicKey", + "RIPEMD160", + "RegisterHash", + "SHA1", + "SHA224", + "SHA256", + "SHA384", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "SHA512", + "SHA512_224", + "SHA512_256", + "Signer", + "SignerOpts", + }, + "crypto/aes": { + "BlockSize", + "KeySizeError", + "NewCipher", + }, + "crypto/cipher": { + "AEAD", + "Block", + "BlockMode", + "NewCBCDecrypter", + "NewCBCEncrypter", + "NewCFBDecrypter", + "NewCFBEncrypter", + "NewCTR", + "NewGCM", + "NewGCMWithNonceSize", + "NewGCMWithTagSize", + "NewOFB", + "Stream", + "StreamReader", + "StreamWriter", + }, + "crypto/des": { + "BlockSize", + "KeySizeError", + "NewCipher", + "NewTripleDESCipher", + }, + "crypto/dsa": { + "ErrInvalidPublicKey", + "GenerateKey", + "GenerateParameters", + "L1024N160", + "L2048N224", + "L2048N256", + "L3072N256", + "ParameterSizes", + "Parameters", + "PrivateKey", + "PublicKey", + "Sign", + "Verify", + }, + "crypto/ecdh": { + "Curve", + "P256", + "P384", + "P521", + "PrivateKey", + "PublicKey", + "X25519", + }, + "crypto/ecdsa": { + "GenerateKey", + "PrivateKey", + "PublicKey", + "Sign", + "SignASN1", + "Verify", + "VerifyASN1", + }, + "crypto/ed25519": { + "GenerateKey", + "NewKeyFromSeed", + "Options", + "PrivateKey", + "PrivateKeySize", + "PublicKey", + "PublicKeySize", + "SeedSize", + "Sign", + "SignatureSize", + "Verify", + "VerifyWithOptions", + }, + "crypto/elliptic": { + "Curve", + "CurveParams", + "GenerateKey", + "Marshal", + "MarshalCompressed", + "P224", + "P256", + "P384", + "P521", + "Unmarshal", + "UnmarshalCompressed", + }, + "crypto/hmac": { + "Equal", + "New", + }, + "crypto/md5": { + "BlockSize", + "New", + "Size", + "Sum", + }, + "crypto/rand": { + "Int", + "Prime", + "Read", + "Reader", + }, + "crypto/rc4": { + "Cipher", + "KeySizeError", + "NewCipher", + }, + "crypto/rsa": { + "CRTValue", + "DecryptOAEP", + "DecryptPKCS1v15", + "DecryptPKCS1v15SessionKey", + "EncryptOAEP", + "EncryptPKCS1v15", + "ErrDecryption", + "ErrMessageTooLong", + "ErrVerification", + "GenerateKey", + "GenerateMultiPrimeKey", + "OAEPOptions", + "PKCS1v15DecryptOptions", + "PSSOptions", + "PSSSaltLengthAuto", + "PSSSaltLengthEqualsHash", + "PrecomputedValues", + "PrivateKey", + "PublicKey", + "SignPKCS1v15", + "SignPSS", + "VerifyPKCS1v15", + "VerifyPSS", + }, + "crypto/sha1": { + "BlockSize", + "New", + "Size", + "Sum", + }, + "crypto/sha256": { + "BlockSize", + "New", + "New224", + "Size", + "Size224", + "Sum224", + "Sum256", + }, + "crypto/sha512": { + "BlockSize", + "New", + "New384", + "New512_224", + "New512_256", + "Size", + "Size224", + "Size256", + "Size384", + "Sum384", + "Sum512", + "Sum512_224", + "Sum512_256", + }, + "crypto/subtle": { + "ConstantTimeByteEq", + "ConstantTimeCompare", + "ConstantTimeCopy", + "ConstantTimeEq", + "ConstantTimeLessOrEq", + "ConstantTimeSelect", + "XORBytes", + }, + "crypto/tls": { + "AlertError", + "Certificate", + "CertificateRequestInfo", + "CertificateVerificationError", + "CipherSuite", + "CipherSuiteName", + "CipherSuites", + "Client", + "ClientAuthType", + "ClientHelloInfo", + "ClientSessionCache", + "ClientSessionState", + "Config", + "Conn", + "ConnectionState", + "CurveID", + "CurveP256", + "CurveP384", + "CurveP521", + "Dial", + "DialWithDialer", + "Dialer", + "ECDSAWithP256AndSHA256", + "ECDSAWithP384AndSHA384", + "ECDSAWithP521AndSHA512", + "ECDSAWithSHA1", + "Ed25519", + "InsecureCipherSuites", + "Listen", + "LoadX509KeyPair", + "NewLRUClientSessionCache", + "NewListener", + "NewResumptionState", + "NoClientCert", + "PKCS1WithSHA1", + "PKCS1WithSHA256", + "PKCS1WithSHA384", + "PKCS1WithSHA512", + "PSSWithSHA256", + "PSSWithSHA384", + "PSSWithSHA512", + "ParseSessionState", + "QUICClient", + "QUICConfig", + "QUICConn", + "QUICEncryptionLevel", + "QUICEncryptionLevelApplication", + "QUICEncryptionLevelEarly", + "QUICEncryptionLevelHandshake", + "QUICEncryptionLevelInitial", + "QUICEvent", + "QUICEventKind", + "QUICHandshakeDone", + "QUICNoEvent", + "QUICRejectedEarlyData", + "QUICServer", + "QUICSessionTicketOptions", + "QUICSetReadSecret", + "QUICSetWriteSecret", + "QUICTransportParameters", + "QUICTransportParametersRequired", + "QUICWriteData", + "RecordHeaderError", + "RenegotiateFreelyAsClient", + "RenegotiateNever", + "RenegotiateOnceAsClient", + "RenegotiationSupport", + "RequestClientCert", + "RequireAndVerifyClientCert", + "RequireAnyClientCert", + "Server", + "SessionState", + "SignatureScheme", + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_FALLBACK_SCSV", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_RC4_128_SHA", + "VerifyClientCertIfGiven", + "VersionName", + "VersionSSL30", + "VersionTLS10", + "VersionTLS11", + "VersionTLS12", + "VersionTLS13", + "X25519", + "X509KeyPair", + }, + "crypto/x509": { + "CANotAuthorizedForExtKeyUsage", + "CANotAuthorizedForThisName", + "CertPool", + "Certificate", + "CertificateInvalidError", + "CertificateRequest", + "ConstraintViolationError", + "CreateCertificate", + "CreateCertificateRequest", + "CreateRevocationList", + "DSA", + "DSAWithSHA1", + "DSAWithSHA256", + "DecryptPEMBlock", + "ECDSA", + "ECDSAWithSHA1", + "ECDSAWithSHA256", + "ECDSAWithSHA384", + "ECDSAWithSHA512", + "Ed25519", + "EncryptPEMBlock", + "ErrUnsupportedAlgorithm", + "Expired", + "ExtKeyUsage", + "ExtKeyUsageAny", + "ExtKeyUsageClientAuth", + "ExtKeyUsageCodeSigning", + "ExtKeyUsageEmailProtection", + "ExtKeyUsageIPSECEndSystem", + "ExtKeyUsageIPSECTunnel", + "ExtKeyUsageIPSECUser", + "ExtKeyUsageMicrosoftCommercialCodeSigning", + "ExtKeyUsageMicrosoftKernelCodeSigning", + "ExtKeyUsageMicrosoftServerGatedCrypto", + "ExtKeyUsageNetscapeServerGatedCrypto", + "ExtKeyUsageOCSPSigning", + "ExtKeyUsageServerAuth", + "ExtKeyUsageTimeStamping", + "HostnameError", + "IncompatibleUsage", + "IncorrectPasswordError", + "InsecureAlgorithmError", + "InvalidReason", + "IsEncryptedPEMBlock", + "KeyUsage", + "KeyUsageCRLSign", + "KeyUsageCertSign", + "KeyUsageContentCommitment", + "KeyUsageDataEncipherment", + "KeyUsageDecipherOnly", + "KeyUsageDigitalSignature", + "KeyUsageEncipherOnly", + "KeyUsageKeyAgreement", + "KeyUsageKeyEncipherment", + "MD2WithRSA", + "MD5WithRSA", + "MarshalECPrivateKey", + "MarshalPKCS1PrivateKey", + "MarshalPKCS1PublicKey", + "MarshalPKCS8PrivateKey", + "MarshalPKIXPublicKey", + "NameConstraintsWithoutSANs", + "NameMismatch", + "NewCertPool", + "NotAuthorizedToSign", + "PEMCipher", + "PEMCipher3DES", + "PEMCipherAES128", + "PEMCipherAES192", + "PEMCipherAES256", + "PEMCipherDES", + "ParseCRL", + "ParseCertificate", + "ParseCertificateRequest", + "ParseCertificates", + "ParseDERCRL", + "ParseECPrivateKey", + "ParsePKCS1PrivateKey", + "ParsePKCS1PublicKey", + "ParsePKCS8PrivateKey", + "ParsePKIXPublicKey", + "ParseRevocationList", + "PublicKeyAlgorithm", + "PureEd25519", + "RSA", + "RevocationList", + "RevocationListEntry", + "SHA1WithRSA", + "SHA256WithRSA", + "SHA256WithRSAPSS", + "SHA384WithRSA", + "SHA384WithRSAPSS", + "SHA512WithRSA", + "SHA512WithRSAPSS", + "SetFallbackRoots", + "SignatureAlgorithm", + "SystemCertPool", + "SystemRootsError", + "TooManyConstraints", + "TooManyIntermediates", + "UnconstrainedName", + "UnhandledCriticalExtension", + "UnknownAuthorityError", + "UnknownPublicKeyAlgorithm", + "UnknownSignatureAlgorithm", + "VerifyOptions", + }, + "crypto/x509/pkix": { + "AlgorithmIdentifier", + "AttributeTypeAndValue", + "AttributeTypeAndValueSET", + "CertificateList", + "Extension", + "Name", + "RDNSequence", + "RelativeDistinguishedNameSET", + "RevokedCertificate", + "TBSCertificateList", + }, + "database/sql": { + "ColumnType", + "Conn", + "DB", + "DBStats", + "Drivers", + "ErrConnDone", + "ErrNoRows", + "ErrTxDone", + "IsolationLevel", + "LevelDefault", + "LevelLinearizable", + "LevelReadCommitted", + "LevelReadUncommitted", + "LevelRepeatableRead", + "LevelSerializable", + "LevelSnapshot", + "LevelWriteCommitted", + "Named", + "NamedArg", + "NullBool", + "NullByte", + "NullFloat64", + "NullInt16", + "NullInt32", + "NullInt64", + "NullString", + "NullTime", + "Open", + "OpenDB", + "Out", + "RawBytes", + "Register", + "Result", + "Row", + "Rows", + "Scanner", + "Stmt", + "Tx", + "TxOptions", + }, + "database/sql/driver": { + "Bool", + "ColumnConverter", + "Conn", + "ConnBeginTx", + "ConnPrepareContext", + "Connector", + "DefaultParameterConverter", + "Driver", + "DriverContext", + "ErrBadConn", + "ErrRemoveArgument", + "ErrSkip", + "Execer", + "ExecerContext", + "Int32", + "IsScanValue", + "IsValue", + "IsolationLevel", + "NamedValue", + "NamedValueChecker", + "NotNull", + "Null", + "Pinger", + "Queryer", + "QueryerContext", + "Result", + "ResultNoRows", + "Rows", + "RowsAffected", + "RowsColumnTypeDatabaseTypeName", + "RowsColumnTypeLength", + "RowsColumnTypeNullable", + "RowsColumnTypePrecisionScale", + "RowsColumnTypeScanType", + "RowsNextResultSet", + "SessionResetter", + "Stmt", + "StmtExecContext", + "StmtQueryContext", + "String", + "Tx", + "TxOptions", + "Validator", + "Value", + "ValueConverter", + "Valuer", + }, + "debug/buildinfo": { + "BuildInfo", + "Read", + "ReadFile", + }, + "debug/dwarf": { + "AddrType", + "ArrayType", + "Attr", + "AttrAbstractOrigin", + "AttrAccessibility", + "AttrAddrBase", + "AttrAddrClass", + "AttrAlignment", + "AttrAllocated", + "AttrArtificial", + "AttrAssociated", + "AttrBaseTypes", + "AttrBinaryScale", + "AttrBitOffset", + "AttrBitSize", + "AttrByteSize", + "AttrCallAllCalls", + "AttrCallAllSourceCalls", + "AttrCallAllTailCalls", + "AttrCallColumn", + "AttrCallDataLocation", + "AttrCallDataValue", + "AttrCallFile", + "AttrCallLine", + "AttrCallOrigin", + "AttrCallPC", + "AttrCallParameter", + "AttrCallReturnPC", + "AttrCallTailCall", + "AttrCallTarget", + "AttrCallTargetClobbered", + "AttrCallValue", + "AttrCalling", + "AttrCommonRef", + "AttrCompDir", + "AttrConstExpr", + "AttrConstValue", + "AttrContainingType", + "AttrCount", + "AttrDataBitOffset", + "AttrDataLocation", + "AttrDataMemberLoc", + "AttrDecimalScale", + "AttrDecimalSign", + "AttrDeclColumn", + "AttrDeclFile", + "AttrDeclLine", + "AttrDeclaration", + "AttrDefaultValue", + "AttrDefaulted", + "AttrDeleted", + "AttrDescription", + "AttrDigitCount", + "AttrDiscr", + "AttrDiscrList", + "AttrDiscrValue", + "AttrDwoName", + "AttrElemental", + "AttrEncoding", + "AttrEndianity", + "AttrEntrypc", + "AttrEnumClass", + "AttrExplicit", + "AttrExportSymbols", + "AttrExtension", + "AttrExternal", + "AttrFrameBase", + "AttrFriend", + "AttrHighpc", + "AttrIdentifierCase", + "AttrImport", + "AttrInline", + "AttrIsOptional", + "AttrLanguage", + "AttrLinkageName", + "AttrLocation", + "AttrLoclistsBase", + "AttrLowerBound", + "AttrLowpc", + "AttrMacroInfo", + "AttrMacros", + "AttrMainSubprogram", + "AttrMutable", + "AttrName", + "AttrNamelistItem", + "AttrNoreturn", + "AttrObjectPointer", + "AttrOrdering", + "AttrPictureString", + "AttrPriority", + "AttrProducer", + "AttrPrototyped", + "AttrPure", + "AttrRanges", + "AttrRank", + "AttrRecursive", + "AttrReference", + "AttrReturnAddr", + "AttrRnglistsBase", + "AttrRvalueReference", + "AttrSegment", + "AttrSibling", + "AttrSignature", + "AttrSmall", + "AttrSpecification", + "AttrStartScope", + "AttrStaticLink", + "AttrStmtList", + "AttrStrOffsetsBase", + "AttrStride", + "AttrStrideSize", + "AttrStringLength", + "AttrStringLengthBitSize", + "AttrStringLengthByteSize", + "AttrThreadsScaled", + "AttrTrampoline", + "AttrType", + "AttrUpperBound", + "AttrUseLocation", + "AttrUseUTF8", + "AttrVarParam", + "AttrVirtuality", + "AttrVisibility", + "AttrVtableElemLoc", + "BasicType", + "BoolType", + "CharType", + "Class", + "ClassAddrPtr", + "ClassAddress", + "ClassBlock", + "ClassConstant", + "ClassExprLoc", + "ClassFlag", + "ClassLinePtr", + "ClassLocList", + "ClassLocListPtr", + "ClassMacPtr", + "ClassRangeListPtr", + "ClassReference", + "ClassReferenceAlt", + "ClassReferenceSig", + "ClassRngList", + "ClassRngListsPtr", + "ClassStrOffsetsPtr", + "ClassString", + "ClassStringAlt", + "ClassUnknown", + "CommonType", + "ComplexType", + "Data", + "DecodeError", + "DotDotDotType", + "Entry", + "EnumType", + "EnumValue", + "ErrUnknownPC", + "Field", + "FloatType", + "FuncType", + "IntType", + "LineEntry", + "LineFile", + "LineReader", + "LineReaderPos", + "New", + "Offset", + "PtrType", + "QualType", + "Reader", + "StructField", + "StructType", + "Tag", + "TagAccessDeclaration", + "TagArrayType", + "TagAtomicType", + "TagBaseType", + "TagCallSite", + "TagCallSiteParameter", + "TagCatchDwarfBlock", + "TagClassType", + "TagCoarrayType", + "TagCommonDwarfBlock", + "TagCommonInclusion", + "TagCompileUnit", + "TagCondition", + "TagConstType", + "TagConstant", + "TagDwarfProcedure", + "TagDynamicType", + "TagEntryPoint", + "TagEnumerationType", + "TagEnumerator", + "TagFileType", + "TagFormalParameter", + "TagFriend", + "TagGenericSubrange", + "TagImmutableType", + "TagImportedDeclaration", + "TagImportedModule", + "TagImportedUnit", + "TagInheritance", + "TagInlinedSubroutine", + "TagInterfaceType", + "TagLabel", + "TagLexDwarfBlock", + "TagMember", + "TagModule", + "TagMutableType", + "TagNamelist", + "TagNamelistItem", + "TagNamespace", + "TagPackedType", + "TagPartialUnit", + "TagPointerType", + "TagPtrToMemberType", + "TagReferenceType", + "TagRestrictType", + "TagRvalueReferenceType", + "TagSetType", + "TagSharedType", + "TagSkeletonUnit", + "TagStringType", + "TagStructType", + "TagSubprogram", + "TagSubrangeType", + "TagSubroutineType", + "TagTemplateAlias", + "TagTemplateTypeParameter", + "TagTemplateValueParameter", + "TagThrownType", + "TagTryDwarfBlock", + "TagTypeUnit", + "TagTypedef", + "TagUnionType", + "TagUnspecifiedParameters", + "TagUnspecifiedType", + "TagVariable", + "TagVariant", + "TagVariantPart", + "TagVolatileType", + "TagWithStmt", + "Type", + "TypedefType", + "UcharType", + "UintType", + "UnspecifiedType", + "UnsupportedType", + "VoidType", + }, + "debug/elf": { + "ARM_MAGIC_TRAMP_NUMBER", + "COMPRESS_HIOS", + "COMPRESS_HIPROC", + "COMPRESS_LOOS", + "COMPRESS_LOPROC", + "COMPRESS_ZLIB", + "COMPRESS_ZSTD", + "Chdr32", + "Chdr64", + "Class", + "CompressionType", + "DF_1_CONFALT", + "DF_1_DIRECT", + "DF_1_DISPRELDNE", + "DF_1_DISPRELPND", + "DF_1_EDITED", + "DF_1_ENDFILTEE", + "DF_1_GLOBAL", + "DF_1_GLOBAUDIT", + "DF_1_GROUP", + "DF_1_IGNMULDEF", + "DF_1_INITFIRST", + "DF_1_INTERPOSE", + "DF_1_KMOD", + "DF_1_LOADFLTR", + "DF_1_NOCOMMON", + "DF_1_NODEFLIB", + "DF_1_NODELETE", + "DF_1_NODIRECT", + "DF_1_NODUMP", + "DF_1_NOHDR", + "DF_1_NOKSYMS", + "DF_1_NOOPEN", + "DF_1_NORELOC", + "DF_1_NOW", + "DF_1_ORIGIN", + "DF_1_PIE", + "DF_1_SINGLETON", + "DF_1_STUB", + "DF_1_SYMINTPOSE", + "DF_1_TRANS", + "DF_1_WEAKFILTER", + "DF_BIND_NOW", + "DF_ORIGIN", + "DF_STATIC_TLS", + "DF_SYMBOLIC", + "DF_TEXTREL", + "DT_ADDRRNGHI", + "DT_ADDRRNGLO", + "DT_AUDIT", + "DT_AUXILIARY", + "DT_BIND_NOW", + "DT_CHECKSUM", + "DT_CONFIG", + "DT_DEBUG", + "DT_DEPAUDIT", + "DT_ENCODING", + "DT_FEATURE", + "DT_FILTER", + "DT_FINI", + "DT_FINI_ARRAY", + "DT_FINI_ARRAYSZ", + "DT_FLAGS", + "DT_FLAGS_1", + "DT_GNU_CONFLICT", + "DT_GNU_CONFLICTSZ", + "DT_GNU_HASH", + "DT_GNU_LIBLIST", + "DT_GNU_LIBLISTSZ", + "DT_GNU_PRELINKED", + "DT_HASH", + "DT_HIOS", + "DT_HIPROC", + "DT_INIT", + "DT_INIT_ARRAY", + "DT_INIT_ARRAYSZ", + "DT_JMPREL", + "DT_LOOS", + "DT_LOPROC", + "DT_MIPS_AUX_DYNAMIC", + "DT_MIPS_BASE_ADDRESS", + "DT_MIPS_COMPACT_SIZE", + "DT_MIPS_CONFLICT", + "DT_MIPS_CONFLICTNO", + "DT_MIPS_CXX_FLAGS", + "DT_MIPS_DELTA_CLASS", + "DT_MIPS_DELTA_CLASSSYM", + "DT_MIPS_DELTA_CLASSSYM_NO", + "DT_MIPS_DELTA_CLASS_NO", + "DT_MIPS_DELTA_INSTANCE", + "DT_MIPS_DELTA_INSTANCE_NO", + "DT_MIPS_DELTA_RELOC", + "DT_MIPS_DELTA_RELOC_NO", + "DT_MIPS_DELTA_SYM", + "DT_MIPS_DELTA_SYM_NO", + "DT_MIPS_DYNSTR_ALIGN", + "DT_MIPS_FLAGS", + "DT_MIPS_GOTSYM", + "DT_MIPS_GP_VALUE", + "DT_MIPS_HIDDEN_GOTIDX", + "DT_MIPS_HIPAGENO", + "DT_MIPS_ICHECKSUM", + "DT_MIPS_INTERFACE", + "DT_MIPS_INTERFACE_SIZE", + "DT_MIPS_IVERSION", + "DT_MIPS_LIBLIST", + "DT_MIPS_LIBLISTNO", + "DT_MIPS_LOCALPAGE_GOTIDX", + "DT_MIPS_LOCAL_GOTIDX", + "DT_MIPS_LOCAL_GOTNO", + "DT_MIPS_MSYM", + "DT_MIPS_OPTIONS", + "DT_MIPS_PERF_SUFFIX", + "DT_MIPS_PIXIE_INIT", + "DT_MIPS_PLTGOT", + "DT_MIPS_PROTECTED_GOTIDX", + "DT_MIPS_RLD_MAP", + "DT_MIPS_RLD_MAP_REL", + "DT_MIPS_RLD_TEXT_RESOLVE_ADDR", + "DT_MIPS_RLD_VERSION", + "DT_MIPS_RWPLT", + "DT_MIPS_SYMBOL_LIB", + "DT_MIPS_SYMTABNO", + "DT_MIPS_TIME_STAMP", + "DT_MIPS_UNREFEXTNO", + "DT_MOVEENT", + "DT_MOVESZ", + "DT_MOVETAB", + "DT_NEEDED", + "DT_NULL", + "DT_PLTGOT", + "DT_PLTPAD", + "DT_PLTPADSZ", + "DT_PLTREL", + "DT_PLTRELSZ", + "DT_POSFLAG_1", + "DT_PPC64_GLINK", + "DT_PPC64_OPD", + "DT_PPC64_OPDSZ", + "DT_PPC64_OPT", + "DT_PPC_GOT", + "DT_PPC_OPT", + "DT_PREINIT_ARRAY", + "DT_PREINIT_ARRAYSZ", + "DT_REL", + "DT_RELA", + "DT_RELACOUNT", + "DT_RELAENT", + "DT_RELASZ", + "DT_RELCOUNT", + "DT_RELENT", + "DT_RELSZ", + "DT_RPATH", + "DT_RUNPATH", + "DT_SONAME", + "DT_SPARC_REGISTER", + "DT_STRSZ", + "DT_STRTAB", + "DT_SYMBOLIC", + "DT_SYMENT", + "DT_SYMINENT", + "DT_SYMINFO", + "DT_SYMINSZ", + "DT_SYMTAB", + "DT_SYMTAB_SHNDX", + "DT_TEXTREL", + "DT_TLSDESC_GOT", + "DT_TLSDESC_PLT", + "DT_USED", + "DT_VALRNGHI", + "DT_VALRNGLO", + "DT_VERDEF", + "DT_VERDEFNUM", + "DT_VERNEED", + "DT_VERNEEDNUM", + "DT_VERSYM", + "Data", + "Dyn32", + "Dyn64", + "DynFlag", + "DynFlag1", + "DynTag", + "EI_ABIVERSION", + "EI_CLASS", + "EI_DATA", + "EI_NIDENT", + "EI_OSABI", + "EI_PAD", + "EI_VERSION", + "ELFCLASS32", + "ELFCLASS64", + "ELFCLASSNONE", + "ELFDATA2LSB", + "ELFDATA2MSB", + "ELFDATANONE", + "ELFMAG", + "ELFOSABI_86OPEN", + "ELFOSABI_AIX", + "ELFOSABI_ARM", + "ELFOSABI_AROS", + "ELFOSABI_CLOUDABI", + "ELFOSABI_FENIXOS", + "ELFOSABI_FREEBSD", + "ELFOSABI_HPUX", + "ELFOSABI_HURD", + "ELFOSABI_IRIX", + "ELFOSABI_LINUX", + "ELFOSABI_MODESTO", + "ELFOSABI_NETBSD", + "ELFOSABI_NONE", + "ELFOSABI_NSK", + "ELFOSABI_OPENBSD", + "ELFOSABI_OPENVMS", + "ELFOSABI_SOLARIS", + "ELFOSABI_STANDALONE", + "ELFOSABI_TRU64", + "EM_386", + "EM_486", + "EM_56800EX", + "EM_68HC05", + "EM_68HC08", + "EM_68HC11", + "EM_68HC12", + "EM_68HC16", + "EM_68K", + "EM_78KOR", + "EM_8051", + "EM_860", + "EM_88K", + "EM_960", + "EM_AARCH64", + "EM_ALPHA", + "EM_ALPHA_STD", + "EM_ALTERA_NIOS2", + "EM_AMDGPU", + "EM_ARC", + "EM_ARCA", + "EM_ARC_COMPACT", + "EM_ARC_COMPACT2", + "EM_ARM", + "EM_AVR", + "EM_AVR32", + "EM_BA1", + "EM_BA2", + "EM_BLACKFIN", + "EM_BPF", + "EM_C166", + "EM_CDP", + "EM_CE", + "EM_CLOUDSHIELD", + "EM_COGE", + "EM_COLDFIRE", + "EM_COOL", + "EM_COREA_1ST", + "EM_COREA_2ND", + "EM_CR", + "EM_CR16", + "EM_CRAYNV2", + "EM_CRIS", + "EM_CRX", + "EM_CSR_KALIMBA", + "EM_CUDA", + "EM_CYPRESS_M8C", + "EM_D10V", + "EM_D30V", + "EM_DSP24", + "EM_DSPIC30F", + "EM_DXP", + "EM_ECOG1", + "EM_ECOG16", + "EM_ECOG1X", + "EM_ECOG2", + "EM_ETPU", + "EM_EXCESS", + "EM_F2MC16", + "EM_FIREPATH", + "EM_FR20", + "EM_FR30", + "EM_FT32", + "EM_FX66", + "EM_H8S", + "EM_H8_300", + "EM_H8_300H", + "EM_H8_500", + "EM_HUANY", + "EM_IA_64", + "EM_INTEL205", + "EM_INTEL206", + "EM_INTEL207", + "EM_INTEL208", + "EM_INTEL209", + "EM_IP2K", + "EM_JAVELIN", + "EM_K10M", + "EM_KM32", + "EM_KMX16", + "EM_KMX32", + "EM_KMX8", + "EM_KVARC", + "EM_L10M", + "EM_LANAI", + "EM_LATTICEMICO32", + "EM_LOONGARCH", + "EM_M16C", + "EM_M32", + "EM_M32C", + "EM_M32R", + "EM_MANIK", + "EM_MAX", + "EM_MAXQ30", + "EM_MCHP_PIC", + "EM_MCST_ELBRUS", + "EM_ME16", + "EM_METAG", + "EM_MICROBLAZE", + "EM_MIPS", + "EM_MIPS_RS3_LE", + "EM_MIPS_RS4_BE", + "EM_MIPS_X", + "EM_MMA", + "EM_MMDSP_PLUS", + "EM_MMIX", + "EM_MN10200", + "EM_MN10300", + "EM_MOXIE", + "EM_MSP430", + "EM_NCPU", + "EM_NDR1", + "EM_NDS32", + "EM_NONE", + "EM_NORC", + "EM_NS32K", + "EM_OPEN8", + "EM_OPENRISC", + "EM_PARISC", + "EM_PCP", + "EM_PDP10", + "EM_PDP11", + "EM_PDSP", + "EM_PJ", + "EM_PPC", + "EM_PPC64", + "EM_PRISM", + "EM_QDSP6", + "EM_R32C", + "EM_RCE", + "EM_RH32", + "EM_RISCV", + "EM_RL78", + "EM_RS08", + "EM_RX", + "EM_S370", + "EM_S390", + "EM_SCORE7", + "EM_SEP", + "EM_SE_C17", + "EM_SE_C33", + "EM_SH", + "EM_SHARC", + "EM_SLE9X", + "EM_SNP1K", + "EM_SPARC", + "EM_SPARC32PLUS", + "EM_SPARCV9", + "EM_ST100", + "EM_ST19", + "EM_ST200", + "EM_ST7", + "EM_ST9PLUS", + "EM_STARCORE", + "EM_STM8", + "EM_STXP7X", + "EM_SVX", + "EM_TILE64", + "EM_TILEGX", + "EM_TILEPRO", + "EM_TINYJ", + "EM_TI_ARP32", + "EM_TI_C2000", + "EM_TI_C5500", + "EM_TI_C6000", + "EM_TI_PRU", + "EM_TMM_GPP", + "EM_TPC", + "EM_TRICORE", + "EM_TRIMEDIA", + "EM_TSK3000", + "EM_UNICORE", + "EM_V800", + "EM_V850", + "EM_VAX", + "EM_VIDEOCORE", + "EM_VIDEOCORE3", + "EM_VIDEOCORE5", + "EM_VISIUM", + "EM_VPP500", + "EM_X86_64", + "EM_XCORE", + "EM_XGATE", + "EM_XIMO16", + "EM_XTENSA", + "EM_Z80", + "EM_ZSP", + "ET_CORE", + "ET_DYN", + "ET_EXEC", + "ET_HIOS", + "ET_HIPROC", + "ET_LOOS", + "ET_LOPROC", + "ET_NONE", + "ET_REL", + "EV_CURRENT", + "EV_NONE", + "ErrNoSymbols", + "File", + "FileHeader", + "FormatError", + "Header32", + "Header64", + "ImportedSymbol", + "Machine", + "NT_FPREGSET", + "NT_PRPSINFO", + "NT_PRSTATUS", + "NType", + "NewFile", + "OSABI", + "Open", + "PF_MASKOS", + "PF_MASKPROC", + "PF_R", + "PF_W", + "PF_X", + "PT_AARCH64_ARCHEXT", + "PT_AARCH64_UNWIND", + "PT_ARM_ARCHEXT", + "PT_ARM_EXIDX", + "PT_DYNAMIC", + "PT_GNU_EH_FRAME", + "PT_GNU_MBIND_HI", + "PT_GNU_MBIND_LO", + "PT_GNU_PROPERTY", + "PT_GNU_RELRO", + "PT_GNU_STACK", + "PT_HIOS", + "PT_HIPROC", + "PT_INTERP", + "PT_LOAD", + "PT_LOOS", + "PT_LOPROC", + "PT_MIPS_ABIFLAGS", + "PT_MIPS_OPTIONS", + "PT_MIPS_REGINFO", + "PT_MIPS_RTPROC", + "PT_NOTE", + "PT_NULL", + "PT_OPENBSD_BOOTDATA", + "PT_OPENBSD_RANDOMIZE", + "PT_OPENBSD_WXNEEDED", + "PT_PAX_FLAGS", + "PT_PHDR", + "PT_S390_PGSTE", + "PT_SHLIB", + "PT_SUNWSTACK", + "PT_SUNW_EH_FRAME", + "PT_TLS", + "Prog", + "Prog32", + "Prog64", + "ProgFlag", + "ProgHeader", + "ProgType", + "R_386", + "R_386_16", + "R_386_32", + "R_386_32PLT", + "R_386_8", + "R_386_COPY", + "R_386_GLOB_DAT", + "R_386_GOT32", + "R_386_GOT32X", + "R_386_GOTOFF", + "R_386_GOTPC", + "R_386_IRELATIVE", + "R_386_JMP_SLOT", + "R_386_NONE", + "R_386_PC16", + "R_386_PC32", + "R_386_PC8", + "R_386_PLT32", + "R_386_RELATIVE", + "R_386_SIZE32", + "R_386_TLS_DESC", + "R_386_TLS_DESC_CALL", + "R_386_TLS_DTPMOD32", + "R_386_TLS_DTPOFF32", + "R_386_TLS_GD", + "R_386_TLS_GD_32", + "R_386_TLS_GD_CALL", + "R_386_TLS_GD_POP", + "R_386_TLS_GD_PUSH", + "R_386_TLS_GOTDESC", + "R_386_TLS_GOTIE", + "R_386_TLS_IE", + "R_386_TLS_IE_32", + "R_386_TLS_LDM", + "R_386_TLS_LDM_32", + "R_386_TLS_LDM_CALL", + "R_386_TLS_LDM_POP", + "R_386_TLS_LDM_PUSH", + "R_386_TLS_LDO_32", + "R_386_TLS_LE", + "R_386_TLS_LE_32", + "R_386_TLS_TPOFF", + "R_386_TLS_TPOFF32", + "R_390", + "R_390_12", + "R_390_16", + "R_390_20", + "R_390_32", + "R_390_64", + "R_390_8", + "R_390_COPY", + "R_390_GLOB_DAT", + "R_390_GOT12", + "R_390_GOT16", + "R_390_GOT20", + "R_390_GOT32", + "R_390_GOT64", + "R_390_GOTENT", + "R_390_GOTOFF", + "R_390_GOTOFF16", + "R_390_GOTOFF64", + "R_390_GOTPC", + "R_390_GOTPCDBL", + "R_390_GOTPLT12", + "R_390_GOTPLT16", + "R_390_GOTPLT20", + "R_390_GOTPLT32", + "R_390_GOTPLT64", + "R_390_GOTPLTENT", + "R_390_GOTPLTOFF16", + "R_390_GOTPLTOFF32", + "R_390_GOTPLTOFF64", + "R_390_JMP_SLOT", + "R_390_NONE", + "R_390_PC16", + "R_390_PC16DBL", + "R_390_PC32", + "R_390_PC32DBL", + "R_390_PC64", + "R_390_PLT16DBL", + "R_390_PLT32", + "R_390_PLT32DBL", + "R_390_PLT64", + "R_390_RELATIVE", + "R_390_TLS_DTPMOD", + "R_390_TLS_DTPOFF", + "R_390_TLS_GD32", + "R_390_TLS_GD64", + "R_390_TLS_GDCALL", + "R_390_TLS_GOTIE12", + "R_390_TLS_GOTIE20", + "R_390_TLS_GOTIE32", + "R_390_TLS_GOTIE64", + "R_390_TLS_IE32", + "R_390_TLS_IE64", + "R_390_TLS_IEENT", + "R_390_TLS_LDCALL", + "R_390_TLS_LDM32", + "R_390_TLS_LDM64", + "R_390_TLS_LDO32", + "R_390_TLS_LDO64", + "R_390_TLS_LE32", + "R_390_TLS_LE64", + "R_390_TLS_LOAD", + "R_390_TLS_TPOFF", + "R_AARCH64", + "R_AARCH64_ABS16", + "R_AARCH64_ABS32", + "R_AARCH64_ABS64", + "R_AARCH64_ADD_ABS_LO12_NC", + "R_AARCH64_ADR_GOT_PAGE", + "R_AARCH64_ADR_PREL_LO21", + "R_AARCH64_ADR_PREL_PG_HI21", + "R_AARCH64_ADR_PREL_PG_HI21_NC", + "R_AARCH64_CALL26", + "R_AARCH64_CONDBR19", + "R_AARCH64_COPY", + "R_AARCH64_GLOB_DAT", + "R_AARCH64_GOT_LD_PREL19", + "R_AARCH64_IRELATIVE", + "R_AARCH64_JUMP26", + "R_AARCH64_JUMP_SLOT", + "R_AARCH64_LD64_GOTOFF_LO15", + "R_AARCH64_LD64_GOTPAGE_LO15", + "R_AARCH64_LD64_GOT_LO12_NC", + "R_AARCH64_LDST128_ABS_LO12_NC", + "R_AARCH64_LDST16_ABS_LO12_NC", + "R_AARCH64_LDST32_ABS_LO12_NC", + "R_AARCH64_LDST64_ABS_LO12_NC", + "R_AARCH64_LDST8_ABS_LO12_NC", + "R_AARCH64_LD_PREL_LO19", + "R_AARCH64_MOVW_SABS_G0", + "R_AARCH64_MOVW_SABS_G1", + "R_AARCH64_MOVW_SABS_G2", + "R_AARCH64_MOVW_UABS_G0", + "R_AARCH64_MOVW_UABS_G0_NC", + "R_AARCH64_MOVW_UABS_G1", + "R_AARCH64_MOVW_UABS_G1_NC", + "R_AARCH64_MOVW_UABS_G2", + "R_AARCH64_MOVW_UABS_G2_NC", + "R_AARCH64_MOVW_UABS_G3", + "R_AARCH64_NONE", + "R_AARCH64_NULL", + "R_AARCH64_P32_ABS16", + "R_AARCH64_P32_ABS32", + "R_AARCH64_P32_ADD_ABS_LO12_NC", + "R_AARCH64_P32_ADR_GOT_PAGE", + "R_AARCH64_P32_ADR_PREL_LO21", + "R_AARCH64_P32_ADR_PREL_PG_HI21", + "R_AARCH64_P32_CALL26", + "R_AARCH64_P32_CONDBR19", + "R_AARCH64_P32_COPY", + "R_AARCH64_P32_GLOB_DAT", + "R_AARCH64_P32_GOT_LD_PREL19", + "R_AARCH64_P32_IRELATIVE", + "R_AARCH64_P32_JUMP26", + "R_AARCH64_P32_JUMP_SLOT", + "R_AARCH64_P32_LD32_GOT_LO12_NC", + "R_AARCH64_P32_LDST128_ABS_LO12_NC", + "R_AARCH64_P32_LDST16_ABS_LO12_NC", + "R_AARCH64_P32_LDST32_ABS_LO12_NC", + "R_AARCH64_P32_LDST64_ABS_LO12_NC", + "R_AARCH64_P32_LDST8_ABS_LO12_NC", + "R_AARCH64_P32_LD_PREL_LO19", + "R_AARCH64_P32_MOVW_SABS_G0", + "R_AARCH64_P32_MOVW_UABS_G0", + "R_AARCH64_P32_MOVW_UABS_G0_NC", + "R_AARCH64_P32_MOVW_UABS_G1", + "R_AARCH64_P32_PREL16", + "R_AARCH64_P32_PREL32", + "R_AARCH64_P32_RELATIVE", + "R_AARCH64_P32_TLSDESC", + "R_AARCH64_P32_TLSDESC_ADD_LO12_NC", + "R_AARCH64_P32_TLSDESC_ADR_PAGE21", + "R_AARCH64_P32_TLSDESC_ADR_PREL21", + "R_AARCH64_P32_TLSDESC_CALL", + "R_AARCH64_P32_TLSDESC_LD32_LO12_NC", + "R_AARCH64_P32_TLSDESC_LD_PREL19", + "R_AARCH64_P32_TLSGD_ADD_LO12_NC", + "R_AARCH64_P32_TLSGD_ADR_PAGE21", + "R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21", + "R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC", + "R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19", + "R_AARCH64_P32_TLSLE_ADD_TPREL_HI12", + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12", + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G1", + "R_AARCH64_P32_TLS_DTPMOD", + "R_AARCH64_P32_TLS_DTPREL", + "R_AARCH64_P32_TLS_TPREL", + "R_AARCH64_P32_TSTBR14", + "R_AARCH64_PREL16", + "R_AARCH64_PREL32", + "R_AARCH64_PREL64", + "R_AARCH64_RELATIVE", + "R_AARCH64_TLSDESC", + "R_AARCH64_TLSDESC_ADD", + "R_AARCH64_TLSDESC_ADD_LO12_NC", + "R_AARCH64_TLSDESC_ADR_PAGE21", + "R_AARCH64_TLSDESC_ADR_PREL21", + "R_AARCH64_TLSDESC_CALL", + "R_AARCH64_TLSDESC_LD64_LO12_NC", + "R_AARCH64_TLSDESC_LDR", + "R_AARCH64_TLSDESC_LD_PREL19", + "R_AARCH64_TLSDESC_OFF_G0_NC", + "R_AARCH64_TLSDESC_OFF_G1", + "R_AARCH64_TLSGD_ADD_LO12_NC", + "R_AARCH64_TLSGD_ADR_PAGE21", + "R_AARCH64_TLSGD_ADR_PREL21", + "R_AARCH64_TLSGD_MOVW_G0_NC", + "R_AARCH64_TLSGD_MOVW_G1", + "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21", + "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", + "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19", + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC", + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1", + "R_AARCH64_TLSLD_ADR_PAGE21", + "R_AARCH64_TLSLD_ADR_PREL21", + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12", + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC", + "R_AARCH64_TLSLE_ADD_TPREL_HI12", + "R_AARCH64_TLSLE_ADD_TPREL_LO12", + "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC", + "R_AARCH64_TLSLE_LDST128_TPREL_LO12", + "R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G0", + "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G1", + "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G2", + "R_AARCH64_TLS_DTPMOD64", + "R_AARCH64_TLS_DTPREL64", + "R_AARCH64_TLS_TPREL64", + "R_AARCH64_TSTBR14", + "R_ALPHA", + "R_ALPHA_BRADDR", + "R_ALPHA_COPY", + "R_ALPHA_GLOB_DAT", + "R_ALPHA_GPDISP", + "R_ALPHA_GPREL32", + "R_ALPHA_GPRELHIGH", + "R_ALPHA_GPRELLOW", + "R_ALPHA_GPVALUE", + "R_ALPHA_HINT", + "R_ALPHA_IMMED_BR_HI32", + "R_ALPHA_IMMED_GP_16", + "R_ALPHA_IMMED_GP_HI32", + "R_ALPHA_IMMED_LO32", + "R_ALPHA_IMMED_SCN_HI32", + "R_ALPHA_JMP_SLOT", + "R_ALPHA_LITERAL", + "R_ALPHA_LITUSE", + "R_ALPHA_NONE", + "R_ALPHA_OP_PRSHIFT", + "R_ALPHA_OP_PSUB", + "R_ALPHA_OP_PUSH", + "R_ALPHA_OP_STORE", + "R_ALPHA_REFLONG", + "R_ALPHA_REFQUAD", + "R_ALPHA_RELATIVE", + "R_ALPHA_SREL16", + "R_ALPHA_SREL32", + "R_ALPHA_SREL64", + "R_ARM", + "R_ARM_ABS12", + "R_ARM_ABS16", + "R_ARM_ABS32", + "R_ARM_ABS32_NOI", + "R_ARM_ABS8", + "R_ARM_ALU_PCREL_15_8", + "R_ARM_ALU_PCREL_23_15", + "R_ARM_ALU_PCREL_7_0", + "R_ARM_ALU_PC_G0", + "R_ARM_ALU_PC_G0_NC", + "R_ARM_ALU_PC_G1", + "R_ARM_ALU_PC_G1_NC", + "R_ARM_ALU_PC_G2", + "R_ARM_ALU_SBREL_19_12_NC", + "R_ARM_ALU_SBREL_27_20_CK", + "R_ARM_ALU_SB_G0", + "R_ARM_ALU_SB_G0_NC", + "R_ARM_ALU_SB_G1", + "R_ARM_ALU_SB_G1_NC", + "R_ARM_ALU_SB_G2", + "R_ARM_AMP_VCALL9", + "R_ARM_BASE_ABS", + "R_ARM_CALL", + "R_ARM_COPY", + "R_ARM_GLOB_DAT", + "R_ARM_GNU_VTENTRY", + "R_ARM_GNU_VTINHERIT", + "R_ARM_GOT32", + "R_ARM_GOTOFF", + "R_ARM_GOTOFF12", + "R_ARM_GOTPC", + "R_ARM_GOTRELAX", + "R_ARM_GOT_ABS", + "R_ARM_GOT_BREL12", + "R_ARM_GOT_PREL", + "R_ARM_IRELATIVE", + "R_ARM_JUMP24", + "R_ARM_JUMP_SLOT", + "R_ARM_LDC_PC_G0", + "R_ARM_LDC_PC_G1", + "R_ARM_LDC_PC_G2", + "R_ARM_LDC_SB_G0", + "R_ARM_LDC_SB_G1", + "R_ARM_LDC_SB_G2", + "R_ARM_LDRS_PC_G0", + "R_ARM_LDRS_PC_G1", + "R_ARM_LDRS_PC_G2", + "R_ARM_LDRS_SB_G0", + "R_ARM_LDRS_SB_G1", + "R_ARM_LDRS_SB_G2", + "R_ARM_LDR_PC_G1", + "R_ARM_LDR_PC_G2", + "R_ARM_LDR_SBREL_11_10_NC", + "R_ARM_LDR_SB_G0", + "R_ARM_LDR_SB_G1", + "R_ARM_LDR_SB_G2", + "R_ARM_ME_TOO", + "R_ARM_MOVT_ABS", + "R_ARM_MOVT_BREL", + "R_ARM_MOVT_PREL", + "R_ARM_MOVW_ABS_NC", + "R_ARM_MOVW_BREL", + "R_ARM_MOVW_BREL_NC", + "R_ARM_MOVW_PREL_NC", + "R_ARM_NONE", + "R_ARM_PC13", + "R_ARM_PC24", + "R_ARM_PLT32", + "R_ARM_PLT32_ABS", + "R_ARM_PREL31", + "R_ARM_PRIVATE_0", + "R_ARM_PRIVATE_1", + "R_ARM_PRIVATE_10", + "R_ARM_PRIVATE_11", + "R_ARM_PRIVATE_12", + "R_ARM_PRIVATE_13", + "R_ARM_PRIVATE_14", + "R_ARM_PRIVATE_15", + "R_ARM_PRIVATE_2", + "R_ARM_PRIVATE_3", + "R_ARM_PRIVATE_4", + "R_ARM_PRIVATE_5", + "R_ARM_PRIVATE_6", + "R_ARM_PRIVATE_7", + "R_ARM_PRIVATE_8", + "R_ARM_PRIVATE_9", + "R_ARM_RABS32", + "R_ARM_RBASE", + "R_ARM_REL32", + "R_ARM_REL32_NOI", + "R_ARM_RELATIVE", + "R_ARM_RPC24", + "R_ARM_RREL32", + "R_ARM_RSBREL32", + "R_ARM_RXPC25", + "R_ARM_SBREL31", + "R_ARM_SBREL32", + "R_ARM_SWI24", + "R_ARM_TARGET1", + "R_ARM_TARGET2", + "R_ARM_THM_ABS5", + "R_ARM_THM_ALU_ABS_G0_NC", + "R_ARM_THM_ALU_ABS_G1_NC", + "R_ARM_THM_ALU_ABS_G2_NC", + "R_ARM_THM_ALU_ABS_G3", + "R_ARM_THM_ALU_PREL_11_0", + "R_ARM_THM_GOT_BREL12", + "R_ARM_THM_JUMP11", + "R_ARM_THM_JUMP19", + "R_ARM_THM_JUMP24", + "R_ARM_THM_JUMP6", + "R_ARM_THM_JUMP8", + "R_ARM_THM_MOVT_ABS", + "R_ARM_THM_MOVT_BREL", + "R_ARM_THM_MOVT_PREL", + "R_ARM_THM_MOVW_ABS_NC", + "R_ARM_THM_MOVW_BREL", + "R_ARM_THM_MOVW_BREL_NC", + "R_ARM_THM_MOVW_PREL_NC", + "R_ARM_THM_PC12", + "R_ARM_THM_PC22", + "R_ARM_THM_PC8", + "R_ARM_THM_RPC22", + "R_ARM_THM_SWI8", + "R_ARM_THM_TLS_CALL", + "R_ARM_THM_TLS_DESCSEQ16", + "R_ARM_THM_TLS_DESCSEQ32", + "R_ARM_THM_XPC22", + "R_ARM_TLS_CALL", + "R_ARM_TLS_DESCSEQ", + "R_ARM_TLS_DTPMOD32", + "R_ARM_TLS_DTPOFF32", + "R_ARM_TLS_GD32", + "R_ARM_TLS_GOTDESC", + "R_ARM_TLS_IE12GP", + "R_ARM_TLS_IE32", + "R_ARM_TLS_LDM32", + "R_ARM_TLS_LDO12", + "R_ARM_TLS_LDO32", + "R_ARM_TLS_LE12", + "R_ARM_TLS_LE32", + "R_ARM_TLS_TPOFF32", + "R_ARM_V4BX", + "R_ARM_XPC25", + "R_INFO", + "R_INFO32", + "R_LARCH", + "R_LARCH_32", + "R_LARCH_32_PCREL", + "R_LARCH_64", + "R_LARCH_ABS64_HI12", + "R_LARCH_ABS64_LO20", + "R_LARCH_ABS_HI20", + "R_LARCH_ABS_LO12", + "R_LARCH_ADD16", + "R_LARCH_ADD24", + "R_LARCH_ADD32", + "R_LARCH_ADD64", + "R_LARCH_ADD8", + "R_LARCH_B16", + "R_LARCH_B21", + "R_LARCH_B26", + "R_LARCH_COPY", + "R_LARCH_GNU_VTENTRY", + "R_LARCH_GNU_VTINHERIT", + "R_LARCH_GOT64_HI12", + "R_LARCH_GOT64_LO20", + "R_LARCH_GOT64_PC_HI12", + "R_LARCH_GOT64_PC_LO20", + "R_LARCH_GOT_HI20", + "R_LARCH_GOT_LO12", + "R_LARCH_GOT_PC_HI20", + "R_LARCH_GOT_PC_LO12", + "R_LARCH_IRELATIVE", + "R_LARCH_JUMP_SLOT", + "R_LARCH_MARK_LA", + "R_LARCH_MARK_PCREL", + "R_LARCH_NONE", + "R_LARCH_PCALA64_HI12", + "R_LARCH_PCALA64_LO20", + "R_LARCH_PCALA_HI20", + "R_LARCH_PCALA_LO12", + "R_LARCH_RELATIVE", + "R_LARCH_RELAX", + "R_LARCH_SOP_ADD", + "R_LARCH_SOP_AND", + "R_LARCH_SOP_ASSERT", + "R_LARCH_SOP_IF_ELSE", + "R_LARCH_SOP_NOT", + "R_LARCH_SOP_POP_32_S_0_10_10_16_S2", + "R_LARCH_SOP_POP_32_S_0_5_10_16_S2", + "R_LARCH_SOP_POP_32_S_10_12", + "R_LARCH_SOP_POP_32_S_10_16", + "R_LARCH_SOP_POP_32_S_10_16_S2", + "R_LARCH_SOP_POP_32_S_10_5", + "R_LARCH_SOP_POP_32_S_5_20", + "R_LARCH_SOP_POP_32_U", + "R_LARCH_SOP_POP_32_U_10_12", + "R_LARCH_SOP_PUSH_ABSOLUTE", + "R_LARCH_SOP_PUSH_DUP", + "R_LARCH_SOP_PUSH_GPREL", + "R_LARCH_SOP_PUSH_PCREL", + "R_LARCH_SOP_PUSH_PLT_PCREL", + "R_LARCH_SOP_PUSH_TLS_GD", + "R_LARCH_SOP_PUSH_TLS_GOT", + "R_LARCH_SOP_PUSH_TLS_TPREL", + "R_LARCH_SOP_SL", + "R_LARCH_SOP_SR", + "R_LARCH_SOP_SUB", + "R_LARCH_SUB16", + "R_LARCH_SUB24", + "R_LARCH_SUB32", + "R_LARCH_SUB64", + "R_LARCH_SUB8", + "R_LARCH_TLS_DTPMOD32", + "R_LARCH_TLS_DTPMOD64", + "R_LARCH_TLS_DTPREL32", + "R_LARCH_TLS_DTPREL64", + "R_LARCH_TLS_GD_HI20", + "R_LARCH_TLS_GD_PC_HI20", + "R_LARCH_TLS_IE64_HI12", + "R_LARCH_TLS_IE64_LO20", + "R_LARCH_TLS_IE64_PC_HI12", + "R_LARCH_TLS_IE64_PC_LO20", + "R_LARCH_TLS_IE_HI20", + "R_LARCH_TLS_IE_LO12", + "R_LARCH_TLS_IE_PC_HI20", + "R_LARCH_TLS_IE_PC_LO12", + "R_LARCH_TLS_LD_HI20", + "R_LARCH_TLS_LD_PC_HI20", + "R_LARCH_TLS_LE64_HI12", + "R_LARCH_TLS_LE64_LO20", + "R_LARCH_TLS_LE_HI20", + "R_LARCH_TLS_LE_LO12", + "R_LARCH_TLS_TPREL32", + "R_LARCH_TLS_TPREL64", + "R_MIPS", + "R_MIPS_16", + "R_MIPS_26", + "R_MIPS_32", + "R_MIPS_64", + "R_MIPS_ADD_IMMEDIATE", + "R_MIPS_CALL16", + "R_MIPS_CALL_HI16", + "R_MIPS_CALL_LO16", + "R_MIPS_DELETE", + "R_MIPS_GOT16", + "R_MIPS_GOT_DISP", + "R_MIPS_GOT_HI16", + "R_MIPS_GOT_LO16", + "R_MIPS_GOT_OFST", + "R_MIPS_GOT_PAGE", + "R_MIPS_GPREL16", + "R_MIPS_GPREL32", + "R_MIPS_HI16", + "R_MIPS_HIGHER", + "R_MIPS_HIGHEST", + "R_MIPS_INSERT_A", + "R_MIPS_INSERT_B", + "R_MIPS_JALR", + "R_MIPS_LITERAL", + "R_MIPS_LO16", + "R_MIPS_NONE", + "R_MIPS_PC16", + "R_MIPS_PJUMP", + "R_MIPS_REL16", + "R_MIPS_REL32", + "R_MIPS_RELGOT", + "R_MIPS_SCN_DISP", + "R_MIPS_SHIFT5", + "R_MIPS_SHIFT6", + "R_MIPS_SUB", + "R_MIPS_TLS_DTPMOD32", + "R_MIPS_TLS_DTPMOD64", + "R_MIPS_TLS_DTPREL32", + "R_MIPS_TLS_DTPREL64", + "R_MIPS_TLS_DTPREL_HI16", + "R_MIPS_TLS_DTPREL_LO16", + "R_MIPS_TLS_GD", + "R_MIPS_TLS_GOTTPREL", + "R_MIPS_TLS_LDM", + "R_MIPS_TLS_TPREL32", + "R_MIPS_TLS_TPREL64", + "R_MIPS_TLS_TPREL_HI16", + "R_MIPS_TLS_TPREL_LO16", + "R_PPC", + "R_PPC64", + "R_PPC64_ADDR14", + "R_PPC64_ADDR14_BRNTAKEN", + "R_PPC64_ADDR14_BRTAKEN", + "R_PPC64_ADDR16", + "R_PPC64_ADDR16_DS", + "R_PPC64_ADDR16_HA", + "R_PPC64_ADDR16_HI", + "R_PPC64_ADDR16_HIGH", + "R_PPC64_ADDR16_HIGHA", + "R_PPC64_ADDR16_HIGHER", + "R_PPC64_ADDR16_HIGHER34", + "R_PPC64_ADDR16_HIGHERA", + "R_PPC64_ADDR16_HIGHERA34", + "R_PPC64_ADDR16_HIGHEST", + "R_PPC64_ADDR16_HIGHEST34", + "R_PPC64_ADDR16_HIGHESTA", + "R_PPC64_ADDR16_HIGHESTA34", + "R_PPC64_ADDR16_LO", + "R_PPC64_ADDR16_LO_DS", + "R_PPC64_ADDR24", + "R_PPC64_ADDR32", + "R_PPC64_ADDR64", + "R_PPC64_ADDR64_LOCAL", + "R_PPC64_COPY", + "R_PPC64_D28", + "R_PPC64_D34", + "R_PPC64_D34_HA30", + "R_PPC64_D34_HI30", + "R_PPC64_D34_LO", + "R_PPC64_DTPMOD64", + "R_PPC64_DTPREL16", + "R_PPC64_DTPREL16_DS", + "R_PPC64_DTPREL16_HA", + "R_PPC64_DTPREL16_HI", + "R_PPC64_DTPREL16_HIGH", + "R_PPC64_DTPREL16_HIGHA", + "R_PPC64_DTPREL16_HIGHER", + "R_PPC64_DTPREL16_HIGHERA", + "R_PPC64_DTPREL16_HIGHEST", + "R_PPC64_DTPREL16_HIGHESTA", + "R_PPC64_DTPREL16_LO", + "R_PPC64_DTPREL16_LO_DS", + "R_PPC64_DTPREL34", + "R_PPC64_DTPREL64", + "R_PPC64_ENTRY", + "R_PPC64_GLOB_DAT", + "R_PPC64_GNU_VTENTRY", + "R_PPC64_GNU_VTINHERIT", + "R_PPC64_GOT16", + "R_PPC64_GOT16_DS", + "R_PPC64_GOT16_HA", + "R_PPC64_GOT16_HI", + "R_PPC64_GOT16_LO", + "R_PPC64_GOT16_LO_DS", + "R_PPC64_GOT_DTPREL16_DS", + "R_PPC64_GOT_DTPREL16_HA", + "R_PPC64_GOT_DTPREL16_HI", + "R_PPC64_GOT_DTPREL16_LO_DS", + "R_PPC64_GOT_DTPREL_PCREL34", + "R_PPC64_GOT_PCREL34", + "R_PPC64_GOT_TLSGD16", + "R_PPC64_GOT_TLSGD16_HA", + "R_PPC64_GOT_TLSGD16_HI", + "R_PPC64_GOT_TLSGD16_LO", + "R_PPC64_GOT_TLSGD_PCREL34", + "R_PPC64_GOT_TLSLD16", + "R_PPC64_GOT_TLSLD16_HA", + "R_PPC64_GOT_TLSLD16_HI", + "R_PPC64_GOT_TLSLD16_LO", + "R_PPC64_GOT_TLSLD_PCREL34", + "R_PPC64_GOT_TPREL16_DS", + "R_PPC64_GOT_TPREL16_HA", + "R_PPC64_GOT_TPREL16_HI", + "R_PPC64_GOT_TPREL16_LO_DS", + "R_PPC64_GOT_TPREL_PCREL34", + "R_PPC64_IRELATIVE", + "R_PPC64_JMP_IREL", + "R_PPC64_JMP_SLOT", + "R_PPC64_NONE", + "R_PPC64_PCREL28", + "R_PPC64_PCREL34", + "R_PPC64_PCREL_OPT", + "R_PPC64_PLT16_HA", + "R_PPC64_PLT16_HI", + "R_PPC64_PLT16_LO", + "R_PPC64_PLT16_LO_DS", + "R_PPC64_PLT32", + "R_PPC64_PLT64", + "R_PPC64_PLTCALL", + "R_PPC64_PLTCALL_NOTOC", + "R_PPC64_PLTGOT16", + "R_PPC64_PLTGOT16_DS", + "R_PPC64_PLTGOT16_HA", + "R_PPC64_PLTGOT16_HI", + "R_PPC64_PLTGOT16_LO", + "R_PPC64_PLTGOT_LO_DS", + "R_PPC64_PLTREL32", + "R_PPC64_PLTREL64", + "R_PPC64_PLTSEQ", + "R_PPC64_PLTSEQ_NOTOC", + "R_PPC64_PLT_PCREL34", + "R_PPC64_PLT_PCREL34_NOTOC", + "R_PPC64_REL14", + "R_PPC64_REL14_BRNTAKEN", + "R_PPC64_REL14_BRTAKEN", + "R_PPC64_REL16", + "R_PPC64_REL16DX_HA", + "R_PPC64_REL16_HA", + "R_PPC64_REL16_HI", + "R_PPC64_REL16_HIGH", + "R_PPC64_REL16_HIGHA", + "R_PPC64_REL16_HIGHER", + "R_PPC64_REL16_HIGHER34", + "R_PPC64_REL16_HIGHERA", + "R_PPC64_REL16_HIGHERA34", + "R_PPC64_REL16_HIGHEST", + "R_PPC64_REL16_HIGHEST34", + "R_PPC64_REL16_HIGHESTA", + "R_PPC64_REL16_HIGHESTA34", + "R_PPC64_REL16_LO", + "R_PPC64_REL24", + "R_PPC64_REL24_NOTOC", + "R_PPC64_REL24_P9NOTOC", + "R_PPC64_REL30", + "R_PPC64_REL32", + "R_PPC64_REL64", + "R_PPC64_RELATIVE", + "R_PPC64_SECTOFF", + "R_PPC64_SECTOFF_DS", + "R_PPC64_SECTOFF_HA", + "R_PPC64_SECTOFF_HI", + "R_PPC64_SECTOFF_LO", + "R_PPC64_SECTOFF_LO_DS", + "R_PPC64_TLS", + "R_PPC64_TLSGD", + "R_PPC64_TLSLD", + "R_PPC64_TOC", + "R_PPC64_TOC16", + "R_PPC64_TOC16_DS", + "R_PPC64_TOC16_HA", + "R_PPC64_TOC16_HI", + "R_PPC64_TOC16_LO", + "R_PPC64_TOC16_LO_DS", + "R_PPC64_TOCSAVE", + "R_PPC64_TPREL16", + "R_PPC64_TPREL16_DS", + "R_PPC64_TPREL16_HA", + "R_PPC64_TPREL16_HI", + "R_PPC64_TPREL16_HIGH", + "R_PPC64_TPREL16_HIGHA", + "R_PPC64_TPREL16_HIGHER", + "R_PPC64_TPREL16_HIGHERA", + "R_PPC64_TPREL16_HIGHEST", + "R_PPC64_TPREL16_HIGHESTA", + "R_PPC64_TPREL16_LO", + "R_PPC64_TPREL16_LO_DS", + "R_PPC64_TPREL34", + "R_PPC64_TPREL64", + "R_PPC64_UADDR16", + "R_PPC64_UADDR32", + "R_PPC64_UADDR64", + "R_PPC_ADDR14", + "R_PPC_ADDR14_BRNTAKEN", + "R_PPC_ADDR14_BRTAKEN", + "R_PPC_ADDR16", + "R_PPC_ADDR16_HA", + "R_PPC_ADDR16_HI", + "R_PPC_ADDR16_LO", + "R_PPC_ADDR24", + "R_PPC_ADDR32", + "R_PPC_COPY", + "R_PPC_DTPMOD32", + "R_PPC_DTPREL16", + "R_PPC_DTPREL16_HA", + "R_PPC_DTPREL16_HI", + "R_PPC_DTPREL16_LO", + "R_PPC_DTPREL32", + "R_PPC_EMB_BIT_FLD", + "R_PPC_EMB_MRKREF", + "R_PPC_EMB_NADDR16", + "R_PPC_EMB_NADDR16_HA", + "R_PPC_EMB_NADDR16_HI", + "R_PPC_EMB_NADDR16_LO", + "R_PPC_EMB_NADDR32", + "R_PPC_EMB_RELSDA", + "R_PPC_EMB_RELSEC16", + "R_PPC_EMB_RELST_HA", + "R_PPC_EMB_RELST_HI", + "R_PPC_EMB_RELST_LO", + "R_PPC_EMB_SDA21", + "R_PPC_EMB_SDA2I16", + "R_PPC_EMB_SDA2REL", + "R_PPC_EMB_SDAI16", + "R_PPC_GLOB_DAT", + "R_PPC_GOT16", + "R_PPC_GOT16_HA", + "R_PPC_GOT16_HI", + "R_PPC_GOT16_LO", + "R_PPC_GOT_TLSGD16", + "R_PPC_GOT_TLSGD16_HA", + "R_PPC_GOT_TLSGD16_HI", + "R_PPC_GOT_TLSGD16_LO", + "R_PPC_GOT_TLSLD16", + "R_PPC_GOT_TLSLD16_HA", + "R_PPC_GOT_TLSLD16_HI", + "R_PPC_GOT_TLSLD16_LO", + "R_PPC_GOT_TPREL16", + "R_PPC_GOT_TPREL16_HA", + "R_PPC_GOT_TPREL16_HI", + "R_PPC_GOT_TPREL16_LO", + "R_PPC_JMP_SLOT", + "R_PPC_LOCAL24PC", + "R_PPC_NONE", + "R_PPC_PLT16_HA", + "R_PPC_PLT16_HI", + "R_PPC_PLT16_LO", + "R_PPC_PLT32", + "R_PPC_PLTREL24", + "R_PPC_PLTREL32", + "R_PPC_REL14", + "R_PPC_REL14_BRNTAKEN", + "R_PPC_REL14_BRTAKEN", + "R_PPC_REL24", + "R_PPC_REL32", + "R_PPC_RELATIVE", + "R_PPC_SDAREL16", + "R_PPC_SECTOFF", + "R_PPC_SECTOFF_HA", + "R_PPC_SECTOFF_HI", + "R_PPC_SECTOFF_LO", + "R_PPC_TLS", + "R_PPC_TPREL16", + "R_PPC_TPREL16_HA", + "R_PPC_TPREL16_HI", + "R_PPC_TPREL16_LO", + "R_PPC_TPREL32", + "R_PPC_UADDR16", + "R_PPC_UADDR32", + "R_RISCV", + "R_RISCV_32", + "R_RISCV_32_PCREL", + "R_RISCV_64", + "R_RISCV_ADD16", + "R_RISCV_ADD32", + "R_RISCV_ADD64", + "R_RISCV_ADD8", + "R_RISCV_ALIGN", + "R_RISCV_BRANCH", + "R_RISCV_CALL", + "R_RISCV_CALL_PLT", + "R_RISCV_COPY", + "R_RISCV_GNU_VTENTRY", + "R_RISCV_GNU_VTINHERIT", + "R_RISCV_GOT_HI20", + "R_RISCV_GPREL_I", + "R_RISCV_GPREL_S", + "R_RISCV_HI20", + "R_RISCV_JAL", + "R_RISCV_JUMP_SLOT", + "R_RISCV_LO12_I", + "R_RISCV_LO12_S", + "R_RISCV_NONE", + "R_RISCV_PCREL_HI20", + "R_RISCV_PCREL_LO12_I", + "R_RISCV_PCREL_LO12_S", + "R_RISCV_RELATIVE", + "R_RISCV_RELAX", + "R_RISCV_RVC_BRANCH", + "R_RISCV_RVC_JUMP", + "R_RISCV_RVC_LUI", + "R_RISCV_SET16", + "R_RISCV_SET32", + "R_RISCV_SET6", + "R_RISCV_SET8", + "R_RISCV_SUB16", + "R_RISCV_SUB32", + "R_RISCV_SUB6", + "R_RISCV_SUB64", + "R_RISCV_SUB8", + "R_RISCV_TLS_DTPMOD32", + "R_RISCV_TLS_DTPMOD64", + "R_RISCV_TLS_DTPREL32", + "R_RISCV_TLS_DTPREL64", + "R_RISCV_TLS_GD_HI20", + "R_RISCV_TLS_GOT_HI20", + "R_RISCV_TLS_TPREL32", + "R_RISCV_TLS_TPREL64", + "R_RISCV_TPREL_ADD", + "R_RISCV_TPREL_HI20", + "R_RISCV_TPREL_I", + "R_RISCV_TPREL_LO12_I", + "R_RISCV_TPREL_LO12_S", + "R_RISCV_TPREL_S", + "R_SPARC", + "R_SPARC_10", + "R_SPARC_11", + "R_SPARC_13", + "R_SPARC_16", + "R_SPARC_22", + "R_SPARC_32", + "R_SPARC_5", + "R_SPARC_6", + "R_SPARC_64", + "R_SPARC_7", + "R_SPARC_8", + "R_SPARC_COPY", + "R_SPARC_DISP16", + "R_SPARC_DISP32", + "R_SPARC_DISP64", + "R_SPARC_DISP8", + "R_SPARC_GLOB_DAT", + "R_SPARC_GLOB_JMP", + "R_SPARC_GOT10", + "R_SPARC_GOT13", + "R_SPARC_GOT22", + "R_SPARC_H44", + "R_SPARC_HH22", + "R_SPARC_HI22", + "R_SPARC_HIPLT22", + "R_SPARC_HIX22", + "R_SPARC_HM10", + "R_SPARC_JMP_SLOT", + "R_SPARC_L44", + "R_SPARC_LM22", + "R_SPARC_LO10", + "R_SPARC_LOPLT10", + "R_SPARC_LOX10", + "R_SPARC_M44", + "R_SPARC_NONE", + "R_SPARC_OLO10", + "R_SPARC_PC10", + "R_SPARC_PC22", + "R_SPARC_PCPLT10", + "R_SPARC_PCPLT22", + "R_SPARC_PCPLT32", + "R_SPARC_PC_HH22", + "R_SPARC_PC_HM10", + "R_SPARC_PC_LM22", + "R_SPARC_PLT32", + "R_SPARC_PLT64", + "R_SPARC_REGISTER", + "R_SPARC_RELATIVE", + "R_SPARC_UA16", + "R_SPARC_UA32", + "R_SPARC_UA64", + "R_SPARC_WDISP16", + "R_SPARC_WDISP19", + "R_SPARC_WDISP22", + "R_SPARC_WDISP30", + "R_SPARC_WPLT30", + "R_SYM32", + "R_SYM64", + "R_TYPE32", + "R_TYPE64", + "R_X86_64", + "R_X86_64_16", + "R_X86_64_32", + "R_X86_64_32S", + "R_X86_64_64", + "R_X86_64_8", + "R_X86_64_COPY", + "R_X86_64_DTPMOD64", + "R_X86_64_DTPOFF32", + "R_X86_64_DTPOFF64", + "R_X86_64_GLOB_DAT", + "R_X86_64_GOT32", + "R_X86_64_GOT64", + "R_X86_64_GOTOFF64", + "R_X86_64_GOTPC32", + "R_X86_64_GOTPC32_TLSDESC", + "R_X86_64_GOTPC64", + "R_X86_64_GOTPCREL", + "R_X86_64_GOTPCREL64", + "R_X86_64_GOTPCRELX", + "R_X86_64_GOTPLT64", + "R_X86_64_GOTTPOFF", + "R_X86_64_IRELATIVE", + "R_X86_64_JMP_SLOT", + "R_X86_64_NONE", + "R_X86_64_PC16", + "R_X86_64_PC32", + "R_X86_64_PC32_BND", + "R_X86_64_PC64", + "R_X86_64_PC8", + "R_X86_64_PLT32", + "R_X86_64_PLT32_BND", + "R_X86_64_PLTOFF64", + "R_X86_64_RELATIVE", + "R_X86_64_RELATIVE64", + "R_X86_64_REX_GOTPCRELX", + "R_X86_64_SIZE32", + "R_X86_64_SIZE64", + "R_X86_64_TLSDESC", + "R_X86_64_TLSDESC_CALL", + "R_X86_64_TLSGD", + "R_X86_64_TLSLD", + "R_X86_64_TPOFF32", + "R_X86_64_TPOFF64", + "Rel32", + "Rel64", + "Rela32", + "Rela64", + "SHF_ALLOC", + "SHF_COMPRESSED", + "SHF_EXECINSTR", + "SHF_GROUP", + "SHF_INFO_LINK", + "SHF_LINK_ORDER", + "SHF_MASKOS", + "SHF_MASKPROC", + "SHF_MERGE", + "SHF_OS_NONCONFORMING", + "SHF_STRINGS", + "SHF_TLS", + "SHF_WRITE", + "SHN_ABS", + "SHN_COMMON", + "SHN_HIOS", + "SHN_HIPROC", + "SHN_HIRESERVE", + "SHN_LOOS", + "SHN_LOPROC", + "SHN_LORESERVE", + "SHN_UNDEF", + "SHN_XINDEX", + "SHT_DYNAMIC", + "SHT_DYNSYM", + "SHT_FINI_ARRAY", + "SHT_GNU_ATTRIBUTES", + "SHT_GNU_HASH", + "SHT_GNU_LIBLIST", + "SHT_GNU_VERDEF", + "SHT_GNU_VERNEED", + "SHT_GNU_VERSYM", + "SHT_GROUP", + "SHT_HASH", + "SHT_HIOS", + "SHT_HIPROC", + "SHT_HIUSER", + "SHT_INIT_ARRAY", + "SHT_LOOS", + "SHT_LOPROC", + "SHT_LOUSER", + "SHT_MIPS_ABIFLAGS", + "SHT_NOBITS", + "SHT_NOTE", + "SHT_NULL", + "SHT_PREINIT_ARRAY", + "SHT_PROGBITS", + "SHT_REL", + "SHT_RELA", + "SHT_SHLIB", + "SHT_STRTAB", + "SHT_SYMTAB", + "SHT_SYMTAB_SHNDX", + "STB_GLOBAL", + "STB_HIOS", + "STB_HIPROC", + "STB_LOCAL", + "STB_LOOS", + "STB_LOPROC", + "STB_WEAK", + "STT_COMMON", + "STT_FILE", + "STT_FUNC", + "STT_HIOS", + "STT_HIPROC", + "STT_LOOS", + "STT_LOPROC", + "STT_NOTYPE", + "STT_OBJECT", + "STT_SECTION", + "STT_TLS", + "STV_DEFAULT", + "STV_HIDDEN", + "STV_INTERNAL", + "STV_PROTECTED", + "ST_BIND", + "ST_INFO", + "ST_TYPE", + "ST_VISIBILITY", + "Section", + "Section32", + "Section64", + "SectionFlag", + "SectionHeader", + "SectionIndex", + "SectionType", + "Sym32", + "Sym32Size", + "Sym64", + "Sym64Size", + "SymBind", + "SymType", + "SymVis", + "Symbol", + "Type", + "Version", + }, + "debug/gosym": { + "DecodingError", + "Func", + "LineTable", + "NewLineTable", + "NewTable", + "Obj", + "Sym", + "Table", + "UnknownFileError", + "UnknownLineError", + }, + "debug/macho": { + "ARM64_RELOC_ADDEND", + "ARM64_RELOC_BRANCH26", + "ARM64_RELOC_GOT_LOAD_PAGE21", + "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + "ARM64_RELOC_PAGE21", + "ARM64_RELOC_PAGEOFF12", + "ARM64_RELOC_POINTER_TO_GOT", + "ARM64_RELOC_SUBTRACTOR", + "ARM64_RELOC_TLVP_LOAD_PAGE21", + "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", + "ARM64_RELOC_UNSIGNED", + "ARM_RELOC_BR24", + "ARM_RELOC_HALF", + "ARM_RELOC_HALF_SECTDIFF", + "ARM_RELOC_LOCAL_SECTDIFF", + "ARM_RELOC_PAIR", + "ARM_RELOC_PB_LA_PTR", + "ARM_RELOC_SECTDIFF", + "ARM_RELOC_VANILLA", + "ARM_THUMB_32BIT_BRANCH", + "ARM_THUMB_RELOC_BR22", + "Cpu", + "Cpu386", + "CpuAmd64", + "CpuArm", + "CpuArm64", + "CpuPpc", + "CpuPpc64", + "Dylib", + "DylibCmd", + "Dysymtab", + "DysymtabCmd", + "ErrNotFat", + "FatArch", + "FatArchHeader", + "FatFile", + "File", + "FileHeader", + "FlagAllModsBound", + "FlagAllowStackExecution", + "FlagAppExtensionSafe", + "FlagBindAtLoad", + "FlagBindsToWeak", + "FlagCanonical", + "FlagDeadStrippableDylib", + "FlagDyldLink", + "FlagForceFlat", + "FlagHasTLVDescriptors", + "FlagIncrLink", + "FlagLazyInit", + "FlagNoFixPrebinding", + "FlagNoHeapExecution", + "FlagNoMultiDefs", + "FlagNoReexportedDylibs", + "FlagNoUndefs", + "FlagPIE", + "FlagPrebindable", + "FlagPrebound", + "FlagRootSafe", + "FlagSetuidSafe", + "FlagSplitSegs", + "FlagSubsectionsViaSymbols", + "FlagTwoLevel", + "FlagWeakDefines", + "FormatError", + "GENERIC_RELOC_LOCAL_SECTDIFF", + "GENERIC_RELOC_PAIR", + "GENERIC_RELOC_PB_LA_PTR", + "GENERIC_RELOC_SECTDIFF", + "GENERIC_RELOC_TLV", + "GENERIC_RELOC_VANILLA", + "Load", + "LoadBytes", + "LoadCmd", + "LoadCmdDylib", + "LoadCmdDylinker", + "LoadCmdDysymtab", + "LoadCmdRpath", + "LoadCmdSegment", + "LoadCmdSegment64", + "LoadCmdSymtab", + "LoadCmdThread", + "LoadCmdUnixThread", + "Magic32", + "Magic64", + "MagicFat", + "NewFatFile", + "NewFile", + "Nlist32", + "Nlist64", + "Open", + "OpenFat", + "Regs386", + "RegsAMD64", + "Reloc", + "RelocTypeARM", + "RelocTypeARM64", + "RelocTypeGeneric", + "RelocTypeX86_64", + "Rpath", + "RpathCmd", + "Section", + "Section32", + "Section64", + "SectionHeader", + "Segment", + "Segment32", + "Segment64", + "SegmentHeader", + "Symbol", + "Symtab", + "SymtabCmd", + "Thread", + "Type", + "TypeBundle", + "TypeDylib", + "TypeExec", + "TypeObj", + "X86_64_RELOC_BRANCH", + "X86_64_RELOC_GOT", + "X86_64_RELOC_GOT_LOAD", + "X86_64_RELOC_SIGNED", + "X86_64_RELOC_SIGNED_1", + "X86_64_RELOC_SIGNED_2", + "X86_64_RELOC_SIGNED_4", + "X86_64_RELOC_SUBTRACTOR", + "X86_64_RELOC_TLV", + "X86_64_RELOC_UNSIGNED", + }, + "debug/pe": { + "COFFSymbol", + "COFFSymbolAuxFormat5", + "COFFSymbolSize", + "DataDirectory", + "File", + "FileHeader", + "FormatError", + "IMAGE_COMDAT_SELECT_ANY", + "IMAGE_COMDAT_SELECT_ASSOCIATIVE", + "IMAGE_COMDAT_SELECT_EXACT_MATCH", + "IMAGE_COMDAT_SELECT_LARGEST", + "IMAGE_COMDAT_SELECT_NODUPLICATES", + "IMAGE_COMDAT_SELECT_SAME_SIZE", + "IMAGE_DIRECTORY_ENTRY_ARCHITECTURE", + "IMAGE_DIRECTORY_ENTRY_BASERELOC", + "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", + "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", + "IMAGE_DIRECTORY_ENTRY_DEBUG", + "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", + "IMAGE_DIRECTORY_ENTRY_EXCEPTION", + "IMAGE_DIRECTORY_ENTRY_EXPORT", + "IMAGE_DIRECTORY_ENTRY_GLOBALPTR", + "IMAGE_DIRECTORY_ENTRY_IAT", + "IMAGE_DIRECTORY_ENTRY_IMPORT", + "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", + "IMAGE_DIRECTORY_ENTRY_RESOURCE", + "IMAGE_DIRECTORY_ENTRY_SECURITY", + "IMAGE_DIRECTORY_ENTRY_TLS", + "IMAGE_DLLCHARACTERISTICS_APPCONTAINER", + "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", + "IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", + "IMAGE_DLLCHARACTERISTICS_GUARD_CF", + "IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", + "IMAGE_DLLCHARACTERISTICS_NO_BIND", + "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", + "IMAGE_DLLCHARACTERISTICS_NO_SEH", + "IMAGE_DLLCHARACTERISTICS_NX_COMPAT", + "IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", + "IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", + "IMAGE_FILE_32BIT_MACHINE", + "IMAGE_FILE_AGGRESIVE_WS_TRIM", + "IMAGE_FILE_BYTES_REVERSED_HI", + "IMAGE_FILE_BYTES_REVERSED_LO", + "IMAGE_FILE_DEBUG_STRIPPED", + "IMAGE_FILE_DLL", + "IMAGE_FILE_EXECUTABLE_IMAGE", + "IMAGE_FILE_LARGE_ADDRESS_AWARE", + "IMAGE_FILE_LINE_NUMS_STRIPPED", + "IMAGE_FILE_LOCAL_SYMS_STRIPPED", + "IMAGE_FILE_MACHINE_AM33", + "IMAGE_FILE_MACHINE_AMD64", + "IMAGE_FILE_MACHINE_ARM", + "IMAGE_FILE_MACHINE_ARM64", + "IMAGE_FILE_MACHINE_ARMNT", + "IMAGE_FILE_MACHINE_EBC", + "IMAGE_FILE_MACHINE_I386", + "IMAGE_FILE_MACHINE_IA64", + "IMAGE_FILE_MACHINE_LOONGARCH32", + "IMAGE_FILE_MACHINE_LOONGARCH64", + "IMAGE_FILE_MACHINE_M32R", + "IMAGE_FILE_MACHINE_MIPS16", + "IMAGE_FILE_MACHINE_MIPSFPU", + "IMAGE_FILE_MACHINE_MIPSFPU16", + "IMAGE_FILE_MACHINE_POWERPC", + "IMAGE_FILE_MACHINE_POWERPCFP", + "IMAGE_FILE_MACHINE_R4000", + "IMAGE_FILE_MACHINE_RISCV128", + "IMAGE_FILE_MACHINE_RISCV32", + "IMAGE_FILE_MACHINE_RISCV64", + "IMAGE_FILE_MACHINE_SH3", + "IMAGE_FILE_MACHINE_SH3DSP", + "IMAGE_FILE_MACHINE_SH4", + "IMAGE_FILE_MACHINE_SH5", + "IMAGE_FILE_MACHINE_THUMB", + "IMAGE_FILE_MACHINE_UNKNOWN", + "IMAGE_FILE_MACHINE_WCEMIPSV2", + "IMAGE_FILE_NET_RUN_FROM_SWAP", + "IMAGE_FILE_RELOCS_STRIPPED", + "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", + "IMAGE_FILE_SYSTEM", + "IMAGE_FILE_UP_SYSTEM_ONLY", + "IMAGE_SCN_CNT_CODE", + "IMAGE_SCN_CNT_INITIALIZED_DATA", + "IMAGE_SCN_CNT_UNINITIALIZED_DATA", + "IMAGE_SCN_LNK_COMDAT", + "IMAGE_SCN_MEM_DISCARDABLE", + "IMAGE_SCN_MEM_EXECUTE", + "IMAGE_SCN_MEM_READ", + "IMAGE_SCN_MEM_WRITE", + "IMAGE_SUBSYSTEM_EFI_APPLICATION", + "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", + "IMAGE_SUBSYSTEM_EFI_ROM", + "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", + "IMAGE_SUBSYSTEM_NATIVE", + "IMAGE_SUBSYSTEM_NATIVE_WINDOWS", + "IMAGE_SUBSYSTEM_OS2_CUI", + "IMAGE_SUBSYSTEM_POSIX_CUI", + "IMAGE_SUBSYSTEM_UNKNOWN", + "IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", + "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", + "IMAGE_SUBSYSTEM_WINDOWS_CUI", + "IMAGE_SUBSYSTEM_WINDOWS_GUI", + "IMAGE_SUBSYSTEM_XBOX", + "ImportDirectory", + "NewFile", + "Open", + "OptionalHeader32", + "OptionalHeader64", + "Reloc", + "Section", + "SectionHeader", + "SectionHeader32", + "StringTable", + "Symbol", + }, + "debug/plan9obj": { + "ErrNoSymbols", + "File", + "FileHeader", + "Magic386", + "Magic64", + "MagicAMD64", + "MagicARM", + "NewFile", + "Open", + "Section", + "SectionHeader", + "Sym", + }, + "embed": { + "FS", + }, + "encoding": { + "BinaryMarshaler", + "BinaryUnmarshaler", + "TextMarshaler", + "TextUnmarshaler", + }, + "encoding/ascii85": { + "CorruptInputError", + "Decode", + "Encode", + "MaxEncodedLen", + "NewDecoder", + "NewEncoder", + }, + "encoding/asn1": { + "BitString", + "ClassApplication", + "ClassContextSpecific", + "ClassPrivate", + "ClassUniversal", + "Enumerated", + "Flag", + "Marshal", + "MarshalWithParams", + "NullBytes", + "NullRawValue", + "ObjectIdentifier", + "RawContent", + "RawValue", + "StructuralError", + "SyntaxError", + "TagBMPString", + "TagBitString", + "TagBoolean", + "TagEnum", + "TagGeneralString", + "TagGeneralizedTime", + "TagIA5String", + "TagInteger", + "TagNull", + "TagNumericString", + "TagOID", + "TagOctetString", + "TagPrintableString", + "TagSequence", + "TagSet", + "TagT61String", + "TagUTCTime", + "TagUTF8String", + "Unmarshal", + "UnmarshalWithParams", + }, + "encoding/base32": { + "CorruptInputError", + "Encoding", + "HexEncoding", + "NewDecoder", + "NewEncoder", + "NewEncoding", + "NoPadding", + "StdEncoding", + "StdPadding", + }, + "encoding/base64": { + "CorruptInputError", + "Encoding", + "NewDecoder", + "NewEncoder", + "NewEncoding", + "NoPadding", + "RawStdEncoding", + "RawURLEncoding", + "StdEncoding", + "StdPadding", + "URLEncoding", + }, + "encoding/binary": { + "AppendByteOrder", + "AppendUvarint", + "AppendVarint", + "BigEndian", + "ByteOrder", + "LittleEndian", + "MaxVarintLen16", + "MaxVarintLen32", + "MaxVarintLen64", + "NativeEndian", + "PutUvarint", + "PutVarint", + "Read", + "ReadUvarint", + "ReadVarint", + "Size", + "Uvarint", + "Varint", + "Write", + }, + "encoding/csv": { + "ErrBareQuote", + "ErrFieldCount", + "ErrQuote", + "ErrTrailingComma", + "NewReader", + "NewWriter", + "ParseError", + "Reader", + "Writer", + }, + "encoding/gob": { + "CommonType", + "Decoder", + "Encoder", + "GobDecoder", + "GobEncoder", + "NewDecoder", + "NewEncoder", + "Register", + "RegisterName", + }, + "encoding/hex": { + "Decode", + "DecodeString", + "DecodedLen", + "Dump", + "Dumper", + "Encode", + "EncodeToString", + "EncodedLen", + "ErrLength", + "InvalidByteError", + "NewDecoder", + "NewEncoder", + }, + "encoding/json": { + "Compact", + "Decoder", + "Delim", + "Encoder", + "HTMLEscape", + "Indent", + "InvalidUTF8Error", + "InvalidUnmarshalError", + "Marshal", + "MarshalIndent", + "Marshaler", + "MarshalerError", + "NewDecoder", + "NewEncoder", + "Number", + "RawMessage", + "SyntaxError", + "Token", + "Unmarshal", + "UnmarshalFieldError", + "UnmarshalTypeError", + "Unmarshaler", + "UnsupportedTypeError", + "UnsupportedValueError", + "Valid", + }, + "encoding/pem": { + "Block", + "Decode", + "Encode", + "EncodeToMemory", + }, + "encoding/xml": { + "Attr", + "CharData", + "Comment", + "CopyToken", + "Decoder", + "Directive", + "Encoder", + "EndElement", + "Escape", + "EscapeText", + "HTMLAutoClose", + "HTMLEntity", + "Header", + "Marshal", + "MarshalIndent", + "Marshaler", + "MarshalerAttr", + "Name", + "NewDecoder", + "NewEncoder", + "NewTokenDecoder", + "ProcInst", + "StartElement", + "SyntaxError", + "TagPathError", + "Token", + "TokenReader", + "Unmarshal", + "UnmarshalError", + "Unmarshaler", + "UnmarshalerAttr", + "UnsupportedTypeError", + }, + "errors": { + "As", + "ErrUnsupported", + "Is", + "Join", + "New", + "Unwrap", + }, + "expvar": { + "Do", + "Float", + "Func", + "Get", + "Handler", + "Int", + "KeyValue", + "Map", + "NewFloat", + "NewInt", + "NewMap", + "NewString", + "Publish", + "String", + "Var", + }, + "flag": { + "Arg", + "Args", + "Bool", + "BoolFunc", + "BoolVar", + "CommandLine", + "ContinueOnError", + "Duration", + "DurationVar", + "ErrHelp", + "ErrorHandling", + "ExitOnError", + "Flag", + "FlagSet", + "Float64", + "Float64Var", + "Func", + "Getter", + "Int", + "Int64", + "Int64Var", + "IntVar", + "Lookup", + "NArg", + "NFlag", + "NewFlagSet", + "PanicOnError", + "Parse", + "Parsed", + "PrintDefaults", + "Set", + "String", + "StringVar", + "TextVar", + "Uint", + "Uint64", + "Uint64Var", + "UintVar", + "UnquoteUsage", + "Usage", + "Value", + "Var", + "Visit", + "VisitAll", + }, + "fmt": { + "Append", + "Appendf", + "Appendln", + "Errorf", + "FormatString", + "Formatter", + "Fprint", + "Fprintf", + "Fprintln", + "Fscan", + "Fscanf", + "Fscanln", + "GoStringer", + "Print", + "Printf", + "Println", + "Scan", + "ScanState", + "Scanf", + "Scanln", + "Scanner", + "Sprint", + "Sprintf", + "Sprintln", + "Sscan", + "Sscanf", + "Sscanln", + "State", + "Stringer", + }, + "go/ast": { + "ArrayType", + "AssignStmt", + "Bad", + "BadDecl", + "BadExpr", + "BadStmt", + "BasicLit", + "BinaryExpr", + "BlockStmt", + "BranchStmt", + "CallExpr", + "CaseClause", + "ChanDir", + "ChanType", + "CommClause", + "Comment", + "CommentGroup", + "CommentMap", + "CompositeLit", + "Con", + "Decl", + "DeclStmt", + "DeferStmt", + "Ellipsis", + "EmptyStmt", + "Expr", + "ExprStmt", + "Field", + "FieldFilter", + "FieldList", + "File", + "FileExports", + "Filter", + "FilterDecl", + "FilterFile", + "FilterFuncDuplicates", + "FilterImportDuplicates", + "FilterPackage", + "FilterUnassociatedComments", + "ForStmt", + "Fprint", + "Fun", + "FuncDecl", + "FuncLit", + "FuncType", + "GenDecl", + "GoStmt", + "Ident", + "IfStmt", + "ImportSpec", + "Importer", + "IncDecStmt", + "IndexExpr", + "IndexListExpr", + "Inspect", + "InterfaceType", + "IsExported", + "IsGenerated", + "KeyValueExpr", + "LabeledStmt", + "Lbl", + "MapType", + "MergeMode", + "MergePackageFiles", + "NewCommentMap", + "NewIdent", + "NewObj", + "NewPackage", + "NewScope", + "Node", + "NotNilFilter", + "ObjKind", + "Object", + "Package", + "PackageExports", + "ParenExpr", + "Pkg", + "Print", + "RECV", + "RangeStmt", + "ReturnStmt", + "SEND", + "Scope", + "SelectStmt", + "SelectorExpr", + "SendStmt", + "SliceExpr", + "SortImports", + "Spec", + "StarExpr", + "Stmt", + "StructType", + "SwitchStmt", + "Typ", + "TypeAssertExpr", + "TypeSpec", + "TypeSwitchStmt", + "UnaryExpr", + "ValueSpec", + "Var", + "Visitor", + "Walk", + }, + "go/build": { + "AllowBinary", + "ArchChar", + "Context", + "Default", + "Directive", + "FindOnly", + "IgnoreVendor", + "Import", + "ImportComment", + "ImportDir", + "ImportMode", + "IsLocalImport", + "MultiplePackageError", + "NoGoError", + "Package", + "ToolDir", + }, + "go/build/constraint": { + "AndExpr", + "Expr", + "GoVersion", + "IsGoBuild", + "IsPlusBuild", + "NotExpr", + "OrExpr", + "Parse", + "PlusBuildLines", + "SyntaxError", + "TagExpr", + }, + "go/constant": { + "BinaryOp", + "BitLen", + "Bool", + "BoolVal", + "Bytes", + "Compare", + "Complex", + "Denom", + "Float", + "Float32Val", + "Float64Val", + "Imag", + "Int", + "Int64Val", + "Kind", + "Make", + "MakeBool", + "MakeFloat64", + "MakeFromBytes", + "MakeFromLiteral", + "MakeImag", + "MakeInt64", + "MakeString", + "MakeUint64", + "MakeUnknown", + "Num", + "Real", + "Shift", + "Sign", + "String", + "StringVal", + "ToComplex", + "ToFloat", + "ToInt", + "Uint64Val", + "UnaryOp", + "Unknown", + "Val", + "Value", + }, + "go/doc": { + "AllDecls", + "AllMethods", + "Example", + "Examples", + "Filter", + "Func", + "IllegalPrefixes", + "IsPredeclared", + "Mode", + "New", + "NewFromFiles", + "Note", + "Package", + "PreserveAST", + "Synopsis", + "ToHTML", + "ToText", + "Type", + "Value", + }, + "go/doc/comment": { + "Block", + "Code", + "DefaultLookupPackage", + "Doc", + "DocLink", + "Heading", + "Italic", + "Link", + "LinkDef", + "List", + "ListItem", + "Paragraph", + "Parser", + "Plain", + "Printer", + "Text", + }, + "go/format": { + "Node", + "Source", + }, + "go/importer": { + "Default", + "For", + "ForCompiler", + "Lookup", + }, + "go/parser": { + "AllErrors", + "DeclarationErrors", + "ImportsOnly", + "Mode", + "PackageClauseOnly", + "ParseComments", + "ParseDir", + "ParseExpr", + "ParseExprFrom", + "ParseFile", + "SkipObjectResolution", + "SpuriousErrors", + "Trace", + }, + "go/printer": { + "CommentedNode", + "Config", + "Fprint", + "Mode", + "RawFormat", + "SourcePos", + "TabIndent", + "UseSpaces", + }, + "go/scanner": { + "Error", + "ErrorHandler", + "ErrorList", + "Mode", + "PrintError", + "ScanComments", + "Scanner", + }, + "go/token": { + "ADD", + "ADD_ASSIGN", + "AND", + "AND_ASSIGN", + "AND_NOT", + "AND_NOT_ASSIGN", + "ARROW", + "ASSIGN", + "BREAK", + "CASE", + "CHAN", + "CHAR", + "COLON", + "COMMA", + "COMMENT", + "CONST", + "CONTINUE", + "DEC", + "DEFAULT", + "DEFER", + "DEFINE", + "ELLIPSIS", + "ELSE", + "EOF", + "EQL", + "FALLTHROUGH", + "FLOAT", + "FOR", + "FUNC", + "File", + "FileSet", + "GEQ", + "GO", + "GOTO", + "GTR", + "HighestPrec", + "IDENT", + "IF", + "ILLEGAL", + "IMAG", + "IMPORT", + "INC", + "INT", + "INTERFACE", + "IsExported", + "IsIdentifier", + "IsKeyword", + "LAND", + "LBRACE", + "LBRACK", + "LEQ", + "LOR", + "LPAREN", + "LSS", + "Lookup", + "LowestPrec", + "MAP", + "MUL", + "MUL_ASSIGN", + "NEQ", + "NOT", + "NewFileSet", + "NoPos", + "OR", + "OR_ASSIGN", + "PACKAGE", + "PERIOD", + "Pos", + "Position", + "QUO", + "QUO_ASSIGN", + "RANGE", + "RBRACE", + "RBRACK", + "REM", + "REM_ASSIGN", + "RETURN", + "RPAREN", + "SELECT", + "SEMICOLON", + "SHL", + "SHL_ASSIGN", + "SHR", + "SHR_ASSIGN", + "STRING", + "STRUCT", + "SUB", + "SUB_ASSIGN", + "SWITCH", + "TILDE", + "TYPE", + "Token", + "UnaryPrec", + "VAR", + "XOR", + "XOR_ASSIGN", + }, + "go/types": { + "ArgumentError", + "Array", + "AssertableTo", + "AssignableTo", + "Basic", + "BasicInfo", + "BasicKind", + "Bool", + "Builtin", + "Byte", + "Chan", + "ChanDir", + "CheckExpr", + "Checker", + "Comparable", + "Complex128", + "Complex64", + "Config", + "Const", + "Context", + "ConvertibleTo", + "DefPredeclaredTestFuncs", + "Default", + "Error", + "Eval", + "ExprString", + "FieldVal", + "Float32", + "Float64", + "Func", + "Id", + "Identical", + "IdenticalIgnoreTags", + "Implements", + "ImportMode", + "Importer", + "ImporterFrom", + "Info", + "Initializer", + "Instance", + "Instantiate", + "Int", + "Int16", + "Int32", + "Int64", + "Int8", + "Interface", + "Invalid", + "IsBoolean", + "IsComplex", + "IsConstType", + "IsFloat", + "IsInteger", + "IsInterface", + "IsNumeric", + "IsOrdered", + "IsString", + "IsUnsigned", + "IsUntyped", + "Label", + "LookupFieldOrMethod", + "Map", + "MethodExpr", + "MethodSet", + "MethodVal", + "MissingMethod", + "Named", + "NewArray", + "NewChan", + "NewChecker", + "NewConst", + "NewContext", + "NewField", + "NewFunc", + "NewInterface", + "NewInterfaceType", + "NewLabel", + "NewMap", + "NewMethodSet", + "NewNamed", + "NewPackage", + "NewParam", + "NewPkgName", + "NewPointer", + "NewScope", + "NewSignature", + "NewSignatureType", + "NewSlice", + "NewStruct", + "NewTerm", + "NewTuple", + "NewTypeName", + "NewTypeParam", + "NewUnion", + "NewVar", + "Nil", + "Object", + "ObjectString", + "Package", + "PkgName", + "Pointer", + "Qualifier", + "RecvOnly", + "RelativeTo", + "Rune", + "Satisfies", + "Scope", + "Selection", + "SelectionKind", + "SelectionString", + "SendOnly", + "SendRecv", + "Signature", + "Sizes", + "SizesFor", + "Slice", + "StdSizes", + "String", + "Struct", + "Term", + "Tuple", + "Typ", + "Type", + "TypeAndValue", + "TypeList", + "TypeName", + "TypeParam", + "TypeParamList", + "TypeString", + "Uint", + "Uint16", + "Uint32", + "Uint64", + "Uint8", + "Uintptr", + "Union", + "Universe", + "Unsafe", + "UnsafePointer", + "UntypedBool", + "UntypedComplex", + "UntypedFloat", + "UntypedInt", + "UntypedNil", + "UntypedRune", + "UntypedString", + "Var", + "WriteExpr", + "WriteSignature", + "WriteType", + }, + "hash": { + "Hash", + "Hash32", + "Hash64", + }, + "hash/adler32": { + "Checksum", + "New", + "Size", + }, + "hash/crc32": { + "Castagnoli", + "Checksum", + "ChecksumIEEE", + "IEEE", + "IEEETable", + "Koopman", + "MakeTable", + "New", + "NewIEEE", + "Size", + "Table", + "Update", + }, + "hash/crc64": { + "Checksum", + "ECMA", + "ISO", + "MakeTable", + "New", + "Size", + "Table", + "Update", + }, + "hash/fnv": { + "New128", + "New128a", + "New32", + "New32a", + "New64", + "New64a", + }, + "hash/maphash": { + "Bytes", + "Hash", + "MakeSeed", + "Seed", + "String", + }, + "html": { + "EscapeString", + "UnescapeString", + }, + "html/template": { + "CSS", + "ErrAmbigContext", + "ErrBadHTML", + "ErrBranchEnd", + "ErrEndContext", + "ErrJSTemplate", + "ErrNoSuchTemplate", + "ErrOutputContext", + "ErrPartialCharset", + "ErrPartialEscape", + "ErrPredefinedEscaper", + "ErrRangeLoopReentry", + "ErrSlashAmbig", + "Error", + "ErrorCode", + "FuncMap", + "HTML", + "HTMLAttr", + "HTMLEscape", + "HTMLEscapeString", + "HTMLEscaper", + "IsTrue", + "JS", + "JSEscape", + "JSEscapeString", + "JSEscaper", + "JSStr", + "Must", + "New", + "OK", + "ParseFS", + "ParseFiles", + "ParseGlob", + "Srcset", + "Template", + "URL", + "URLQueryEscaper", + }, + "image": { + "Alpha", + "Alpha16", + "Black", + "CMYK", + "Config", + "Decode", + "DecodeConfig", + "ErrFormat", + "Gray", + "Gray16", + "Image", + "NRGBA", + "NRGBA64", + "NYCbCrA", + "NewAlpha", + "NewAlpha16", + "NewCMYK", + "NewGray", + "NewGray16", + "NewNRGBA", + "NewNRGBA64", + "NewNYCbCrA", + "NewPaletted", + "NewRGBA", + "NewRGBA64", + "NewUniform", + "NewYCbCr", + "Opaque", + "Paletted", + "PalettedImage", + "Point", + "Pt", + "RGBA", + "RGBA64", + "RGBA64Image", + "Rect", + "Rectangle", + "RegisterFormat", + "Transparent", + "Uniform", + "White", + "YCbCr", + "YCbCrSubsampleRatio", + "YCbCrSubsampleRatio410", + "YCbCrSubsampleRatio411", + "YCbCrSubsampleRatio420", + "YCbCrSubsampleRatio422", + "YCbCrSubsampleRatio440", + "YCbCrSubsampleRatio444", + "ZP", + "ZR", + }, + "image/color": { + "Alpha", + "Alpha16", + "Alpha16Model", + "AlphaModel", + "Black", + "CMYK", + "CMYKModel", + "CMYKToRGB", + "Color", + "Gray", + "Gray16", + "Gray16Model", + "GrayModel", + "Model", + "ModelFunc", + "NRGBA", + "NRGBA64", + "NRGBA64Model", + "NRGBAModel", + "NYCbCrA", + "NYCbCrAModel", + "Opaque", + "Palette", + "RGBA", + "RGBA64", + "RGBA64Model", + "RGBAModel", + "RGBToCMYK", + "RGBToYCbCr", + "Transparent", + "White", + "YCbCr", + "YCbCrModel", + "YCbCrToRGB", + }, + "image/color/palette": { + "Plan9", + "WebSafe", + }, + "image/draw": { + "Draw", + "DrawMask", + "Drawer", + "FloydSteinberg", + "Image", + "Op", + "Over", + "Quantizer", + "RGBA64Image", + "Src", + }, + "image/gif": { + "Decode", + "DecodeAll", + "DecodeConfig", + "DisposalBackground", + "DisposalNone", + "DisposalPrevious", + "Encode", + "EncodeAll", + "GIF", + "Options", + }, + "image/jpeg": { + "Decode", + "DecodeConfig", + "DefaultQuality", + "Encode", + "FormatError", + "Options", + "Reader", + "UnsupportedError", + }, + "image/png": { + "BestCompression", + "BestSpeed", + "CompressionLevel", + "Decode", + "DecodeConfig", + "DefaultCompression", + "Encode", + "Encoder", + "EncoderBuffer", + "EncoderBufferPool", + "FormatError", + "NoCompression", + "UnsupportedError", + }, + "index/suffixarray": { + "Index", + "New", + }, + "io": { + "ByteReader", + "ByteScanner", + "ByteWriter", + "Closer", + "Copy", + "CopyBuffer", + "CopyN", + "Discard", + "EOF", + "ErrClosedPipe", + "ErrNoProgress", + "ErrShortBuffer", + "ErrShortWrite", + "ErrUnexpectedEOF", + "LimitReader", + "LimitedReader", + "MultiReader", + "MultiWriter", + "NewOffsetWriter", + "NewSectionReader", + "NopCloser", + "OffsetWriter", + "Pipe", + "PipeReader", + "PipeWriter", + "ReadAll", + "ReadAtLeast", + "ReadCloser", + "ReadFull", + "ReadSeekCloser", + "ReadSeeker", + "ReadWriteCloser", + "ReadWriteSeeker", + "ReadWriter", + "Reader", + "ReaderAt", + "ReaderFrom", + "RuneReader", + "RuneScanner", + "SectionReader", + "SeekCurrent", + "SeekEnd", + "SeekStart", + "Seeker", + "StringWriter", + "TeeReader", + "WriteCloser", + "WriteSeeker", + "WriteString", + "Writer", + "WriterAt", + "WriterTo", + }, + "io/fs": { + "DirEntry", + "ErrClosed", + "ErrExist", + "ErrInvalid", + "ErrNotExist", + "ErrPermission", + "FS", + "File", + "FileInfo", + "FileInfoToDirEntry", + "FileMode", + "FormatDirEntry", + "FormatFileInfo", + "Glob", + "GlobFS", + "ModeAppend", + "ModeCharDevice", + "ModeDevice", + "ModeDir", + "ModeExclusive", + "ModeIrregular", + "ModeNamedPipe", + "ModePerm", + "ModeSetgid", + "ModeSetuid", + "ModeSocket", + "ModeSticky", + "ModeSymlink", + "ModeTemporary", + "ModeType", + "PathError", + "ReadDir", + "ReadDirFS", + "ReadDirFile", + "ReadFile", + "ReadFileFS", + "SkipAll", + "SkipDir", + "Stat", + "StatFS", + "Sub", + "SubFS", + "ValidPath", + "WalkDir", + "WalkDirFunc", + }, + "io/ioutil": { + "Discard", + "NopCloser", + "ReadAll", + "ReadDir", + "ReadFile", + "TempDir", + "TempFile", + "WriteFile", + }, + "log": { + "Default", + "Fatal", + "Fatalf", + "Fatalln", + "Flags", + "LUTC", + "Ldate", + "Llongfile", + "Lmicroseconds", + "Lmsgprefix", + "Logger", + "Lshortfile", + "LstdFlags", + "Ltime", + "New", + "Output", + "Panic", + "Panicf", + "Panicln", + "Prefix", + "Print", + "Printf", + "Println", + "SetFlags", + "SetOutput", + "SetPrefix", + "Writer", + }, + "log/slog": { + "Any", + "AnyValue", + "Attr", + "Bool", + "BoolValue", + "Debug", + "DebugContext", + "Default", + "Duration", + "DurationValue", + "Error", + "ErrorContext", + "Float64", + "Float64Value", + "Group", + "GroupValue", + "Handler", + "HandlerOptions", + "Info", + "InfoContext", + "Int", + "Int64", + "Int64Value", + "IntValue", + "JSONHandler", + "Kind", + "KindAny", + "KindBool", + "KindDuration", + "KindFloat64", + "KindGroup", + "KindInt64", + "KindLogValuer", + "KindString", + "KindTime", + "KindUint64", + "Level", + "LevelDebug", + "LevelError", + "LevelInfo", + "LevelKey", + "LevelVar", + "LevelWarn", + "Leveler", + "Log", + "LogAttrs", + "LogValuer", + "Logger", + "MessageKey", + "New", + "NewJSONHandler", + "NewLogLogger", + "NewRecord", + "NewTextHandler", + "Record", + "SetDefault", + "Source", + "SourceKey", + "String", + "StringValue", + "TextHandler", + "Time", + "TimeKey", + "TimeValue", + "Uint64", + "Uint64Value", + "Value", + "Warn", + "WarnContext", + "With", + }, + "log/syslog": { + "Dial", + "LOG_ALERT", + "LOG_AUTH", + "LOG_AUTHPRIV", + "LOG_CRIT", + "LOG_CRON", + "LOG_DAEMON", + "LOG_DEBUG", + "LOG_EMERG", + "LOG_ERR", + "LOG_FTP", + "LOG_INFO", + "LOG_KERN", + "LOG_LOCAL0", + "LOG_LOCAL1", + "LOG_LOCAL2", + "LOG_LOCAL3", + "LOG_LOCAL4", + "LOG_LOCAL5", + "LOG_LOCAL6", + "LOG_LOCAL7", + "LOG_LPR", + "LOG_MAIL", + "LOG_NEWS", + "LOG_NOTICE", + "LOG_SYSLOG", + "LOG_USER", + "LOG_UUCP", + "LOG_WARNING", + "New", + "NewLogger", + "Priority", + "Writer", + }, + "maps": { + "Clone", + "Copy", + "DeleteFunc", + "Equal", + "EqualFunc", + }, + "math": { + "Abs", + "Acos", + "Acosh", + "Asin", + "Asinh", + "Atan", + "Atan2", + "Atanh", + "Cbrt", + "Ceil", + "Copysign", + "Cos", + "Cosh", + "Dim", + "E", + "Erf", + "Erfc", + "Erfcinv", + "Erfinv", + "Exp", + "Exp2", + "Expm1", + "FMA", + "Float32bits", + "Float32frombits", + "Float64bits", + "Float64frombits", + "Floor", + "Frexp", + "Gamma", + "Hypot", + "Ilogb", + "Inf", + "IsInf", + "IsNaN", + "J0", + "J1", + "Jn", + "Ldexp", + "Lgamma", + "Ln10", + "Ln2", + "Log", + "Log10", + "Log10E", + "Log1p", + "Log2", + "Log2E", + "Logb", + "Max", + "MaxFloat32", + "MaxFloat64", + "MaxInt", + "MaxInt16", + "MaxInt32", + "MaxInt64", + "MaxInt8", + "MaxUint", + "MaxUint16", + "MaxUint32", + "MaxUint64", + "MaxUint8", + "Min", + "MinInt", + "MinInt16", + "MinInt32", + "MinInt64", + "MinInt8", + "Mod", + "Modf", + "NaN", + "Nextafter", + "Nextafter32", + "Phi", + "Pi", + "Pow", + "Pow10", + "Remainder", + "Round", + "RoundToEven", + "Signbit", + "Sin", + "Sincos", + "Sinh", + "SmallestNonzeroFloat32", + "SmallestNonzeroFloat64", + "Sqrt", + "Sqrt2", + "SqrtE", + "SqrtPhi", + "SqrtPi", + "Tan", + "Tanh", + "Trunc", + "Y0", + "Y1", + "Yn", + }, + "math/big": { + "Above", + "Accuracy", + "AwayFromZero", + "Below", + "ErrNaN", + "Exact", + "Float", + "Int", + "Jacobi", + "MaxBase", + "MaxExp", + "MaxPrec", + "MinExp", + "NewFloat", + "NewInt", + "NewRat", + "ParseFloat", + "Rat", + "RoundingMode", + "ToNearestAway", + "ToNearestEven", + "ToNegativeInf", + "ToPositiveInf", + "ToZero", + "Word", + }, + "math/bits": { + "Add", + "Add32", + "Add64", + "Div", + "Div32", + "Div64", + "LeadingZeros", + "LeadingZeros16", + "LeadingZeros32", + "LeadingZeros64", + "LeadingZeros8", + "Len", + "Len16", + "Len32", + "Len64", + "Len8", + "Mul", + "Mul32", + "Mul64", + "OnesCount", + "OnesCount16", + "OnesCount32", + "OnesCount64", + "OnesCount8", + "Rem", + "Rem32", + "Rem64", + "Reverse", + "Reverse16", + "Reverse32", + "Reverse64", + "Reverse8", + "ReverseBytes", + "ReverseBytes16", + "ReverseBytes32", + "ReverseBytes64", + "RotateLeft", + "RotateLeft16", + "RotateLeft32", + "RotateLeft64", + "RotateLeft8", + "Sub", + "Sub32", + "Sub64", + "TrailingZeros", + "TrailingZeros16", + "TrailingZeros32", + "TrailingZeros64", + "TrailingZeros8", + "UintSize", + }, + "math/cmplx": { + "Abs", + "Acos", + "Acosh", + "Asin", + "Asinh", + "Atan", + "Atanh", + "Conj", + "Cos", + "Cosh", + "Cot", + "Exp", + "Inf", + "IsInf", + "IsNaN", + "Log", + "Log10", + "NaN", + "Phase", + "Polar", + "Pow", + "Rect", + "Sin", + "Sinh", + "Sqrt", + "Tan", + "Tanh", + }, + "math/rand": { + "ExpFloat64", + "Float32", + "Float64", + "Int", + "Int31", + "Int31n", + "Int63", + "Int63n", + "Intn", + "New", + "NewSource", + "NewZipf", + "NormFloat64", + "Perm", + "Rand", + "Read", + "Seed", + "Shuffle", + "Source", + "Source64", + "Uint32", + "Uint64", + "Zipf", + }, + "mime": { + "AddExtensionType", + "BEncoding", + "ErrInvalidMediaParameter", + "ExtensionsByType", + "FormatMediaType", + "ParseMediaType", + "QEncoding", + "TypeByExtension", + "WordDecoder", + "WordEncoder", + }, + "mime/multipart": { + "ErrMessageTooLarge", + "File", + "FileHeader", + "Form", + "NewReader", + "NewWriter", + "Part", + "Reader", + "Writer", + }, + "mime/quotedprintable": { + "NewReader", + "NewWriter", + "Reader", + "Writer", + }, + "net": { + "Addr", + "AddrError", + "Buffers", + "CIDRMask", + "Conn", + "DNSConfigError", + "DNSError", + "DefaultResolver", + "Dial", + "DialIP", + "DialTCP", + "DialTimeout", + "DialUDP", + "DialUnix", + "Dialer", + "ErrClosed", + "ErrWriteToConnected", + "Error", + "FileConn", + "FileListener", + "FilePacketConn", + "FlagBroadcast", + "FlagLoopback", + "FlagMulticast", + "FlagPointToPoint", + "FlagRunning", + "FlagUp", + "Flags", + "HardwareAddr", + "IP", + "IPAddr", + "IPConn", + "IPMask", + "IPNet", + "IPv4", + "IPv4Mask", + "IPv4allrouter", + "IPv4allsys", + "IPv4bcast", + "IPv4len", + "IPv4zero", + "IPv6interfacelocalallnodes", + "IPv6len", + "IPv6linklocalallnodes", + "IPv6linklocalallrouters", + "IPv6loopback", + "IPv6unspecified", + "IPv6zero", + "Interface", + "InterfaceAddrs", + "InterfaceByIndex", + "InterfaceByName", + "Interfaces", + "InvalidAddrError", + "JoinHostPort", + "Listen", + "ListenConfig", + "ListenIP", + "ListenMulticastUDP", + "ListenPacket", + "ListenTCP", + "ListenUDP", + "ListenUnix", + "ListenUnixgram", + "Listener", + "LookupAddr", + "LookupCNAME", + "LookupHost", + "LookupIP", + "LookupMX", + "LookupNS", + "LookupPort", + "LookupSRV", + "LookupTXT", + "MX", + "NS", + "OpError", + "PacketConn", + "ParseCIDR", + "ParseError", + "ParseIP", + "ParseMAC", + "Pipe", + "ResolveIPAddr", + "ResolveTCPAddr", + "ResolveUDPAddr", + "ResolveUnixAddr", + "Resolver", + "SRV", + "SplitHostPort", + "TCPAddr", + "TCPAddrFromAddrPort", + "TCPConn", + "TCPListener", + "UDPAddr", + "UDPAddrFromAddrPort", + "UDPConn", + "UnixAddr", + "UnixConn", + "UnixListener", + "UnknownNetworkError", + }, + "net/http": { + "AllowQuerySemicolons", + "CanonicalHeaderKey", + "Client", + "CloseNotifier", + "ConnState", + "Cookie", + "CookieJar", + "DefaultClient", + "DefaultMaxHeaderBytes", + "DefaultMaxIdleConnsPerHost", + "DefaultServeMux", + "DefaultTransport", + "DetectContentType", + "Dir", + "ErrAbortHandler", + "ErrBodyNotAllowed", + "ErrBodyReadAfterClose", + "ErrContentLength", + "ErrHandlerTimeout", + "ErrHeaderTooLong", + "ErrHijacked", + "ErrLineTooLong", + "ErrMissingBoundary", + "ErrMissingContentLength", + "ErrMissingFile", + "ErrNoCookie", + "ErrNoLocation", + "ErrNotMultipart", + "ErrNotSupported", + "ErrSchemeMismatch", + "ErrServerClosed", + "ErrShortBody", + "ErrSkipAltProtocol", + "ErrUnexpectedTrailer", + "ErrUseLastResponse", + "ErrWriteAfterFlush", + "Error", + "FS", + "File", + "FileServer", + "FileSystem", + "Flusher", + "Get", + "Handle", + "HandleFunc", + "Handler", + "HandlerFunc", + "Head", + "Header", + "Hijacker", + "ListenAndServe", + "ListenAndServeTLS", + "LocalAddrContextKey", + "MaxBytesError", + "MaxBytesHandler", + "MaxBytesReader", + "MethodConnect", + "MethodDelete", + "MethodGet", + "MethodHead", + "MethodOptions", + "MethodPatch", + "MethodPost", + "MethodPut", + "MethodTrace", + "NewFileTransport", + "NewRequest", + "NewRequestWithContext", + "NewResponseController", + "NewServeMux", + "NoBody", + "NotFound", + "NotFoundHandler", + "ParseHTTPVersion", + "ParseTime", + "Post", + "PostForm", + "ProtocolError", + "ProxyFromEnvironment", + "ProxyURL", + "PushOptions", + "Pusher", + "ReadRequest", + "ReadResponse", + "Redirect", + "RedirectHandler", + "Request", + "Response", + "ResponseController", + "ResponseWriter", + "RoundTripper", + "SameSite", + "SameSiteDefaultMode", + "SameSiteLaxMode", + "SameSiteNoneMode", + "SameSiteStrictMode", + "Serve", + "ServeContent", + "ServeFile", + "ServeMux", + "ServeTLS", + "Server", + "ServerContextKey", + "SetCookie", + "StateActive", + "StateClosed", + "StateHijacked", + "StateIdle", + "StateNew", + "StatusAccepted", + "StatusAlreadyReported", + "StatusBadGateway", + "StatusBadRequest", + "StatusConflict", + "StatusContinue", + "StatusCreated", + "StatusEarlyHints", + "StatusExpectationFailed", + "StatusFailedDependency", + "StatusForbidden", + "StatusFound", + "StatusGatewayTimeout", + "StatusGone", + "StatusHTTPVersionNotSupported", + "StatusIMUsed", + "StatusInsufficientStorage", + "StatusInternalServerError", + "StatusLengthRequired", + "StatusLocked", + "StatusLoopDetected", + "StatusMethodNotAllowed", + "StatusMisdirectedRequest", + "StatusMovedPermanently", + "StatusMultiStatus", + "StatusMultipleChoices", + "StatusNetworkAuthenticationRequired", + "StatusNoContent", + "StatusNonAuthoritativeInfo", + "StatusNotAcceptable", + "StatusNotExtended", + "StatusNotFound", + "StatusNotImplemented", + "StatusNotModified", + "StatusOK", + "StatusPartialContent", + "StatusPaymentRequired", + "StatusPermanentRedirect", + "StatusPreconditionFailed", + "StatusPreconditionRequired", + "StatusProcessing", + "StatusProxyAuthRequired", + "StatusRequestEntityTooLarge", + "StatusRequestHeaderFieldsTooLarge", + "StatusRequestTimeout", + "StatusRequestURITooLong", + "StatusRequestedRangeNotSatisfiable", + "StatusResetContent", + "StatusSeeOther", + "StatusServiceUnavailable", + "StatusSwitchingProtocols", + "StatusTeapot", + "StatusTemporaryRedirect", + "StatusText", + "StatusTooEarly", + "StatusTooManyRequests", + "StatusUnauthorized", + "StatusUnavailableForLegalReasons", + "StatusUnprocessableEntity", + "StatusUnsupportedMediaType", + "StatusUpgradeRequired", + "StatusUseProxy", + "StatusVariantAlsoNegotiates", + "StripPrefix", + "TimeFormat", + "TimeoutHandler", + "TrailerPrefix", + "Transport", + }, + "net/http/cgi": { + "Handler", + "Request", + "RequestFromMap", + "Serve", + }, + "net/http/cookiejar": { + "Jar", + "New", + "Options", + "PublicSuffixList", + }, + "net/http/fcgi": { + "ErrConnClosed", + "ErrRequestAborted", + "ProcessEnv", + "Serve", + }, + "net/http/httptest": { + "DefaultRemoteAddr", + "NewRecorder", + "NewRequest", + "NewServer", + "NewTLSServer", + "NewUnstartedServer", + "ResponseRecorder", + "Server", + }, + "net/http/httptrace": { + "ClientTrace", + "ContextClientTrace", + "DNSDoneInfo", + "DNSStartInfo", + "GotConnInfo", + "WithClientTrace", + "WroteRequestInfo", + }, + "net/http/httputil": { + "BufferPool", + "ClientConn", + "DumpRequest", + "DumpRequestOut", + "DumpResponse", + "ErrClosed", + "ErrLineTooLong", + "ErrPersistEOF", + "ErrPipeline", + "NewChunkedReader", + "NewChunkedWriter", + "NewClientConn", + "NewProxyClientConn", + "NewServerConn", + "NewSingleHostReverseProxy", + "ProxyRequest", + "ReverseProxy", + "ServerConn", + }, + "net/http/pprof": { + "Cmdline", + "Handler", + "Index", + "Profile", + "Symbol", + "Trace", + }, + "net/mail": { + "Address", + "AddressParser", + "ErrHeaderNotPresent", + "Header", + "Message", + "ParseAddress", + "ParseAddressList", + "ParseDate", + "ReadMessage", + }, + "net/netip": { + "Addr", + "AddrFrom16", + "AddrFrom4", + "AddrFromSlice", + "AddrPort", + "AddrPortFrom", + "IPv4Unspecified", + "IPv6LinkLocalAllNodes", + "IPv6LinkLocalAllRouters", + "IPv6Loopback", + "IPv6Unspecified", + "MustParseAddr", + "MustParseAddrPort", + "MustParsePrefix", + "ParseAddr", + "ParseAddrPort", + "ParsePrefix", + "Prefix", + "PrefixFrom", + }, + "net/rpc": { + "Accept", + "Call", + "Client", + "ClientCodec", + "DefaultDebugPath", + "DefaultRPCPath", + "DefaultServer", + "Dial", + "DialHTTP", + "DialHTTPPath", + "ErrShutdown", + "HandleHTTP", + "NewClient", + "NewClientWithCodec", + "NewServer", + "Register", + "RegisterName", + "Request", + "Response", + "ServeCodec", + "ServeConn", + "ServeRequest", + "Server", + "ServerCodec", + "ServerError", + }, + "net/rpc/jsonrpc": { + "Dial", + "NewClient", + "NewClientCodec", + "NewServerCodec", + "ServeConn", + }, + "net/smtp": { + "Auth", + "CRAMMD5Auth", + "Client", + "Dial", + "NewClient", + "PlainAuth", + "SendMail", + "ServerInfo", + }, + "net/textproto": { + "CanonicalMIMEHeaderKey", + "Conn", + "Dial", + "Error", + "MIMEHeader", + "NewConn", + "NewReader", + "NewWriter", + "Pipeline", + "ProtocolError", + "Reader", + "TrimBytes", + "TrimString", + "Writer", + }, + "net/url": { + "Error", + "EscapeError", + "InvalidHostError", + "JoinPath", + "Parse", + "ParseQuery", + "ParseRequestURI", + "PathEscape", + "PathUnescape", + "QueryEscape", + "QueryUnescape", + "URL", + "User", + "UserPassword", + "Userinfo", + "Values", + }, + "os": { + "Args", + "Chdir", + "Chmod", + "Chown", + "Chtimes", + "Clearenv", + "Create", + "CreateTemp", + "DevNull", + "DirEntry", + "DirFS", + "Environ", + "ErrClosed", + "ErrDeadlineExceeded", + "ErrExist", + "ErrInvalid", + "ErrNoDeadline", + "ErrNotExist", + "ErrPermission", + "ErrProcessDone", + "Executable", + "Exit", + "Expand", + "ExpandEnv", + "File", + "FileInfo", + "FileMode", + "FindProcess", + "Getegid", + "Getenv", + "Geteuid", + "Getgid", + "Getgroups", + "Getpagesize", + "Getpid", + "Getppid", + "Getuid", + "Getwd", + "Hostname", + "Interrupt", + "IsExist", + "IsNotExist", + "IsPathSeparator", + "IsPermission", + "IsTimeout", + "Kill", + "Lchown", + "Link", + "LinkError", + "LookupEnv", + "Lstat", + "Mkdir", + "MkdirAll", + "MkdirTemp", + "ModeAppend", + "ModeCharDevice", + "ModeDevice", + "ModeDir", + "ModeExclusive", + "ModeIrregular", + "ModeNamedPipe", + "ModePerm", + "ModeSetgid", + "ModeSetuid", + "ModeSocket", + "ModeSticky", + "ModeSymlink", + "ModeTemporary", + "ModeType", + "NewFile", + "NewSyscallError", + "O_APPEND", + "O_CREATE", + "O_EXCL", + "O_RDONLY", + "O_RDWR", + "O_SYNC", + "O_TRUNC", + "O_WRONLY", + "Open", + "OpenFile", + "PathError", + "PathListSeparator", + "PathSeparator", + "Pipe", + "ProcAttr", + "Process", + "ProcessState", + "ReadDir", + "ReadFile", + "Readlink", + "Remove", + "RemoveAll", + "Rename", + "SEEK_CUR", + "SEEK_END", + "SEEK_SET", + "SameFile", + "Setenv", + "Signal", + "StartProcess", + "Stat", + "Stderr", + "Stdin", + "Stdout", + "Symlink", + "SyscallError", + "TempDir", + "Truncate", + "Unsetenv", + "UserCacheDir", + "UserConfigDir", + "UserHomeDir", + "WriteFile", + }, + "os/exec": { + "Cmd", + "Command", + "CommandContext", + "ErrDot", + "ErrNotFound", + "ErrWaitDelay", + "Error", + "ExitError", + "LookPath", + }, + "os/signal": { + "Ignore", + "Ignored", + "Notify", + "NotifyContext", + "Reset", + "Stop", + }, + "os/user": { + "Current", + "Group", + "Lookup", + "LookupGroup", + "LookupGroupId", + "LookupId", + "UnknownGroupError", + "UnknownGroupIdError", + "UnknownUserError", + "UnknownUserIdError", + "User", + }, + "path": { + "Base", + "Clean", + "Dir", + "ErrBadPattern", + "Ext", + "IsAbs", + "Join", + "Match", + "Split", + }, + "path/filepath": { + "Abs", + "Base", + "Clean", + "Dir", + "ErrBadPattern", + "EvalSymlinks", + "Ext", + "FromSlash", + "Glob", + "HasPrefix", + "IsAbs", + "IsLocal", + "Join", + "ListSeparator", + "Match", + "Rel", + "Separator", + "SkipAll", + "SkipDir", + "Split", + "SplitList", + "ToSlash", + "VolumeName", + "Walk", + "WalkDir", + "WalkFunc", + }, + "plugin": { + "Open", + "Plugin", + "Symbol", + }, + "reflect": { + "Append", + "AppendSlice", + "Array", + "ArrayOf", + "Bool", + "BothDir", + "Chan", + "ChanDir", + "ChanOf", + "Complex128", + "Complex64", + "Copy", + "DeepEqual", + "Float32", + "Float64", + "Func", + "FuncOf", + "Indirect", + "Int", + "Int16", + "Int32", + "Int64", + "Int8", + "Interface", + "Invalid", + "Kind", + "MakeChan", + "MakeFunc", + "MakeMap", + "MakeMapWithSize", + "MakeSlice", + "Map", + "MapIter", + "MapOf", + "Method", + "New", + "NewAt", + "Pointer", + "PointerTo", + "Ptr", + "PtrTo", + "RecvDir", + "Select", + "SelectCase", + "SelectDefault", + "SelectDir", + "SelectRecv", + "SelectSend", + "SendDir", + "Slice", + "SliceHeader", + "SliceOf", + "String", + "StringHeader", + "Struct", + "StructField", + "StructOf", + "StructTag", + "Swapper", + "Type", + "TypeOf", + "Uint", + "Uint16", + "Uint32", + "Uint64", + "Uint8", + "Uintptr", + "UnsafePointer", + "Value", + "ValueError", + "ValueOf", + "VisibleFields", + "Zero", + }, + "regexp": { + "Compile", + "CompilePOSIX", + "Match", + "MatchReader", + "MatchString", + "MustCompile", + "MustCompilePOSIX", + "QuoteMeta", + "Regexp", + }, + "regexp/syntax": { + "ClassNL", + "Compile", + "DotNL", + "EmptyBeginLine", + "EmptyBeginText", + "EmptyEndLine", + "EmptyEndText", + "EmptyNoWordBoundary", + "EmptyOp", + "EmptyOpContext", + "EmptyWordBoundary", + "ErrInternalError", + "ErrInvalidCharClass", + "ErrInvalidCharRange", + "ErrInvalidEscape", + "ErrInvalidNamedCapture", + "ErrInvalidPerlOp", + "ErrInvalidRepeatOp", + "ErrInvalidRepeatSize", + "ErrInvalidUTF8", + "ErrLarge", + "ErrMissingBracket", + "ErrMissingParen", + "ErrMissingRepeatArgument", + "ErrNestingDepth", + "ErrTrailingBackslash", + "ErrUnexpectedParen", + "Error", + "ErrorCode", + "Flags", + "FoldCase", + "Inst", + "InstAlt", + "InstAltMatch", + "InstCapture", + "InstEmptyWidth", + "InstFail", + "InstMatch", + "InstNop", + "InstOp", + "InstRune", + "InstRune1", + "InstRuneAny", + "InstRuneAnyNotNL", + "IsWordChar", + "Literal", + "MatchNL", + "NonGreedy", + "OneLine", + "Op", + "OpAlternate", + "OpAnyChar", + "OpAnyCharNotNL", + "OpBeginLine", + "OpBeginText", + "OpCapture", + "OpCharClass", + "OpConcat", + "OpEmptyMatch", + "OpEndLine", + "OpEndText", + "OpLiteral", + "OpNoMatch", + "OpNoWordBoundary", + "OpPlus", + "OpQuest", + "OpRepeat", + "OpStar", + "OpWordBoundary", + "POSIX", + "Parse", + "Perl", + "PerlX", + "Prog", + "Regexp", + "Simple", + "UnicodeGroups", + "WasDollar", + }, + "runtime": { + "BlockProfile", + "BlockProfileRecord", + "Breakpoint", + "CPUProfile", + "Caller", + "Callers", + "CallersFrames", + "Compiler", + "Error", + "Frame", + "Frames", + "Func", + "FuncForPC", + "GC", + "GOARCH", + "GOMAXPROCS", + "GOOS", + "GOROOT", + "Goexit", + "GoroutineProfile", + "Gosched", + "KeepAlive", + "LockOSThread", + "MemProfile", + "MemProfileRate", + "MemProfileRecord", + "MemStats", + "MutexProfile", + "NumCPU", + "NumCgoCall", + "NumGoroutine", + "PanicNilError", + "Pinner", + "ReadMemStats", + "ReadTrace", + "SetBlockProfileRate", + "SetCPUProfileRate", + "SetCgoTraceback", + "SetFinalizer", + "SetMutexProfileFraction", + "Stack", + "StackRecord", + "StartTrace", + "StopTrace", + "ThreadCreateProfile", + "TypeAssertionError", + "UnlockOSThread", + "Version", + }, + "runtime/cgo": { + "Handle", + "Incomplete", + "NewHandle", + }, + "runtime/coverage": { + "ClearCounters", + "WriteCounters", + "WriteCountersDir", + "WriteMeta", + "WriteMetaDir", + }, + "runtime/debug": { + "BuildInfo", + "BuildSetting", + "FreeOSMemory", + "GCStats", + "Module", + "ParseBuildInfo", + "PrintStack", + "ReadBuildInfo", + "ReadGCStats", + "SetGCPercent", + "SetMaxStack", + "SetMaxThreads", + "SetMemoryLimit", + "SetPanicOnFault", + "SetTraceback", + "Stack", + "WriteHeapDump", + }, + "runtime/metrics": { + "All", + "Description", + "Float64Histogram", + "KindBad", + "KindFloat64", + "KindFloat64Histogram", + "KindUint64", + "Read", + "Sample", + "Value", + "ValueKind", + }, + "runtime/pprof": { + "Do", + "ForLabels", + "Label", + "LabelSet", + "Labels", + "Lookup", + "NewProfile", + "Profile", + "Profiles", + "SetGoroutineLabels", + "StartCPUProfile", + "StopCPUProfile", + "WithLabels", + "WriteHeapProfile", + }, + "runtime/trace": { + "IsEnabled", + "Log", + "Logf", + "NewTask", + "Region", + "Start", + "StartRegion", + "Stop", + "Task", + "WithRegion", + }, + "slices": { + "BinarySearch", + "BinarySearchFunc", + "Clip", + "Clone", + "Compact", + "CompactFunc", + "Compare", + "CompareFunc", + "Contains", + "ContainsFunc", + "Delete", + "DeleteFunc", + "Equal", + "EqualFunc", + "Grow", + "Index", + "IndexFunc", + "Insert", + "IsSorted", + "IsSortedFunc", + "Max", + "MaxFunc", + "Min", + "MinFunc", + "Replace", + "Reverse", + "Sort", + "SortFunc", + "SortStableFunc", + }, + "sort": { + "Find", + "Float64Slice", + "Float64s", + "Float64sAreSorted", + "IntSlice", + "Interface", + "Ints", + "IntsAreSorted", + "IsSorted", + "Reverse", + "Search", + "SearchFloat64s", + "SearchInts", + "SearchStrings", + "Slice", + "SliceIsSorted", + "SliceStable", + "Sort", + "Stable", + "StringSlice", + "Strings", + "StringsAreSorted", + }, + "strconv": { + "AppendBool", + "AppendFloat", + "AppendInt", + "AppendQuote", + "AppendQuoteRune", + "AppendQuoteRuneToASCII", + "AppendQuoteRuneToGraphic", + "AppendQuoteToASCII", + "AppendQuoteToGraphic", + "AppendUint", + "Atoi", + "CanBackquote", + "ErrRange", + "ErrSyntax", + "FormatBool", + "FormatComplex", + "FormatFloat", + "FormatInt", + "FormatUint", + "IntSize", + "IsGraphic", + "IsPrint", + "Itoa", + "NumError", + "ParseBool", + "ParseComplex", + "ParseFloat", + "ParseInt", + "ParseUint", + "Quote", + "QuoteRune", + "QuoteRuneToASCII", + "QuoteRuneToGraphic", + "QuoteToASCII", + "QuoteToGraphic", + "QuotedPrefix", + "Unquote", + "UnquoteChar", + }, + "strings": { + "Builder", + "Clone", + "Compare", + "Contains", + "ContainsAny", + "ContainsFunc", + "ContainsRune", + "Count", + "Cut", + "CutPrefix", + "CutSuffix", + "EqualFold", + "Fields", + "FieldsFunc", + "HasPrefix", + "HasSuffix", + "Index", + "IndexAny", + "IndexByte", + "IndexFunc", + "IndexRune", + "Join", + "LastIndex", + "LastIndexAny", + "LastIndexByte", + "LastIndexFunc", + "Map", + "NewReader", + "NewReplacer", + "Reader", + "Repeat", + "Replace", + "ReplaceAll", + "Replacer", + "Split", + "SplitAfter", + "SplitAfterN", + "SplitN", + "Title", + "ToLower", + "ToLowerSpecial", + "ToTitle", + "ToTitleSpecial", + "ToUpper", + "ToUpperSpecial", + "ToValidUTF8", + "Trim", + "TrimFunc", + "TrimLeft", + "TrimLeftFunc", + "TrimPrefix", + "TrimRight", + "TrimRightFunc", + "TrimSpace", + "TrimSuffix", + }, + "sync": { + "Cond", + "Locker", + "Map", + "Mutex", + "NewCond", + "Once", + "OnceFunc", + "OnceValue", + "OnceValues", + "Pool", + "RWMutex", + "WaitGroup", + }, + "sync/atomic": { + "AddInt32", + "AddInt64", + "AddUint32", + "AddUint64", + "AddUintptr", + "Bool", + "CompareAndSwapInt32", + "CompareAndSwapInt64", + "CompareAndSwapPointer", + "CompareAndSwapUint32", + "CompareAndSwapUint64", + "CompareAndSwapUintptr", + "Int32", + "Int64", + "LoadInt32", + "LoadInt64", + "LoadPointer", + "LoadUint32", + "LoadUint64", + "LoadUintptr", + "Pointer", + "StoreInt32", + "StoreInt64", + "StorePointer", + "StoreUint32", + "StoreUint64", + "StoreUintptr", + "SwapInt32", + "SwapInt64", + "SwapPointer", + "SwapUint32", + "SwapUint64", + "SwapUintptr", + "Uint32", + "Uint64", + "Uintptr", + "Value", + }, + "syscall": { + "AF_ALG", + "AF_APPLETALK", + "AF_ARP", + "AF_ASH", + "AF_ATM", + "AF_ATMPVC", + "AF_ATMSVC", + "AF_AX25", + "AF_BLUETOOTH", + "AF_BRIDGE", + "AF_CAIF", + "AF_CAN", + "AF_CCITT", + "AF_CHAOS", + "AF_CNT", + "AF_COIP", + "AF_DATAKIT", + "AF_DECnet", + "AF_DLI", + "AF_E164", + "AF_ECMA", + "AF_ECONET", + "AF_ENCAP", + "AF_FILE", + "AF_HYLINK", + "AF_IEEE80211", + "AF_IEEE802154", + "AF_IMPLINK", + "AF_INET", + "AF_INET6", + "AF_INET6_SDP", + "AF_INET_SDP", + "AF_IPX", + "AF_IRDA", + "AF_ISDN", + "AF_ISO", + "AF_IUCV", + "AF_KEY", + "AF_LAT", + "AF_LINK", + "AF_LLC", + "AF_LOCAL", + "AF_MAX", + "AF_MPLS", + "AF_NATM", + "AF_NDRV", + "AF_NETBEUI", + "AF_NETBIOS", + "AF_NETGRAPH", + "AF_NETLINK", + "AF_NETROM", + "AF_NS", + "AF_OROUTE", + "AF_OSI", + "AF_PACKET", + "AF_PHONET", + "AF_PPP", + "AF_PPPOX", + "AF_PUP", + "AF_RDS", + "AF_RESERVED_36", + "AF_ROSE", + "AF_ROUTE", + "AF_RXRPC", + "AF_SCLUSTER", + "AF_SECURITY", + "AF_SIP", + "AF_SLOW", + "AF_SNA", + "AF_SYSTEM", + "AF_TIPC", + "AF_UNIX", + "AF_UNSPEC", + "AF_UTUN", + "AF_VENDOR00", + "AF_VENDOR01", + "AF_VENDOR02", + "AF_VENDOR03", + "AF_VENDOR04", + "AF_VENDOR05", + "AF_VENDOR06", + "AF_VENDOR07", + "AF_VENDOR08", + "AF_VENDOR09", + "AF_VENDOR10", + "AF_VENDOR11", + "AF_VENDOR12", + "AF_VENDOR13", + "AF_VENDOR14", + "AF_VENDOR15", + "AF_VENDOR16", + "AF_VENDOR17", + "AF_VENDOR18", + "AF_VENDOR19", + "AF_VENDOR20", + "AF_VENDOR21", + "AF_VENDOR22", + "AF_VENDOR23", + "AF_VENDOR24", + "AF_VENDOR25", + "AF_VENDOR26", + "AF_VENDOR27", + "AF_VENDOR28", + "AF_VENDOR29", + "AF_VENDOR30", + "AF_VENDOR31", + "AF_VENDOR32", + "AF_VENDOR33", + "AF_VENDOR34", + "AF_VENDOR35", + "AF_VENDOR36", + "AF_VENDOR37", + "AF_VENDOR38", + "AF_VENDOR39", + "AF_VENDOR40", + "AF_VENDOR41", + "AF_VENDOR42", + "AF_VENDOR43", + "AF_VENDOR44", + "AF_VENDOR45", + "AF_VENDOR46", + "AF_VENDOR47", + "AF_WANPIPE", + "AF_X25", + "AI_CANONNAME", + "AI_NUMERICHOST", + "AI_PASSIVE", + "APPLICATION_ERROR", + "ARPHRD_ADAPT", + "ARPHRD_APPLETLK", + "ARPHRD_ARCNET", + "ARPHRD_ASH", + "ARPHRD_ATM", + "ARPHRD_AX25", + "ARPHRD_BIF", + "ARPHRD_CHAOS", + "ARPHRD_CISCO", + "ARPHRD_CSLIP", + "ARPHRD_CSLIP6", + "ARPHRD_DDCMP", + "ARPHRD_DLCI", + "ARPHRD_ECONET", + "ARPHRD_EETHER", + "ARPHRD_ETHER", + "ARPHRD_EUI64", + "ARPHRD_FCAL", + "ARPHRD_FCFABRIC", + "ARPHRD_FCPL", + "ARPHRD_FCPP", + "ARPHRD_FDDI", + "ARPHRD_FRAD", + "ARPHRD_FRELAY", + "ARPHRD_HDLC", + "ARPHRD_HIPPI", + "ARPHRD_HWX25", + "ARPHRD_IEEE1394", + "ARPHRD_IEEE802", + "ARPHRD_IEEE80211", + "ARPHRD_IEEE80211_PRISM", + "ARPHRD_IEEE80211_RADIOTAP", + "ARPHRD_IEEE802154", + "ARPHRD_IEEE802154_PHY", + "ARPHRD_IEEE802_TR", + "ARPHRD_INFINIBAND", + "ARPHRD_IPDDP", + "ARPHRD_IPGRE", + "ARPHRD_IRDA", + "ARPHRD_LAPB", + "ARPHRD_LOCALTLK", + "ARPHRD_LOOPBACK", + "ARPHRD_METRICOM", + "ARPHRD_NETROM", + "ARPHRD_NONE", + "ARPHRD_PIMREG", + "ARPHRD_PPP", + "ARPHRD_PRONET", + "ARPHRD_RAWHDLC", + "ARPHRD_ROSE", + "ARPHRD_RSRVD", + "ARPHRD_SIT", + "ARPHRD_SKIP", + "ARPHRD_SLIP", + "ARPHRD_SLIP6", + "ARPHRD_STRIP", + "ARPHRD_TUNNEL", + "ARPHRD_TUNNEL6", + "ARPHRD_VOID", + "ARPHRD_X25", + "AUTHTYPE_CLIENT", + "AUTHTYPE_SERVER", + "Accept", + "Accept4", + "AcceptEx", + "Access", + "Acct", + "AddrinfoW", + "Adjtime", + "Adjtimex", + "AllThreadsSyscall", + "AllThreadsSyscall6", + "AttachLsf", + "B0", + "B1000000", + "B110", + "B115200", + "B1152000", + "B1200", + "B134", + "B14400", + "B150", + "B1500000", + "B1800", + "B19200", + "B200", + "B2000000", + "B230400", + "B2400", + "B2500000", + "B28800", + "B300", + "B3000000", + "B3500000", + "B38400", + "B4000000", + "B460800", + "B4800", + "B50", + "B500000", + "B57600", + "B576000", + "B600", + "B7200", + "B75", + "B76800", + "B921600", + "B9600", + "BASE_PROTOCOL", + "BIOCFEEDBACK", + "BIOCFLUSH", + "BIOCGBLEN", + "BIOCGDIRECTION", + "BIOCGDIRFILT", + "BIOCGDLT", + "BIOCGDLTLIST", + "BIOCGETBUFMODE", + "BIOCGETIF", + "BIOCGETZMAX", + "BIOCGFEEDBACK", + "BIOCGFILDROP", + "BIOCGHDRCMPLT", + "BIOCGRSIG", + "BIOCGRTIMEOUT", + "BIOCGSEESENT", + "BIOCGSTATS", + "BIOCGSTATSOLD", + "BIOCGTSTAMP", + "BIOCIMMEDIATE", + "BIOCLOCK", + "BIOCPROMISC", + "BIOCROTZBUF", + "BIOCSBLEN", + "BIOCSDIRECTION", + "BIOCSDIRFILT", + "BIOCSDLT", + "BIOCSETBUFMODE", + "BIOCSETF", + "BIOCSETFNR", + "BIOCSETIF", + "BIOCSETWF", + "BIOCSETZBUF", + "BIOCSFEEDBACK", + "BIOCSFILDROP", + "BIOCSHDRCMPLT", + "BIOCSRSIG", + "BIOCSRTIMEOUT", + "BIOCSSEESENT", + "BIOCSTCPF", + "BIOCSTSTAMP", + "BIOCSUDPF", + "BIOCVERSION", + "BPF_A", + "BPF_ABS", + "BPF_ADD", + "BPF_ALIGNMENT", + "BPF_ALIGNMENT32", + "BPF_ALU", + "BPF_AND", + "BPF_B", + "BPF_BUFMODE_BUFFER", + "BPF_BUFMODE_ZBUF", + "BPF_DFLTBUFSIZE", + "BPF_DIRECTION_IN", + "BPF_DIRECTION_OUT", + "BPF_DIV", + "BPF_H", + "BPF_IMM", + "BPF_IND", + "BPF_JA", + "BPF_JEQ", + "BPF_JGE", + "BPF_JGT", + "BPF_JMP", + "BPF_JSET", + "BPF_K", + "BPF_LD", + "BPF_LDX", + "BPF_LEN", + "BPF_LSH", + "BPF_MAJOR_VERSION", + "BPF_MAXBUFSIZE", + "BPF_MAXINSNS", + "BPF_MEM", + "BPF_MEMWORDS", + "BPF_MINBUFSIZE", + "BPF_MINOR_VERSION", + "BPF_MISC", + "BPF_MSH", + "BPF_MUL", + "BPF_NEG", + "BPF_OR", + "BPF_RELEASE", + "BPF_RET", + "BPF_RSH", + "BPF_ST", + "BPF_STX", + "BPF_SUB", + "BPF_TAX", + "BPF_TXA", + "BPF_T_BINTIME", + "BPF_T_BINTIME_FAST", + "BPF_T_BINTIME_MONOTONIC", + "BPF_T_BINTIME_MONOTONIC_FAST", + "BPF_T_FAST", + "BPF_T_FLAG_MASK", + "BPF_T_FORMAT_MASK", + "BPF_T_MICROTIME", + "BPF_T_MICROTIME_FAST", + "BPF_T_MICROTIME_MONOTONIC", + "BPF_T_MICROTIME_MONOTONIC_FAST", + "BPF_T_MONOTONIC", + "BPF_T_MONOTONIC_FAST", + "BPF_T_NANOTIME", + "BPF_T_NANOTIME_FAST", + "BPF_T_NANOTIME_MONOTONIC", + "BPF_T_NANOTIME_MONOTONIC_FAST", + "BPF_T_NONE", + "BPF_T_NORMAL", + "BPF_W", + "BPF_X", + "BRKINT", + "Bind", + "BindToDevice", + "BpfBuflen", + "BpfDatalink", + "BpfHdr", + "BpfHeadercmpl", + "BpfInsn", + "BpfInterface", + "BpfJump", + "BpfProgram", + "BpfStat", + "BpfStats", + "BpfStmt", + "BpfTimeout", + "BpfTimeval", + "BpfVersion", + "BpfZbuf", + "BpfZbufHeader", + "ByHandleFileInformation", + "BytePtrFromString", + "ByteSliceFromString", + "CCR0_FLUSH", + "CERT_CHAIN_POLICY_AUTHENTICODE", + "CERT_CHAIN_POLICY_AUTHENTICODE_TS", + "CERT_CHAIN_POLICY_BASE", + "CERT_CHAIN_POLICY_BASIC_CONSTRAINTS", + "CERT_CHAIN_POLICY_EV", + "CERT_CHAIN_POLICY_MICROSOFT_ROOT", + "CERT_CHAIN_POLICY_NT_AUTH", + "CERT_CHAIN_POLICY_SSL", + "CERT_E_CN_NO_MATCH", + "CERT_E_EXPIRED", + "CERT_E_PURPOSE", + "CERT_E_ROLE", + "CERT_E_UNTRUSTEDROOT", + "CERT_STORE_ADD_ALWAYS", + "CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG", + "CERT_STORE_PROV_MEMORY", + "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT", + "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT", + "CERT_TRUST_INVALID_BASIC_CONSTRAINTS", + "CERT_TRUST_INVALID_EXTENSION", + "CERT_TRUST_INVALID_NAME_CONSTRAINTS", + "CERT_TRUST_INVALID_POLICY_CONSTRAINTS", + "CERT_TRUST_IS_CYCLIC", + "CERT_TRUST_IS_EXPLICIT_DISTRUST", + "CERT_TRUST_IS_NOT_SIGNATURE_VALID", + "CERT_TRUST_IS_NOT_TIME_VALID", + "CERT_TRUST_IS_NOT_VALID_FOR_USAGE", + "CERT_TRUST_IS_OFFLINE_REVOCATION", + "CERT_TRUST_IS_REVOKED", + "CERT_TRUST_IS_UNTRUSTED_ROOT", + "CERT_TRUST_NO_ERROR", + "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY", + "CERT_TRUST_REVOCATION_STATUS_UNKNOWN", + "CFLUSH", + "CLOCAL", + "CLONE_CHILD_CLEARTID", + "CLONE_CHILD_SETTID", + "CLONE_CLEAR_SIGHAND", + "CLONE_CSIGNAL", + "CLONE_DETACHED", + "CLONE_FILES", + "CLONE_FS", + "CLONE_INTO_CGROUP", + "CLONE_IO", + "CLONE_NEWCGROUP", + "CLONE_NEWIPC", + "CLONE_NEWNET", + "CLONE_NEWNS", + "CLONE_NEWPID", + "CLONE_NEWTIME", + "CLONE_NEWUSER", + "CLONE_NEWUTS", + "CLONE_PARENT", + "CLONE_PARENT_SETTID", + "CLONE_PID", + "CLONE_PIDFD", + "CLONE_PTRACE", + "CLONE_SETTLS", + "CLONE_SIGHAND", + "CLONE_SYSVSEM", + "CLONE_THREAD", + "CLONE_UNTRACED", + "CLONE_VFORK", + "CLONE_VM", + "CPUID_CFLUSH", + "CREAD", + "CREATE_ALWAYS", + "CREATE_NEW", + "CREATE_NEW_PROCESS_GROUP", + "CREATE_UNICODE_ENVIRONMENT", + "CRYPT_DEFAULT_CONTAINER_OPTIONAL", + "CRYPT_DELETEKEYSET", + "CRYPT_MACHINE_KEYSET", + "CRYPT_NEWKEYSET", + "CRYPT_SILENT", + "CRYPT_VERIFYCONTEXT", + "CS5", + "CS6", + "CS7", + "CS8", + "CSIZE", + "CSTART", + "CSTATUS", + "CSTOP", + "CSTOPB", + "CSUSP", + "CTL_MAXNAME", + "CTL_NET", + "CTL_QUERY", + "CTRL_BREAK_EVENT", + "CTRL_CLOSE_EVENT", + "CTRL_C_EVENT", + "CTRL_LOGOFF_EVENT", + "CTRL_SHUTDOWN_EVENT", + "CancelIo", + "CancelIoEx", + "CertAddCertificateContextToStore", + "CertChainContext", + "CertChainElement", + "CertChainPara", + "CertChainPolicyPara", + "CertChainPolicyStatus", + "CertCloseStore", + "CertContext", + "CertCreateCertificateContext", + "CertEnhKeyUsage", + "CertEnumCertificatesInStore", + "CertFreeCertificateChain", + "CertFreeCertificateContext", + "CertGetCertificateChain", + "CertInfo", + "CertOpenStore", + "CertOpenSystemStore", + "CertRevocationCrlInfo", + "CertRevocationInfo", + "CertSimpleChain", + "CertTrustListInfo", + "CertTrustStatus", + "CertUsageMatch", + "CertVerifyCertificateChainPolicy", + "Chdir", + "CheckBpfVersion", + "Chflags", + "Chmod", + "Chown", + "Chroot", + "Clearenv", + "Close", + "CloseHandle", + "CloseOnExec", + "Closesocket", + "CmsgLen", + "CmsgSpace", + "Cmsghdr", + "CommandLineToArgv", + "ComputerName", + "Conn", + "Connect", + "ConnectEx", + "ConvertSidToStringSid", + "ConvertStringSidToSid", + "CopySid", + "Creat", + "CreateDirectory", + "CreateFile", + "CreateFileMapping", + "CreateHardLink", + "CreateIoCompletionPort", + "CreatePipe", + "CreateProcess", + "CreateProcessAsUser", + "CreateSymbolicLink", + "CreateToolhelp32Snapshot", + "Credential", + "CryptAcquireContext", + "CryptGenRandom", + "CryptReleaseContext", + "DIOCBSFLUSH", + "DIOCOSFPFLUSH", + "DLL", + "DLLError", + "DLT_A429", + "DLT_A653_ICM", + "DLT_AIRONET_HEADER", + "DLT_AOS", + "DLT_APPLE_IP_OVER_IEEE1394", + "DLT_ARCNET", + "DLT_ARCNET_LINUX", + "DLT_ATM_CLIP", + "DLT_ATM_RFC1483", + "DLT_AURORA", + "DLT_AX25", + "DLT_AX25_KISS", + "DLT_BACNET_MS_TP", + "DLT_BLUETOOTH_HCI_H4", + "DLT_BLUETOOTH_HCI_H4_WITH_PHDR", + "DLT_CAN20B", + "DLT_CAN_SOCKETCAN", + "DLT_CHAOS", + "DLT_CHDLC", + "DLT_CISCO_IOS", + "DLT_C_HDLC", + "DLT_C_HDLC_WITH_DIR", + "DLT_DBUS", + "DLT_DECT", + "DLT_DOCSIS", + "DLT_DVB_CI", + "DLT_ECONET", + "DLT_EN10MB", + "DLT_EN3MB", + "DLT_ENC", + "DLT_ERF", + "DLT_ERF_ETH", + "DLT_ERF_POS", + "DLT_FC_2", + "DLT_FC_2_WITH_FRAME_DELIMS", + "DLT_FDDI", + "DLT_FLEXRAY", + "DLT_FRELAY", + "DLT_FRELAY_WITH_DIR", + "DLT_GCOM_SERIAL", + "DLT_GCOM_T1E1", + "DLT_GPF_F", + "DLT_GPF_T", + "DLT_GPRS_LLC", + "DLT_GSMTAP_ABIS", + "DLT_GSMTAP_UM", + "DLT_HDLC", + "DLT_HHDLC", + "DLT_HIPPI", + "DLT_IBM_SN", + "DLT_IBM_SP", + "DLT_IEEE802", + "DLT_IEEE802_11", + "DLT_IEEE802_11_RADIO", + "DLT_IEEE802_11_RADIO_AVS", + "DLT_IEEE802_15_4", + "DLT_IEEE802_15_4_LINUX", + "DLT_IEEE802_15_4_NOFCS", + "DLT_IEEE802_15_4_NONASK_PHY", + "DLT_IEEE802_16_MAC_CPS", + "DLT_IEEE802_16_MAC_CPS_RADIO", + "DLT_IPFILTER", + "DLT_IPMB", + "DLT_IPMB_LINUX", + "DLT_IPNET", + "DLT_IPOIB", + "DLT_IPV4", + "DLT_IPV6", + "DLT_IP_OVER_FC", + "DLT_JUNIPER_ATM1", + "DLT_JUNIPER_ATM2", + "DLT_JUNIPER_ATM_CEMIC", + "DLT_JUNIPER_CHDLC", + "DLT_JUNIPER_ES", + "DLT_JUNIPER_ETHER", + "DLT_JUNIPER_FIBRECHANNEL", + "DLT_JUNIPER_FRELAY", + "DLT_JUNIPER_GGSN", + "DLT_JUNIPER_ISM", + "DLT_JUNIPER_MFR", + "DLT_JUNIPER_MLFR", + "DLT_JUNIPER_MLPPP", + "DLT_JUNIPER_MONITOR", + "DLT_JUNIPER_PIC_PEER", + "DLT_JUNIPER_PPP", + "DLT_JUNIPER_PPPOE", + "DLT_JUNIPER_PPPOE_ATM", + "DLT_JUNIPER_SERVICES", + "DLT_JUNIPER_SRX_E2E", + "DLT_JUNIPER_ST", + "DLT_JUNIPER_VP", + "DLT_JUNIPER_VS", + "DLT_LAPB_WITH_DIR", + "DLT_LAPD", + "DLT_LIN", + "DLT_LINUX_EVDEV", + "DLT_LINUX_IRDA", + "DLT_LINUX_LAPD", + "DLT_LINUX_PPP_WITHDIRECTION", + "DLT_LINUX_SLL", + "DLT_LOOP", + "DLT_LTALK", + "DLT_MATCHING_MAX", + "DLT_MATCHING_MIN", + "DLT_MFR", + "DLT_MOST", + "DLT_MPEG_2_TS", + "DLT_MPLS", + "DLT_MTP2", + "DLT_MTP2_WITH_PHDR", + "DLT_MTP3", + "DLT_MUX27010", + "DLT_NETANALYZER", + "DLT_NETANALYZER_TRANSPARENT", + "DLT_NFC_LLCP", + "DLT_NFLOG", + "DLT_NG40", + "DLT_NULL", + "DLT_PCI_EXP", + "DLT_PFLOG", + "DLT_PFSYNC", + "DLT_PPI", + "DLT_PPP", + "DLT_PPP_BSDOS", + "DLT_PPP_ETHER", + "DLT_PPP_PPPD", + "DLT_PPP_SERIAL", + "DLT_PPP_WITH_DIR", + "DLT_PPP_WITH_DIRECTION", + "DLT_PRISM_HEADER", + "DLT_PRONET", + "DLT_RAIF1", + "DLT_RAW", + "DLT_RAWAF_MASK", + "DLT_RIO", + "DLT_SCCP", + "DLT_SITA", + "DLT_SLIP", + "DLT_SLIP_BSDOS", + "DLT_STANAG_5066_D_PDU", + "DLT_SUNATM", + "DLT_SYMANTEC_FIREWALL", + "DLT_TZSP", + "DLT_USB", + "DLT_USB_LINUX", + "DLT_USB_LINUX_MMAPPED", + "DLT_USER0", + "DLT_USER1", + "DLT_USER10", + "DLT_USER11", + "DLT_USER12", + "DLT_USER13", + "DLT_USER14", + "DLT_USER15", + "DLT_USER2", + "DLT_USER3", + "DLT_USER4", + "DLT_USER5", + "DLT_USER6", + "DLT_USER7", + "DLT_USER8", + "DLT_USER9", + "DLT_WIHART", + "DLT_X2E_SERIAL", + "DLT_X2E_XORAYA", + "DNSMXData", + "DNSPTRData", + "DNSRecord", + "DNSSRVData", + "DNSTXTData", + "DNS_INFO_NO_RECORDS", + "DNS_TYPE_A", + "DNS_TYPE_A6", + "DNS_TYPE_AAAA", + "DNS_TYPE_ADDRS", + "DNS_TYPE_AFSDB", + "DNS_TYPE_ALL", + "DNS_TYPE_ANY", + "DNS_TYPE_ATMA", + "DNS_TYPE_AXFR", + "DNS_TYPE_CERT", + "DNS_TYPE_CNAME", + "DNS_TYPE_DHCID", + "DNS_TYPE_DNAME", + "DNS_TYPE_DNSKEY", + "DNS_TYPE_DS", + "DNS_TYPE_EID", + "DNS_TYPE_GID", + "DNS_TYPE_GPOS", + "DNS_TYPE_HINFO", + "DNS_TYPE_ISDN", + "DNS_TYPE_IXFR", + "DNS_TYPE_KEY", + "DNS_TYPE_KX", + "DNS_TYPE_LOC", + "DNS_TYPE_MAILA", + "DNS_TYPE_MAILB", + "DNS_TYPE_MB", + "DNS_TYPE_MD", + "DNS_TYPE_MF", + "DNS_TYPE_MG", + "DNS_TYPE_MINFO", + "DNS_TYPE_MR", + "DNS_TYPE_MX", + "DNS_TYPE_NAPTR", + "DNS_TYPE_NBSTAT", + "DNS_TYPE_NIMLOC", + "DNS_TYPE_NS", + "DNS_TYPE_NSAP", + "DNS_TYPE_NSAPPTR", + "DNS_TYPE_NSEC", + "DNS_TYPE_NULL", + "DNS_TYPE_NXT", + "DNS_TYPE_OPT", + "DNS_TYPE_PTR", + "DNS_TYPE_PX", + "DNS_TYPE_RP", + "DNS_TYPE_RRSIG", + "DNS_TYPE_RT", + "DNS_TYPE_SIG", + "DNS_TYPE_SINK", + "DNS_TYPE_SOA", + "DNS_TYPE_SRV", + "DNS_TYPE_TEXT", + "DNS_TYPE_TKEY", + "DNS_TYPE_TSIG", + "DNS_TYPE_UID", + "DNS_TYPE_UINFO", + "DNS_TYPE_UNSPEC", + "DNS_TYPE_WINS", + "DNS_TYPE_WINSR", + "DNS_TYPE_WKS", + "DNS_TYPE_X25", + "DT_BLK", + "DT_CHR", + "DT_DIR", + "DT_FIFO", + "DT_LNK", + "DT_REG", + "DT_SOCK", + "DT_UNKNOWN", + "DT_WHT", + "DUPLICATE_CLOSE_SOURCE", + "DUPLICATE_SAME_ACCESS", + "DeleteFile", + "DetachLsf", + "DeviceIoControl", + "Dirent", + "DnsNameCompare", + "DnsQuery", + "DnsRecordListFree", + "DnsSectionAdditional", + "DnsSectionAnswer", + "DnsSectionAuthority", + "DnsSectionQuestion", + "Dup", + "Dup2", + "Dup3", + "DuplicateHandle", + "E2BIG", + "EACCES", + "EADDRINUSE", + "EADDRNOTAVAIL", + "EADV", + "EAFNOSUPPORT", + "EAGAIN", + "EALREADY", + "EAUTH", + "EBADARCH", + "EBADE", + "EBADEXEC", + "EBADF", + "EBADFD", + "EBADMACHO", + "EBADMSG", + "EBADR", + "EBADRPC", + "EBADRQC", + "EBADSLT", + "EBFONT", + "EBUSY", + "ECANCELED", + "ECAPMODE", + "ECHILD", + "ECHO", + "ECHOCTL", + "ECHOE", + "ECHOK", + "ECHOKE", + "ECHONL", + "ECHOPRT", + "ECHRNG", + "ECOMM", + "ECONNABORTED", + "ECONNREFUSED", + "ECONNRESET", + "EDEADLK", + "EDEADLOCK", + "EDESTADDRREQ", + "EDEVERR", + "EDOM", + "EDOOFUS", + "EDOTDOT", + "EDQUOT", + "EEXIST", + "EFAULT", + "EFBIG", + "EFER_LMA", + "EFER_LME", + "EFER_NXE", + "EFER_SCE", + "EFTYPE", + "EHOSTDOWN", + "EHOSTUNREACH", + "EHWPOISON", + "EIDRM", + "EILSEQ", + "EINPROGRESS", + "EINTR", + "EINVAL", + "EIO", + "EIPSEC", + "EISCONN", + "EISDIR", + "EISNAM", + "EKEYEXPIRED", + "EKEYREJECTED", + "EKEYREVOKED", + "EL2HLT", + "EL2NSYNC", + "EL3HLT", + "EL3RST", + "ELAST", + "ELF_NGREG", + "ELF_PRARGSZ", + "ELIBACC", + "ELIBBAD", + "ELIBEXEC", + "ELIBMAX", + "ELIBSCN", + "ELNRNG", + "ELOOP", + "EMEDIUMTYPE", + "EMFILE", + "EMLINK", + "EMSGSIZE", + "EMT_TAGOVF", + "EMULTIHOP", + "EMUL_ENABLED", + "EMUL_LINUX", + "EMUL_LINUX32", + "EMUL_MAXID", + "EMUL_NATIVE", + "ENAMETOOLONG", + "ENAVAIL", + "ENDRUNDISC", + "ENEEDAUTH", + "ENETDOWN", + "ENETRESET", + "ENETUNREACH", + "ENFILE", + "ENOANO", + "ENOATTR", + "ENOBUFS", + "ENOCSI", + "ENODATA", + "ENODEV", + "ENOENT", + "ENOEXEC", + "ENOKEY", + "ENOLCK", + "ENOLINK", + "ENOMEDIUM", + "ENOMEM", + "ENOMSG", + "ENONET", + "ENOPKG", + "ENOPOLICY", + "ENOPROTOOPT", + "ENOSPC", + "ENOSR", + "ENOSTR", + "ENOSYS", + "ENOTBLK", + "ENOTCAPABLE", + "ENOTCONN", + "ENOTDIR", + "ENOTEMPTY", + "ENOTNAM", + "ENOTRECOVERABLE", + "ENOTSOCK", + "ENOTSUP", + "ENOTTY", + "ENOTUNIQ", + "ENXIO", + "EN_SW_CTL_INF", + "EN_SW_CTL_PREC", + "EN_SW_CTL_ROUND", + "EN_SW_DATACHAIN", + "EN_SW_DENORM", + "EN_SW_INVOP", + "EN_SW_OVERFLOW", + "EN_SW_PRECLOSS", + "EN_SW_UNDERFLOW", + "EN_SW_ZERODIV", + "EOPNOTSUPP", + "EOVERFLOW", + "EOWNERDEAD", + "EPERM", + "EPFNOSUPPORT", + "EPIPE", + "EPOLLERR", + "EPOLLET", + "EPOLLHUP", + "EPOLLIN", + "EPOLLMSG", + "EPOLLONESHOT", + "EPOLLOUT", + "EPOLLPRI", + "EPOLLRDBAND", + "EPOLLRDHUP", + "EPOLLRDNORM", + "EPOLLWRBAND", + "EPOLLWRNORM", + "EPOLL_CLOEXEC", + "EPOLL_CTL_ADD", + "EPOLL_CTL_DEL", + "EPOLL_CTL_MOD", + "EPOLL_NONBLOCK", + "EPROCLIM", + "EPROCUNAVAIL", + "EPROGMISMATCH", + "EPROGUNAVAIL", + "EPROTO", + "EPROTONOSUPPORT", + "EPROTOTYPE", + "EPWROFF", + "EQFULL", + "ERANGE", + "EREMCHG", + "EREMOTE", + "EREMOTEIO", + "ERESTART", + "ERFKILL", + "EROFS", + "ERPCMISMATCH", + "ERROR_ACCESS_DENIED", + "ERROR_ALREADY_EXISTS", + "ERROR_BROKEN_PIPE", + "ERROR_BUFFER_OVERFLOW", + "ERROR_DIR_NOT_EMPTY", + "ERROR_ENVVAR_NOT_FOUND", + "ERROR_FILE_EXISTS", + "ERROR_FILE_NOT_FOUND", + "ERROR_HANDLE_EOF", + "ERROR_INSUFFICIENT_BUFFER", + "ERROR_IO_PENDING", + "ERROR_MOD_NOT_FOUND", + "ERROR_MORE_DATA", + "ERROR_NETNAME_DELETED", + "ERROR_NOT_FOUND", + "ERROR_NO_MORE_FILES", + "ERROR_OPERATION_ABORTED", + "ERROR_PATH_NOT_FOUND", + "ERROR_PRIVILEGE_NOT_HELD", + "ERROR_PROC_NOT_FOUND", + "ESHLIBVERS", + "ESHUTDOWN", + "ESOCKTNOSUPPORT", + "ESPIPE", + "ESRCH", + "ESRMNT", + "ESTALE", + "ESTRPIPE", + "ETHERCAP_JUMBO_MTU", + "ETHERCAP_VLAN_HWTAGGING", + "ETHERCAP_VLAN_MTU", + "ETHERMIN", + "ETHERMTU", + "ETHERMTU_JUMBO", + "ETHERTYPE_8023", + "ETHERTYPE_AARP", + "ETHERTYPE_ACCTON", + "ETHERTYPE_AEONIC", + "ETHERTYPE_ALPHA", + "ETHERTYPE_AMBER", + "ETHERTYPE_AMOEBA", + "ETHERTYPE_AOE", + "ETHERTYPE_APOLLO", + "ETHERTYPE_APOLLODOMAIN", + "ETHERTYPE_APPLETALK", + "ETHERTYPE_APPLITEK", + "ETHERTYPE_ARGONAUT", + "ETHERTYPE_ARP", + "ETHERTYPE_AT", + "ETHERTYPE_ATALK", + "ETHERTYPE_ATOMIC", + "ETHERTYPE_ATT", + "ETHERTYPE_ATTSTANFORD", + "ETHERTYPE_AUTOPHON", + "ETHERTYPE_AXIS", + "ETHERTYPE_BCLOOP", + "ETHERTYPE_BOFL", + "ETHERTYPE_CABLETRON", + "ETHERTYPE_CHAOS", + "ETHERTYPE_COMDESIGN", + "ETHERTYPE_COMPUGRAPHIC", + "ETHERTYPE_COUNTERPOINT", + "ETHERTYPE_CRONUS", + "ETHERTYPE_CRONUSVLN", + "ETHERTYPE_DCA", + "ETHERTYPE_DDE", + "ETHERTYPE_DEBNI", + "ETHERTYPE_DECAM", + "ETHERTYPE_DECCUST", + "ETHERTYPE_DECDIAG", + "ETHERTYPE_DECDNS", + "ETHERTYPE_DECDTS", + "ETHERTYPE_DECEXPER", + "ETHERTYPE_DECLAST", + "ETHERTYPE_DECLTM", + "ETHERTYPE_DECMUMPS", + "ETHERTYPE_DECNETBIOS", + "ETHERTYPE_DELTACON", + "ETHERTYPE_DIDDLE", + "ETHERTYPE_DLOG1", + "ETHERTYPE_DLOG2", + "ETHERTYPE_DN", + "ETHERTYPE_DOGFIGHT", + "ETHERTYPE_DSMD", + "ETHERTYPE_ECMA", + "ETHERTYPE_ENCRYPT", + "ETHERTYPE_ES", + "ETHERTYPE_EXCELAN", + "ETHERTYPE_EXPERDATA", + "ETHERTYPE_FLIP", + "ETHERTYPE_FLOWCONTROL", + "ETHERTYPE_FRARP", + "ETHERTYPE_GENDYN", + "ETHERTYPE_HAYES", + "ETHERTYPE_HIPPI_FP", + "ETHERTYPE_HITACHI", + "ETHERTYPE_HP", + "ETHERTYPE_IEEEPUP", + "ETHERTYPE_IEEEPUPAT", + "ETHERTYPE_IMLBL", + "ETHERTYPE_IMLBLDIAG", + "ETHERTYPE_IP", + "ETHERTYPE_IPAS", + "ETHERTYPE_IPV6", + "ETHERTYPE_IPX", + "ETHERTYPE_IPXNEW", + "ETHERTYPE_KALPANA", + "ETHERTYPE_LANBRIDGE", + "ETHERTYPE_LANPROBE", + "ETHERTYPE_LAT", + "ETHERTYPE_LBACK", + "ETHERTYPE_LITTLE", + "ETHERTYPE_LLDP", + "ETHERTYPE_LOGICRAFT", + "ETHERTYPE_LOOPBACK", + "ETHERTYPE_MATRA", + "ETHERTYPE_MAX", + "ETHERTYPE_MERIT", + "ETHERTYPE_MICP", + "ETHERTYPE_MOPDL", + "ETHERTYPE_MOPRC", + "ETHERTYPE_MOTOROLA", + "ETHERTYPE_MPLS", + "ETHERTYPE_MPLS_MCAST", + "ETHERTYPE_MUMPS", + "ETHERTYPE_NBPCC", + "ETHERTYPE_NBPCLAIM", + "ETHERTYPE_NBPCLREQ", + "ETHERTYPE_NBPCLRSP", + "ETHERTYPE_NBPCREQ", + "ETHERTYPE_NBPCRSP", + "ETHERTYPE_NBPDG", + "ETHERTYPE_NBPDGB", + "ETHERTYPE_NBPDLTE", + "ETHERTYPE_NBPRAR", + "ETHERTYPE_NBPRAS", + "ETHERTYPE_NBPRST", + "ETHERTYPE_NBPSCD", + "ETHERTYPE_NBPVCD", + "ETHERTYPE_NBS", + "ETHERTYPE_NCD", + "ETHERTYPE_NESTAR", + "ETHERTYPE_NETBEUI", + "ETHERTYPE_NOVELL", + "ETHERTYPE_NS", + "ETHERTYPE_NSAT", + "ETHERTYPE_NSCOMPAT", + "ETHERTYPE_NTRAILER", + "ETHERTYPE_OS9", + "ETHERTYPE_OS9NET", + "ETHERTYPE_PACER", + "ETHERTYPE_PAE", + "ETHERTYPE_PCS", + "ETHERTYPE_PLANNING", + "ETHERTYPE_PPP", + "ETHERTYPE_PPPOE", + "ETHERTYPE_PPPOEDISC", + "ETHERTYPE_PRIMENTS", + "ETHERTYPE_PUP", + "ETHERTYPE_PUPAT", + "ETHERTYPE_QINQ", + "ETHERTYPE_RACAL", + "ETHERTYPE_RATIONAL", + "ETHERTYPE_RAWFR", + "ETHERTYPE_RCL", + "ETHERTYPE_RDP", + "ETHERTYPE_RETIX", + "ETHERTYPE_REVARP", + "ETHERTYPE_SCA", + "ETHERTYPE_SECTRA", + "ETHERTYPE_SECUREDATA", + "ETHERTYPE_SGITW", + "ETHERTYPE_SG_BOUNCE", + "ETHERTYPE_SG_DIAG", + "ETHERTYPE_SG_NETGAMES", + "ETHERTYPE_SG_RESV", + "ETHERTYPE_SIMNET", + "ETHERTYPE_SLOW", + "ETHERTYPE_SLOWPROTOCOLS", + "ETHERTYPE_SNA", + "ETHERTYPE_SNMP", + "ETHERTYPE_SONIX", + "ETHERTYPE_SPIDER", + "ETHERTYPE_SPRITE", + "ETHERTYPE_STP", + "ETHERTYPE_TALARIS", + "ETHERTYPE_TALARISMC", + "ETHERTYPE_TCPCOMP", + "ETHERTYPE_TCPSM", + "ETHERTYPE_TEC", + "ETHERTYPE_TIGAN", + "ETHERTYPE_TRAIL", + "ETHERTYPE_TRANSETHER", + "ETHERTYPE_TYMSHARE", + "ETHERTYPE_UBBST", + "ETHERTYPE_UBDEBUG", + "ETHERTYPE_UBDIAGLOOP", + "ETHERTYPE_UBDL", + "ETHERTYPE_UBNIU", + "ETHERTYPE_UBNMC", + "ETHERTYPE_VALID", + "ETHERTYPE_VARIAN", + "ETHERTYPE_VAXELN", + "ETHERTYPE_VEECO", + "ETHERTYPE_VEXP", + "ETHERTYPE_VGLAB", + "ETHERTYPE_VINES", + "ETHERTYPE_VINESECHO", + "ETHERTYPE_VINESLOOP", + "ETHERTYPE_VITAL", + "ETHERTYPE_VLAN", + "ETHERTYPE_VLTLMAN", + "ETHERTYPE_VPROD", + "ETHERTYPE_VURESERVED", + "ETHERTYPE_WATERLOO", + "ETHERTYPE_WELLFLEET", + "ETHERTYPE_X25", + "ETHERTYPE_X75", + "ETHERTYPE_XNSSM", + "ETHERTYPE_XTP", + "ETHER_ADDR_LEN", + "ETHER_ALIGN", + "ETHER_CRC_LEN", + "ETHER_CRC_POLY_BE", + "ETHER_CRC_POLY_LE", + "ETHER_HDR_LEN", + "ETHER_MAX_DIX_LEN", + "ETHER_MAX_LEN", + "ETHER_MAX_LEN_JUMBO", + "ETHER_MIN_LEN", + "ETHER_PPPOE_ENCAP_LEN", + "ETHER_TYPE_LEN", + "ETHER_VLAN_ENCAP_LEN", + "ETH_P_1588", + "ETH_P_8021Q", + "ETH_P_802_2", + "ETH_P_802_3", + "ETH_P_AARP", + "ETH_P_ALL", + "ETH_P_AOE", + "ETH_P_ARCNET", + "ETH_P_ARP", + "ETH_P_ATALK", + "ETH_P_ATMFATE", + "ETH_P_ATMMPOA", + "ETH_P_AX25", + "ETH_P_BPQ", + "ETH_P_CAIF", + "ETH_P_CAN", + "ETH_P_CONTROL", + "ETH_P_CUST", + "ETH_P_DDCMP", + "ETH_P_DEC", + "ETH_P_DIAG", + "ETH_P_DNA_DL", + "ETH_P_DNA_RC", + "ETH_P_DNA_RT", + "ETH_P_DSA", + "ETH_P_ECONET", + "ETH_P_EDSA", + "ETH_P_FCOE", + "ETH_P_FIP", + "ETH_P_HDLC", + "ETH_P_IEEE802154", + "ETH_P_IEEEPUP", + "ETH_P_IEEEPUPAT", + "ETH_P_IP", + "ETH_P_IPV6", + "ETH_P_IPX", + "ETH_P_IRDA", + "ETH_P_LAT", + "ETH_P_LINK_CTL", + "ETH_P_LOCALTALK", + "ETH_P_LOOP", + "ETH_P_MOBITEX", + "ETH_P_MPLS_MC", + "ETH_P_MPLS_UC", + "ETH_P_PAE", + "ETH_P_PAUSE", + "ETH_P_PHONET", + "ETH_P_PPPTALK", + "ETH_P_PPP_DISC", + "ETH_P_PPP_MP", + "ETH_P_PPP_SES", + "ETH_P_PUP", + "ETH_P_PUPAT", + "ETH_P_RARP", + "ETH_P_SCA", + "ETH_P_SLOW", + "ETH_P_SNAP", + "ETH_P_TEB", + "ETH_P_TIPC", + "ETH_P_TRAILER", + "ETH_P_TR_802_2", + "ETH_P_WAN_PPP", + "ETH_P_WCCP", + "ETH_P_X25", + "ETIME", + "ETIMEDOUT", + "ETOOMANYREFS", + "ETXTBSY", + "EUCLEAN", + "EUNATCH", + "EUSERS", + "EVFILT_AIO", + "EVFILT_FS", + "EVFILT_LIO", + "EVFILT_MACHPORT", + "EVFILT_PROC", + "EVFILT_READ", + "EVFILT_SIGNAL", + "EVFILT_SYSCOUNT", + "EVFILT_THREADMARKER", + "EVFILT_TIMER", + "EVFILT_USER", + "EVFILT_VM", + "EVFILT_VNODE", + "EVFILT_WRITE", + "EV_ADD", + "EV_CLEAR", + "EV_DELETE", + "EV_DISABLE", + "EV_DISPATCH", + "EV_DROP", + "EV_ENABLE", + "EV_EOF", + "EV_ERROR", + "EV_FLAG0", + "EV_FLAG1", + "EV_ONESHOT", + "EV_OOBAND", + "EV_POLL", + "EV_RECEIPT", + "EV_SYSFLAGS", + "EWINDOWS", + "EWOULDBLOCK", + "EXDEV", + "EXFULL", + "EXTA", + "EXTB", + "EXTPROC", + "Environ", + "EpollCreate", + "EpollCreate1", + "EpollCtl", + "EpollEvent", + "EpollWait", + "Errno", + "EscapeArg", + "Exchangedata", + "Exec", + "Exit", + "ExitProcess", + "FD_CLOEXEC", + "FD_SETSIZE", + "FILE_ACTION_ADDED", + "FILE_ACTION_MODIFIED", + "FILE_ACTION_REMOVED", + "FILE_ACTION_RENAMED_NEW_NAME", + "FILE_ACTION_RENAMED_OLD_NAME", + "FILE_APPEND_DATA", + "FILE_ATTRIBUTE_ARCHIVE", + "FILE_ATTRIBUTE_DIRECTORY", + "FILE_ATTRIBUTE_HIDDEN", + "FILE_ATTRIBUTE_NORMAL", + "FILE_ATTRIBUTE_READONLY", + "FILE_ATTRIBUTE_REPARSE_POINT", + "FILE_ATTRIBUTE_SYSTEM", + "FILE_BEGIN", + "FILE_CURRENT", + "FILE_END", + "FILE_FLAG_BACKUP_SEMANTICS", + "FILE_FLAG_OPEN_REPARSE_POINT", + "FILE_FLAG_OVERLAPPED", + "FILE_LIST_DIRECTORY", + "FILE_MAP_COPY", + "FILE_MAP_EXECUTE", + "FILE_MAP_READ", + "FILE_MAP_WRITE", + "FILE_NOTIFY_CHANGE_ATTRIBUTES", + "FILE_NOTIFY_CHANGE_CREATION", + "FILE_NOTIFY_CHANGE_DIR_NAME", + "FILE_NOTIFY_CHANGE_FILE_NAME", + "FILE_NOTIFY_CHANGE_LAST_ACCESS", + "FILE_NOTIFY_CHANGE_LAST_WRITE", + "FILE_NOTIFY_CHANGE_SIZE", + "FILE_SHARE_DELETE", + "FILE_SHARE_READ", + "FILE_SHARE_WRITE", + "FILE_SKIP_COMPLETION_PORT_ON_SUCCESS", + "FILE_SKIP_SET_EVENT_ON_HANDLE", + "FILE_TYPE_CHAR", + "FILE_TYPE_DISK", + "FILE_TYPE_PIPE", + "FILE_TYPE_REMOTE", + "FILE_TYPE_UNKNOWN", + "FILE_WRITE_ATTRIBUTES", + "FLUSHO", + "FORMAT_MESSAGE_ALLOCATE_BUFFER", + "FORMAT_MESSAGE_ARGUMENT_ARRAY", + "FORMAT_MESSAGE_FROM_HMODULE", + "FORMAT_MESSAGE_FROM_STRING", + "FORMAT_MESSAGE_FROM_SYSTEM", + "FORMAT_MESSAGE_IGNORE_INSERTS", + "FORMAT_MESSAGE_MAX_WIDTH_MASK", + "FSCTL_GET_REPARSE_POINT", + "F_ADDFILESIGS", + "F_ADDSIGS", + "F_ALLOCATEALL", + "F_ALLOCATECONTIG", + "F_CANCEL", + "F_CHKCLEAN", + "F_CLOSEM", + "F_DUP2FD", + "F_DUP2FD_CLOEXEC", + "F_DUPFD", + "F_DUPFD_CLOEXEC", + "F_EXLCK", + "F_FINDSIGS", + "F_FLUSH_DATA", + "F_FREEZE_FS", + "F_FSCTL", + "F_FSDIRMASK", + "F_FSIN", + "F_FSINOUT", + "F_FSOUT", + "F_FSPRIV", + "F_FSVOID", + "F_FULLFSYNC", + "F_GETCODEDIR", + "F_GETFD", + "F_GETFL", + "F_GETLEASE", + "F_GETLK", + "F_GETLK64", + "F_GETLKPID", + "F_GETNOSIGPIPE", + "F_GETOWN", + "F_GETOWN_EX", + "F_GETPATH", + "F_GETPATH_MTMINFO", + "F_GETPIPE_SZ", + "F_GETPROTECTIONCLASS", + "F_GETPROTECTIONLEVEL", + "F_GETSIG", + "F_GLOBAL_NOCACHE", + "F_LOCK", + "F_LOG2PHYS", + "F_LOG2PHYS_EXT", + "F_MARKDEPENDENCY", + "F_MAXFD", + "F_NOCACHE", + "F_NODIRECT", + "F_NOTIFY", + "F_OGETLK", + "F_OK", + "F_OSETLK", + "F_OSETLKW", + "F_PARAM_MASK", + "F_PARAM_MAX", + "F_PATHPKG_CHECK", + "F_PEOFPOSMODE", + "F_PREALLOCATE", + "F_RDADVISE", + "F_RDAHEAD", + "F_RDLCK", + "F_READAHEAD", + "F_READBOOTSTRAP", + "F_SETBACKINGSTORE", + "F_SETFD", + "F_SETFL", + "F_SETLEASE", + "F_SETLK", + "F_SETLK64", + "F_SETLKW", + "F_SETLKW64", + "F_SETLKWTIMEOUT", + "F_SETLK_REMOTE", + "F_SETNOSIGPIPE", + "F_SETOWN", + "F_SETOWN_EX", + "F_SETPIPE_SZ", + "F_SETPROTECTIONCLASS", + "F_SETSIG", + "F_SETSIZE", + "F_SHLCK", + "F_SINGLE_WRITER", + "F_TEST", + "F_THAW_FS", + "F_TLOCK", + "F_TRANSCODEKEY", + "F_ULOCK", + "F_UNLCK", + "F_UNLCKSYS", + "F_VOLPOSMODE", + "F_WRITEBOOTSTRAP", + "F_WRLCK", + "Faccessat", + "Fallocate", + "Fbootstraptransfer_t", + "Fchdir", + "Fchflags", + "Fchmod", + "Fchmodat", + "Fchown", + "Fchownat", + "FcntlFlock", + "FdSet", + "Fdatasync", + "FileNotifyInformation", + "Filetime", + "FindClose", + "FindFirstFile", + "FindNextFile", + "Flock", + "Flock_t", + "FlushBpf", + "FlushFileBuffers", + "FlushViewOfFile", + "ForkExec", + "ForkLock", + "FormatMessage", + "Fpathconf", + "FreeAddrInfoW", + "FreeEnvironmentStrings", + "FreeLibrary", + "Fsid", + "Fstat", + "Fstatat", + "Fstatfs", + "Fstore_t", + "Fsync", + "Ftruncate", + "FullPath", + "Futimes", + "Futimesat", + "GENERIC_ALL", + "GENERIC_EXECUTE", + "GENERIC_READ", + "GENERIC_WRITE", + "GUID", + "GetAcceptExSockaddrs", + "GetAdaptersInfo", + "GetAddrInfoW", + "GetCommandLine", + "GetComputerName", + "GetConsoleMode", + "GetCurrentDirectory", + "GetCurrentProcess", + "GetEnvironmentStrings", + "GetEnvironmentVariable", + "GetExitCodeProcess", + "GetFileAttributes", + "GetFileAttributesEx", + "GetFileExInfoStandard", + "GetFileExMaxInfoLevel", + "GetFileInformationByHandle", + "GetFileType", + "GetFullPathName", + "GetHostByName", + "GetIfEntry", + "GetLastError", + "GetLengthSid", + "GetLongPathName", + "GetProcAddress", + "GetProcessTimes", + "GetProtoByName", + "GetQueuedCompletionStatus", + "GetServByName", + "GetShortPathName", + "GetStartupInfo", + "GetStdHandle", + "GetSystemTimeAsFileTime", + "GetTempPath", + "GetTimeZoneInformation", + "GetTokenInformation", + "GetUserNameEx", + "GetUserProfileDirectory", + "GetVersion", + "Getcwd", + "Getdents", + "Getdirentries", + "Getdtablesize", + "Getegid", + "Getenv", + "Geteuid", + "Getfsstat", + "Getgid", + "Getgroups", + "Getpagesize", + "Getpeername", + "Getpgid", + "Getpgrp", + "Getpid", + "Getppid", + "Getpriority", + "Getrlimit", + "Getrusage", + "Getsid", + "Getsockname", + "Getsockopt", + "GetsockoptByte", + "GetsockoptICMPv6Filter", + "GetsockoptIPMreq", + "GetsockoptIPMreqn", + "GetsockoptIPv6MTUInfo", + "GetsockoptIPv6Mreq", + "GetsockoptInet4Addr", + "GetsockoptInt", + "GetsockoptUcred", + "Gettid", + "Gettimeofday", + "Getuid", + "Getwd", + "Getxattr", + "HANDLE_FLAG_INHERIT", + "HKEY_CLASSES_ROOT", + "HKEY_CURRENT_CONFIG", + "HKEY_CURRENT_USER", + "HKEY_DYN_DATA", + "HKEY_LOCAL_MACHINE", + "HKEY_PERFORMANCE_DATA", + "HKEY_USERS", + "HUPCL", + "Handle", + "Hostent", + "ICANON", + "ICMP6_FILTER", + "ICMPV6_FILTER", + "ICMPv6Filter", + "ICRNL", + "IEXTEN", + "IFAN_ARRIVAL", + "IFAN_DEPARTURE", + "IFA_ADDRESS", + "IFA_ANYCAST", + "IFA_BROADCAST", + "IFA_CACHEINFO", + "IFA_F_DADFAILED", + "IFA_F_DEPRECATED", + "IFA_F_HOMEADDRESS", + "IFA_F_NODAD", + "IFA_F_OPTIMISTIC", + "IFA_F_PERMANENT", + "IFA_F_SECONDARY", + "IFA_F_TEMPORARY", + "IFA_F_TENTATIVE", + "IFA_LABEL", + "IFA_LOCAL", + "IFA_MAX", + "IFA_MULTICAST", + "IFA_ROUTE", + "IFA_UNSPEC", + "IFF_ALLMULTI", + "IFF_ALTPHYS", + "IFF_AUTOMEDIA", + "IFF_BROADCAST", + "IFF_CANTCHANGE", + "IFF_CANTCONFIG", + "IFF_DEBUG", + "IFF_DRV_OACTIVE", + "IFF_DRV_RUNNING", + "IFF_DYING", + "IFF_DYNAMIC", + "IFF_LINK0", + "IFF_LINK1", + "IFF_LINK2", + "IFF_LOOPBACK", + "IFF_MASTER", + "IFF_MONITOR", + "IFF_MULTICAST", + "IFF_NOARP", + "IFF_NOTRAILERS", + "IFF_NO_PI", + "IFF_OACTIVE", + "IFF_ONE_QUEUE", + "IFF_POINTOPOINT", + "IFF_POINTTOPOINT", + "IFF_PORTSEL", + "IFF_PPROMISC", + "IFF_PROMISC", + "IFF_RENAMING", + "IFF_RUNNING", + "IFF_SIMPLEX", + "IFF_SLAVE", + "IFF_SMART", + "IFF_STATICARP", + "IFF_TAP", + "IFF_TUN", + "IFF_TUN_EXCL", + "IFF_UP", + "IFF_VNET_HDR", + "IFLA_ADDRESS", + "IFLA_BROADCAST", + "IFLA_COST", + "IFLA_IFALIAS", + "IFLA_IFNAME", + "IFLA_LINK", + "IFLA_LINKINFO", + "IFLA_LINKMODE", + "IFLA_MAP", + "IFLA_MASTER", + "IFLA_MAX", + "IFLA_MTU", + "IFLA_NET_NS_PID", + "IFLA_OPERSTATE", + "IFLA_PRIORITY", + "IFLA_PROTINFO", + "IFLA_QDISC", + "IFLA_STATS", + "IFLA_TXQLEN", + "IFLA_UNSPEC", + "IFLA_WEIGHT", + "IFLA_WIRELESS", + "IFNAMSIZ", + "IFT_1822", + "IFT_A12MPPSWITCH", + "IFT_AAL2", + "IFT_AAL5", + "IFT_ADSL", + "IFT_AFLANE8023", + "IFT_AFLANE8025", + "IFT_ARAP", + "IFT_ARCNET", + "IFT_ARCNETPLUS", + "IFT_ASYNC", + "IFT_ATM", + "IFT_ATMDXI", + "IFT_ATMFUNI", + "IFT_ATMIMA", + "IFT_ATMLOGICAL", + "IFT_ATMRADIO", + "IFT_ATMSUBINTERFACE", + "IFT_ATMVCIENDPT", + "IFT_ATMVIRTUAL", + "IFT_BGPPOLICYACCOUNTING", + "IFT_BLUETOOTH", + "IFT_BRIDGE", + "IFT_BSC", + "IFT_CARP", + "IFT_CCTEMUL", + "IFT_CELLULAR", + "IFT_CEPT", + "IFT_CES", + "IFT_CHANNEL", + "IFT_CNR", + "IFT_COFFEE", + "IFT_COMPOSITELINK", + "IFT_DCN", + "IFT_DIGITALPOWERLINE", + "IFT_DIGITALWRAPPEROVERHEADCHANNEL", + "IFT_DLSW", + "IFT_DOCSCABLEDOWNSTREAM", + "IFT_DOCSCABLEMACLAYER", + "IFT_DOCSCABLEUPSTREAM", + "IFT_DOCSCABLEUPSTREAMCHANNEL", + "IFT_DS0", + "IFT_DS0BUNDLE", + "IFT_DS1FDL", + "IFT_DS3", + "IFT_DTM", + "IFT_DUMMY", + "IFT_DVBASILN", + "IFT_DVBASIOUT", + "IFT_DVBRCCDOWNSTREAM", + "IFT_DVBRCCMACLAYER", + "IFT_DVBRCCUPSTREAM", + "IFT_ECONET", + "IFT_ENC", + "IFT_EON", + "IFT_EPLRS", + "IFT_ESCON", + "IFT_ETHER", + "IFT_FAITH", + "IFT_FAST", + "IFT_FASTETHER", + "IFT_FASTETHERFX", + "IFT_FDDI", + "IFT_FIBRECHANNEL", + "IFT_FRAMERELAYINTERCONNECT", + "IFT_FRAMERELAYMPI", + "IFT_FRDLCIENDPT", + "IFT_FRELAY", + "IFT_FRELAYDCE", + "IFT_FRF16MFRBUNDLE", + "IFT_FRFORWARD", + "IFT_G703AT2MB", + "IFT_G703AT64K", + "IFT_GIF", + "IFT_GIGABITETHERNET", + "IFT_GR303IDT", + "IFT_GR303RDT", + "IFT_H323GATEKEEPER", + "IFT_H323PROXY", + "IFT_HDH1822", + "IFT_HDLC", + "IFT_HDSL2", + "IFT_HIPERLAN2", + "IFT_HIPPI", + "IFT_HIPPIINTERFACE", + "IFT_HOSTPAD", + "IFT_HSSI", + "IFT_HY", + "IFT_IBM370PARCHAN", + "IFT_IDSL", + "IFT_IEEE1394", + "IFT_IEEE80211", + "IFT_IEEE80212", + "IFT_IEEE8023ADLAG", + "IFT_IFGSN", + "IFT_IMT", + "IFT_INFINIBAND", + "IFT_INTERLEAVE", + "IFT_IP", + "IFT_IPFORWARD", + "IFT_IPOVERATM", + "IFT_IPOVERCDLC", + "IFT_IPOVERCLAW", + "IFT_IPSWITCH", + "IFT_IPXIP", + "IFT_ISDN", + "IFT_ISDNBASIC", + "IFT_ISDNPRIMARY", + "IFT_ISDNS", + "IFT_ISDNU", + "IFT_ISO88022LLC", + "IFT_ISO88023", + "IFT_ISO88024", + "IFT_ISO88025", + "IFT_ISO88025CRFPINT", + "IFT_ISO88025DTR", + "IFT_ISO88025FIBER", + "IFT_ISO88026", + "IFT_ISUP", + "IFT_L2VLAN", + "IFT_L3IPVLAN", + "IFT_L3IPXVLAN", + "IFT_LAPB", + "IFT_LAPD", + "IFT_LAPF", + "IFT_LINEGROUP", + "IFT_LOCALTALK", + "IFT_LOOP", + "IFT_MEDIAMAILOVERIP", + "IFT_MFSIGLINK", + "IFT_MIOX25", + "IFT_MODEM", + "IFT_MPC", + "IFT_MPLS", + "IFT_MPLSTUNNEL", + "IFT_MSDSL", + "IFT_MVL", + "IFT_MYRINET", + "IFT_NFAS", + "IFT_NSIP", + "IFT_OPTICALCHANNEL", + "IFT_OPTICALTRANSPORT", + "IFT_OTHER", + "IFT_P10", + "IFT_P80", + "IFT_PARA", + "IFT_PDP", + "IFT_PFLOG", + "IFT_PFLOW", + "IFT_PFSYNC", + "IFT_PLC", + "IFT_PON155", + "IFT_PON622", + "IFT_POS", + "IFT_PPP", + "IFT_PPPMULTILINKBUNDLE", + "IFT_PROPATM", + "IFT_PROPBWAP2MP", + "IFT_PROPCNLS", + "IFT_PROPDOCSWIRELESSDOWNSTREAM", + "IFT_PROPDOCSWIRELESSMACLAYER", + "IFT_PROPDOCSWIRELESSUPSTREAM", + "IFT_PROPMUX", + "IFT_PROPVIRTUAL", + "IFT_PROPWIRELESSP2P", + "IFT_PTPSERIAL", + "IFT_PVC", + "IFT_Q2931", + "IFT_QLLC", + "IFT_RADIOMAC", + "IFT_RADSL", + "IFT_REACHDSL", + "IFT_RFC1483", + "IFT_RS232", + "IFT_RSRB", + "IFT_SDLC", + "IFT_SDSL", + "IFT_SHDSL", + "IFT_SIP", + "IFT_SIPSIG", + "IFT_SIPTG", + "IFT_SLIP", + "IFT_SMDSDXI", + "IFT_SMDSICIP", + "IFT_SONET", + "IFT_SONETOVERHEADCHANNEL", + "IFT_SONETPATH", + "IFT_SONETVT", + "IFT_SRP", + "IFT_SS7SIGLINK", + "IFT_STACKTOSTACK", + "IFT_STARLAN", + "IFT_STF", + "IFT_T1", + "IFT_TDLC", + "IFT_TELINK", + "IFT_TERMPAD", + "IFT_TR008", + "IFT_TRANSPHDLC", + "IFT_TUNNEL", + "IFT_ULTRA", + "IFT_USB", + "IFT_V11", + "IFT_V35", + "IFT_V36", + "IFT_V37", + "IFT_VDSL", + "IFT_VIRTUALIPADDRESS", + "IFT_VIRTUALTG", + "IFT_VOICEDID", + "IFT_VOICEEM", + "IFT_VOICEEMFGD", + "IFT_VOICEENCAP", + "IFT_VOICEFGDEANA", + "IFT_VOICEFXO", + "IFT_VOICEFXS", + "IFT_VOICEOVERATM", + "IFT_VOICEOVERCABLE", + "IFT_VOICEOVERFRAMERELAY", + "IFT_VOICEOVERIP", + "IFT_X213", + "IFT_X25", + "IFT_X25DDN", + "IFT_X25HUNTGROUP", + "IFT_X25MLP", + "IFT_X25PLE", + "IFT_XETHER", + "IGNBRK", + "IGNCR", + "IGNORE", + "IGNPAR", + "IMAXBEL", + "INFINITE", + "INLCR", + "INPCK", + "INVALID_FILE_ATTRIBUTES", + "IN_ACCESS", + "IN_ALL_EVENTS", + "IN_ATTRIB", + "IN_CLASSA_HOST", + "IN_CLASSA_MAX", + "IN_CLASSA_NET", + "IN_CLASSA_NSHIFT", + "IN_CLASSB_HOST", + "IN_CLASSB_MAX", + "IN_CLASSB_NET", + "IN_CLASSB_NSHIFT", + "IN_CLASSC_HOST", + "IN_CLASSC_NET", + "IN_CLASSC_NSHIFT", + "IN_CLASSD_HOST", + "IN_CLASSD_NET", + "IN_CLASSD_NSHIFT", + "IN_CLOEXEC", + "IN_CLOSE", + "IN_CLOSE_NOWRITE", + "IN_CLOSE_WRITE", + "IN_CREATE", + "IN_DELETE", + "IN_DELETE_SELF", + "IN_DONT_FOLLOW", + "IN_EXCL_UNLINK", + "IN_IGNORED", + "IN_ISDIR", + "IN_LINKLOCALNETNUM", + "IN_LOOPBACKNET", + "IN_MASK_ADD", + "IN_MODIFY", + "IN_MOVE", + "IN_MOVED_FROM", + "IN_MOVED_TO", + "IN_MOVE_SELF", + "IN_NONBLOCK", + "IN_ONESHOT", + "IN_ONLYDIR", + "IN_OPEN", + "IN_Q_OVERFLOW", + "IN_RFC3021_HOST", + "IN_RFC3021_MASK", + "IN_RFC3021_NET", + "IN_RFC3021_NSHIFT", + "IN_UNMOUNT", + "IOC_IN", + "IOC_INOUT", + "IOC_OUT", + "IOC_VENDOR", + "IOC_WS2", + "IO_REPARSE_TAG_SYMLINK", + "IPMreq", + "IPMreqn", + "IPPROTO_3PC", + "IPPROTO_ADFS", + "IPPROTO_AH", + "IPPROTO_AHIP", + "IPPROTO_APES", + "IPPROTO_ARGUS", + "IPPROTO_AX25", + "IPPROTO_BHA", + "IPPROTO_BLT", + "IPPROTO_BRSATMON", + "IPPROTO_CARP", + "IPPROTO_CFTP", + "IPPROTO_CHAOS", + "IPPROTO_CMTP", + "IPPROTO_COMP", + "IPPROTO_CPHB", + "IPPROTO_CPNX", + "IPPROTO_DCCP", + "IPPROTO_DDP", + "IPPROTO_DGP", + "IPPROTO_DIVERT", + "IPPROTO_DIVERT_INIT", + "IPPROTO_DIVERT_RESP", + "IPPROTO_DONE", + "IPPROTO_DSTOPTS", + "IPPROTO_EGP", + "IPPROTO_EMCON", + "IPPROTO_ENCAP", + "IPPROTO_EON", + "IPPROTO_ESP", + "IPPROTO_ETHERIP", + "IPPROTO_FRAGMENT", + "IPPROTO_GGP", + "IPPROTO_GMTP", + "IPPROTO_GRE", + "IPPROTO_HELLO", + "IPPROTO_HMP", + "IPPROTO_HOPOPTS", + "IPPROTO_ICMP", + "IPPROTO_ICMPV6", + "IPPROTO_IDP", + "IPPROTO_IDPR", + "IPPROTO_IDRP", + "IPPROTO_IGMP", + "IPPROTO_IGP", + "IPPROTO_IGRP", + "IPPROTO_IL", + "IPPROTO_INLSP", + "IPPROTO_INP", + "IPPROTO_IP", + "IPPROTO_IPCOMP", + "IPPROTO_IPCV", + "IPPROTO_IPEIP", + "IPPROTO_IPIP", + "IPPROTO_IPPC", + "IPPROTO_IPV4", + "IPPROTO_IPV6", + "IPPROTO_IPV6_ICMP", + "IPPROTO_IRTP", + "IPPROTO_KRYPTOLAN", + "IPPROTO_LARP", + "IPPROTO_LEAF1", + "IPPROTO_LEAF2", + "IPPROTO_MAX", + "IPPROTO_MAXID", + "IPPROTO_MEAS", + "IPPROTO_MH", + "IPPROTO_MHRP", + "IPPROTO_MICP", + "IPPROTO_MOBILE", + "IPPROTO_MPLS", + "IPPROTO_MTP", + "IPPROTO_MUX", + "IPPROTO_ND", + "IPPROTO_NHRP", + "IPPROTO_NONE", + "IPPROTO_NSP", + "IPPROTO_NVPII", + "IPPROTO_OLD_DIVERT", + "IPPROTO_OSPFIGP", + "IPPROTO_PFSYNC", + "IPPROTO_PGM", + "IPPROTO_PIGP", + "IPPROTO_PIM", + "IPPROTO_PRM", + "IPPROTO_PUP", + "IPPROTO_PVP", + "IPPROTO_RAW", + "IPPROTO_RCCMON", + "IPPROTO_RDP", + "IPPROTO_ROUTING", + "IPPROTO_RSVP", + "IPPROTO_RVD", + "IPPROTO_SATEXPAK", + "IPPROTO_SATMON", + "IPPROTO_SCCSP", + "IPPROTO_SCTP", + "IPPROTO_SDRP", + "IPPROTO_SEND", + "IPPROTO_SEP", + "IPPROTO_SKIP", + "IPPROTO_SPACER", + "IPPROTO_SRPC", + "IPPROTO_ST", + "IPPROTO_SVMTP", + "IPPROTO_SWIPE", + "IPPROTO_TCF", + "IPPROTO_TCP", + "IPPROTO_TLSP", + "IPPROTO_TP", + "IPPROTO_TPXX", + "IPPROTO_TRUNK1", + "IPPROTO_TRUNK2", + "IPPROTO_TTP", + "IPPROTO_UDP", + "IPPROTO_UDPLITE", + "IPPROTO_VINES", + "IPPROTO_VISA", + "IPPROTO_VMTP", + "IPPROTO_VRRP", + "IPPROTO_WBEXPAK", + "IPPROTO_WBMON", + "IPPROTO_WSN", + "IPPROTO_XNET", + "IPPROTO_XTP", + "IPV6_2292DSTOPTS", + "IPV6_2292HOPLIMIT", + "IPV6_2292HOPOPTS", + "IPV6_2292NEXTHOP", + "IPV6_2292PKTINFO", + "IPV6_2292PKTOPTIONS", + "IPV6_2292RTHDR", + "IPV6_ADDRFORM", + "IPV6_ADD_MEMBERSHIP", + "IPV6_AUTHHDR", + "IPV6_AUTH_LEVEL", + "IPV6_AUTOFLOWLABEL", + "IPV6_BINDANY", + "IPV6_BINDV6ONLY", + "IPV6_BOUND_IF", + "IPV6_CHECKSUM", + "IPV6_DEFAULT_MULTICAST_HOPS", + "IPV6_DEFAULT_MULTICAST_LOOP", + "IPV6_DEFHLIM", + "IPV6_DONTFRAG", + "IPV6_DROP_MEMBERSHIP", + "IPV6_DSTOPTS", + "IPV6_ESP_NETWORK_LEVEL", + "IPV6_ESP_TRANS_LEVEL", + "IPV6_FAITH", + "IPV6_FLOWINFO_MASK", + "IPV6_FLOWLABEL_MASK", + "IPV6_FRAGTTL", + "IPV6_FW_ADD", + "IPV6_FW_DEL", + "IPV6_FW_FLUSH", + "IPV6_FW_GET", + "IPV6_FW_ZERO", + "IPV6_HLIMDEC", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_IPCOMP_LEVEL", + "IPV6_IPSEC_POLICY", + "IPV6_JOIN_ANYCAST", + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_ANYCAST", + "IPV6_LEAVE_GROUP", + "IPV6_MAXHLIM", + "IPV6_MAXOPTHDR", + "IPV6_MAXPACKET", + "IPV6_MAX_GROUP_SRC_FILTER", + "IPV6_MAX_MEMBERSHIPS", + "IPV6_MAX_SOCK_SRC_FILTER", + "IPV6_MIN_MEMBERSHIPS", + "IPV6_MMTU", + "IPV6_MSFILTER", + "IPV6_MTU", + "IPV6_MTU_DISCOVER", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_NEXTHOP", + "IPV6_OPTIONS", + "IPV6_PATHMTU", + "IPV6_PIPEX", + "IPV6_PKTINFO", + "IPV6_PMTUDISC_DO", + "IPV6_PMTUDISC_DONT", + "IPV6_PMTUDISC_PROBE", + "IPV6_PMTUDISC_WANT", + "IPV6_PORTRANGE", + "IPV6_PORTRANGE_DEFAULT", + "IPV6_PORTRANGE_HIGH", + "IPV6_PORTRANGE_LOW", + "IPV6_PREFER_TEMPADDR", + "IPV6_RECVDSTOPTS", + "IPV6_RECVDSTPORT", + "IPV6_RECVERR", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPATHMTU", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_ROUTER_ALERT", + "IPV6_RTABLE", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_LOOSE", + "IPV6_RTHDR_STRICT", + "IPV6_RTHDR_TYPE_0", + "IPV6_RXDSTOPTS", + "IPV6_RXHOPOPTS", + "IPV6_SOCKOPT_RESERVED1", + "IPV6_TCLASS", + "IPV6_UNICAST_HOPS", + "IPV6_USE_MIN_MTU", + "IPV6_V6ONLY", + "IPV6_VERSION", + "IPV6_VERSION_MASK", + "IPV6_XFRM_POLICY", + "IP_ADD_MEMBERSHIP", + "IP_ADD_SOURCE_MEMBERSHIP", + "IP_AUTH_LEVEL", + "IP_BINDANY", + "IP_BLOCK_SOURCE", + "IP_BOUND_IF", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DF", + "IP_DIVERTFL", + "IP_DONTFRAG", + "IP_DROP_MEMBERSHIP", + "IP_DROP_SOURCE_MEMBERSHIP", + "IP_DUMMYNET3", + "IP_DUMMYNET_CONFIGURE", + "IP_DUMMYNET_DEL", + "IP_DUMMYNET_FLUSH", + "IP_DUMMYNET_GET", + "IP_EF", + "IP_ERRORMTU", + "IP_ESP_NETWORK_LEVEL", + "IP_ESP_TRANS_LEVEL", + "IP_FAITH", + "IP_FREEBIND", + "IP_FW3", + "IP_FW_ADD", + "IP_FW_DEL", + "IP_FW_FLUSH", + "IP_FW_GET", + "IP_FW_NAT_CFG", + "IP_FW_NAT_DEL", + "IP_FW_NAT_GET_CONFIG", + "IP_FW_NAT_GET_LOG", + "IP_FW_RESETLOG", + "IP_FW_TABLE_ADD", + "IP_FW_TABLE_DEL", + "IP_FW_TABLE_FLUSH", + "IP_FW_TABLE_GETSIZE", + "IP_FW_TABLE_LIST", + "IP_FW_ZERO", + "IP_HDRINCL", + "IP_IPCOMP_LEVEL", + "IP_IPSECFLOWINFO", + "IP_IPSEC_LOCAL_AUTH", + "IP_IPSEC_LOCAL_CRED", + "IP_IPSEC_LOCAL_ID", + "IP_IPSEC_POLICY", + "IP_IPSEC_REMOTE_AUTH", + "IP_IPSEC_REMOTE_CRED", + "IP_IPSEC_REMOTE_ID", + "IP_MAXPACKET", + "IP_MAX_GROUP_SRC_FILTER", + "IP_MAX_MEMBERSHIPS", + "IP_MAX_SOCK_MUTE_FILTER", + "IP_MAX_SOCK_SRC_FILTER", + "IP_MAX_SOURCE_FILTER", + "IP_MF", + "IP_MINFRAGSIZE", + "IP_MINTTL", + "IP_MIN_MEMBERSHIPS", + "IP_MSFILTER", + "IP_MSS", + "IP_MTU", + "IP_MTU_DISCOVER", + "IP_MULTICAST_IF", + "IP_MULTICAST_IFINDEX", + "IP_MULTICAST_LOOP", + "IP_MULTICAST_TTL", + "IP_MULTICAST_VIF", + "IP_NAT__XXX", + "IP_OFFMASK", + "IP_OLD_FW_ADD", + "IP_OLD_FW_DEL", + "IP_OLD_FW_FLUSH", + "IP_OLD_FW_GET", + "IP_OLD_FW_RESETLOG", + "IP_OLD_FW_ZERO", + "IP_ONESBCAST", + "IP_OPTIONS", + "IP_ORIGDSTADDR", + "IP_PASSSEC", + "IP_PIPEX", + "IP_PKTINFO", + "IP_PKTOPTIONS", + "IP_PMTUDISC", + "IP_PMTUDISC_DO", + "IP_PMTUDISC_DONT", + "IP_PMTUDISC_PROBE", + "IP_PMTUDISC_WANT", + "IP_PORTRANGE", + "IP_PORTRANGE_DEFAULT", + "IP_PORTRANGE_HIGH", + "IP_PORTRANGE_LOW", + "IP_RECVDSTADDR", + "IP_RECVDSTPORT", + "IP_RECVERR", + "IP_RECVIF", + "IP_RECVOPTS", + "IP_RECVORIGDSTADDR", + "IP_RECVPKTINFO", + "IP_RECVRETOPTS", + "IP_RECVRTABLE", + "IP_RECVTOS", + "IP_RECVTTL", + "IP_RETOPTS", + "IP_RF", + "IP_ROUTER_ALERT", + "IP_RSVP_OFF", + "IP_RSVP_ON", + "IP_RSVP_VIF_OFF", + "IP_RSVP_VIF_ON", + "IP_RTABLE", + "IP_SENDSRCADDR", + "IP_STRIPHDR", + "IP_TOS", + "IP_TRAFFIC_MGT_BACKGROUND", + "IP_TRANSPARENT", + "IP_TTL", + "IP_UNBLOCK_SOURCE", + "IP_XFRM_POLICY", + "IPv6MTUInfo", + "IPv6Mreq", + "ISIG", + "ISTRIP", + "IUCLC", + "IUTF8", + "IXANY", + "IXOFF", + "IXON", + "IfAddrmsg", + "IfAnnounceMsghdr", + "IfData", + "IfInfomsg", + "IfMsghdr", + "IfaMsghdr", + "IfmaMsghdr", + "IfmaMsghdr2", + "ImplementsGetwd", + "Inet4Pktinfo", + "Inet6Pktinfo", + "InotifyAddWatch", + "InotifyEvent", + "InotifyInit", + "InotifyInit1", + "InotifyRmWatch", + "InterfaceAddrMessage", + "InterfaceAnnounceMessage", + "InterfaceInfo", + "InterfaceMessage", + "InterfaceMulticastAddrMessage", + "InvalidHandle", + "Ioperm", + "Iopl", + "Iovec", + "IpAdapterInfo", + "IpAddrString", + "IpAddressString", + "IpMaskString", + "Issetugid", + "KEY_ALL_ACCESS", + "KEY_CREATE_LINK", + "KEY_CREATE_SUB_KEY", + "KEY_ENUMERATE_SUB_KEYS", + "KEY_EXECUTE", + "KEY_NOTIFY", + "KEY_QUERY_VALUE", + "KEY_READ", + "KEY_SET_VALUE", + "KEY_WOW64_32KEY", + "KEY_WOW64_64KEY", + "KEY_WRITE", + "Kevent", + "Kevent_t", + "Kill", + "Klogctl", + "Kqueue", + "LANG_ENGLISH", + "LAYERED_PROTOCOL", + "LCNT_OVERLOAD_FLUSH", + "LINUX_REBOOT_CMD_CAD_OFF", + "LINUX_REBOOT_CMD_CAD_ON", + "LINUX_REBOOT_CMD_HALT", + "LINUX_REBOOT_CMD_KEXEC", + "LINUX_REBOOT_CMD_POWER_OFF", + "LINUX_REBOOT_CMD_RESTART", + "LINUX_REBOOT_CMD_RESTART2", + "LINUX_REBOOT_CMD_SW_SUSPEND", + "LINUX_REBOOT_MAGIC1", + "LINUX_REBOOT_MAGIC2", + "LOCK_EX", + "LOCK_NB", + "LOCK_SH", + "LOCK_UN", + "LazyDLL", + "LazyProc", + "Lchown", + "Linger", + "Link", + "Listen", + "Listxattr", + "LoadCancelIoEx", + "LoadConnectEx", + "LoadCreateSymbolicLink", + "LoadDLL", + "LoadGetAddrInfo", + "LoadLibrary", + "LoadSetFileCompletionNotificationModes", + "LocalFree", + "Log2phys_t", + "LookupAccountName", + "LookupAccountSid", + "LookupSID", + "LsfJump", + "LsfSocket", + "LsfStmt", + "Lstat", + "MADV_AUTOSYNC", + "MADV_CAN_REUSE", + "MADV_CORE", + "MADV_DOFORK", + "MADV_DONTFORK", + "MADV_DONTNEED", + "MADV_FREE", + "MADV_FREE_REUSABLE", + "MADV_FREE_REUSE", + "MADV_HUGEPAGE", + "MADV_HWPOISON", + "MADV_MERGEABLE", + "MADV_NOCORE", + "MADV_NOHUGEPAGE", + "MADV_NORMAL", + "MADV_NOSYNC", + "MADV_PROTECT", + "MADV_RANDOM", + "MADV_REMOVE", + "MADV_SEQUENTIAL", + "MADV_SPACEAVAIL", + "MADV_UNMERGEABLE", + "MADV_WILLNEED", + "MADV_ZERO_WIRED_PAGES", + "MAP_32BIT", + "MAP_ALIGNED_SUPER", + "MAP_ALIGNMENT_16MB", + "MAP_ALIGNMENT_1TB", + "MAP_ALIGNMENT_256TB", + "MAP_ALIGNMENT_4GB", + "MAP_ALIGNMENT_64KB", + "MAP_ALIGNMENT_64PB", + "MAP_ALIGNMENT_MASK", + "MAP_ALIGNMENT_SHIFT", + "MAP_ANON", + "MAP_ANONYMOUS", + "MAP_COPY", + "MAP_DENYWRITE", + "MAP_EXECUTABLE", + "MAP_FILE", + "MAP_FIXED", + "MAP_FLAGMASK", + "MAP_GROWSDOWN", + "MAP_HASSEMAPHORE", + "MAP_HUGETLB", + "MAP_INHERIT", + "MAP_INHERIT_COPY", + "MAP_INHERIT_DEFAULT", + "MAP_INHERIT_DONATE_COPY", + "MAP_INHERIT_NONE", + "MAP_INHERIT_SHARE", + "MAP_JIT", + "MAP_LOCKED", + "MAP_NOCACHE", + "MAP_NOCORE", + "MAP_NOEXTEND", + "MAP_NONBLOCK", + "MAP_NORESERVE", + "MAP_NOSYNC", + "MAP_POPULATE", + "MAP_PREFAULT_READ", + "MAP_PRIVATE", + "MAP_RENAME", + "MAP_RESERVED0080", + "MAP_RESERVED0100", + "MAP_SHARED", + "MAP_STACK", + "MAP_TRYFIXED", + "MAP_TYPE", + "MAP_WIRED", + "MAXIMUM_REPARSE_DATA_BUFFER_SIZE", + "MAXLEN_IFDESCR", + "MAXLEN_PHYSADDR", + "MAX_ADAPTER_ADDRESS_LENGTH", + "MAX_ADAPTER_DESCRIPTION_LENGTH", + "MAX_ADAPTER_NAME_LENGTH", + "MAX_COMPUTERNAME_LENGTH", + "MAX_INTERFACE_NAME_LEN", + "MAX_LONG_PATH", + "MAX_PATH", + "MAX_PROTOCOL_CHAIN", + "MCL_CURRENT", + "MCL_FUTURE", + "MNT_DETACH", + "MNT_EXPIRE", + "MNT_FORCE", + "MSG_BCAST", + "MSG_CMSG_CLOEXEC", + "MSG_COMPAT", + "MSG_CONFIRM", + "MSG_CONTROLMBUF", + "MSG_CTRUNC", + "MSG_DONTROUTE", + "MSG_DONTWAIT", + "MSG_EOF", + "MSG_EOR", + "MSG_ERRQUEUE", + "MSG_FASTOPEN", + "MSG_FIN", + "MSG_FLUSH", + "MSG_HAVEMORE", + "MSG_HOLD", + "MSG_IOVUSRSPACE", + "MSG_LENUSRSPACE", + "MSG_MCAST", + "MSG_MORE", + "MSG_NAMEMBUF", + "MSG_NBIO", + "MSG_NEEDSA", + "MSG_NOSIGNAL", + "MSG_NOTIFICATION", + "MSG_OOB", + "MSG_PEEK", + "MSG_PROXY", + "MSG_RCVMORE", + "MSG_RST", + "MSG_SEND", + "MSG_SYN", + "MSG_TRUNC", + "MSG_TRYHARD", + "MSG_USERFLAGS", + "MSG_WAITALL", + "MSG_WAITFORONE", + "MSG_WAITSTREAM", + "MS_ACTIVE", + "MS_ASYNC", + "MS_BIND", + "MS_DEACTIVATE", + "MS_DIRSYNC", + "MS_INVALIDATE", + "MS_I_VERSION", + "MS_KERNMOUNT", + "MS_KILLPAGES", + "MS_MANDLOCK", + "MS_MGC_MSK", + "MS_MGC_VAL", + "MS_MOVE", + "MS_NOATIME", + "MS_NODEV", + "MS_NODIRATIME", + "MS_NOEXEC", + "MS_NOSUID", + "MS_NOUSER", + "MS_POSIXACL", + "MS_PRIVATE", + "MS_RDONLY", + "MS_REC", + "MS_RELATIME", + "MS_REMOUNT", + "MS_RMT_MASK", + "MS_SHARED", + "MS_SILENT", + "MS_SLAVE", + "MS_STRICTATIME", + "MS_SYNC", + "MS_SYNCHRONOUS", + "MS_UNBINDABLE", + "Madvise", + "MapViewOfFile", + "MaxTokenInfoClass", + "Mclpool", + "MibIfRow", + "Mkdir", + "Mkdirat", + "Mkfifo", + "Mknod", + "Mknodat", + "Mlock", + "Mlockall", + "Mmap", + "Mount", + "MoveFile", + "Mprotect", + "Msghdr", + "Munlock", + "Munlockall", + "Munmap", + "MustLoadDLL", + "NAME_MAX", + "NETLINK_ADD_MEMBERSHIP", + "NETLINK_AUDIT", + "NETLINK_BROADCAST_ERROR", + "NETLINK_CONNECTOR", + "NETLINK_DNRTMSG", + "NETLINK_DROP_MEMBERSHIP", + "NETLINK_ECRYPTFS", + "NETLINK_FIB_LOOKUP", + "NETLINK_FIREWALL", + "NETLINK_GENERIC", + "NETLINK_INET_DIAG", + "NETLINK_IP6_FW", + "NETLINK_ISCSI", + "NETLINK_KOBJECT_UEVENT", + "NETLINK_NETFILTER", + "NETLINK_NFLOG", + "NETLINK_NO_ENOBUFS", + "NETLINK_PKTINFO", + "NETLINK_RDMA", + "NETLINK_ROUTE", + "NETLINK_SCSITRANSPORT", + "NETLINK_SELINUX", + "NETLINK_UNUSED", + "NETLINK_USERSOCK", + "NETLINK_XFRM", + "NET_RT_DUMP", + "NET_RT_DUMP2", + "NET_RT_FLAGS", + "NET_RT_IFLIST", + "NET_RT_IFLIST2", + "NET_RT_IFLISTL", + "NET_RT_IFMALIST", + "NET_RT_MAXID", + "NET_RT_OIFLIST", + "NET_RT_OOIFLIST", + "NET_RT_STAT", + "NET_RT_STATS", + "NET_RT_TABLE", + "NET_RT_TRASH", + "NLA_ALIGNTO", + "NLA_F_NESTED", + "NLA_F_NET_BYTEORDER", + "NLA_HDRLEN", + "NLMSG_ALIGNTO", + "NLMSG_DONE", + "NLMSG_ERROR", + "NLMSG_HDRLEN", + "NLMSG_MIN_TYPE", + "NLMSG_NOOP", + "NLMSG_OVERRUN", + "NLM_F_ACK", + "NLM_F_APPEND", + "NLM_F_ATOMIC", + "NLM_F_CREATE", + "NLM_F_DUMP", + "NLM_F_ECHO", + "NLM_F_EXCL", + "NLM_F_MATCH", + "NLM_F_MULTI", + "NLM_F_REPLACE", + "NLM_F_REQUEST", + "NLM_F_ROOT", + "NOFLSH", + "NOTE_ABSOLUTE", + "NOTE_ATTRIB", + "NOTE_BACKGROUND", + "NOTE_CHILD", + "NOTE_CRITICAL", + "NOTE_DELETE", + "NOTE_EOF", + "NOTE_EXEC", + "NOTE_EXIT", + "NOTE_EXITSTATUS", + "NOTE_EXIT_CSERROR", + "NOTE_EXIT_DECRYPTFAIL", + "NOTE_EXIT_DETAIL", + "NOTE_EXIT_DETAIL_MASK", + "NOTE_EXIT_MEMORY", + "NOTE_EXIT_REPARENTED", + "NOTE_EXTEND", + "NOTE_FFAND", + "NOTE_FFCOPY", + "NOTE_FFCTRLMASK", + "NOTE_FFLAGSMASK", + "NOTE_FFNOP", + "NOTE_FFOR", + "NOTE_FORK", + "NOTE_LEEWAY", + "NOTE_LINK", + "NOTE_LOWAT", + "NOTE_NONE", + "NOTE_NSECONDS", + "NOTE_PCTRLMASK", + "NOTE_PDATAMASK", + "NOTE_REAP", + "NOTE_RENAME", + "NOTE_RESOURCEEND", + "NOTE_REVOKE", + "NOTE_SECONDS", + "NOTE_SIGNAL", + "NOTE_TRACK", + "NOTE_TRACKERR", + "NOTE_TRIGGER", + "NOTE_TRUNCATE", + "NOTE_USECONDS", + "NOTE_VM_ERROR", + "NOTE_VM_PRESSURE", + "NOTE_VM_PRESSURE_SUDDEN_TERMINATE", + "NOTE_VM_PRESSURE_TERMINATE", + "NOTE_WRITE", + "NameCanonical", + "NameCanonicalEx", + "NameDisplay", + "NameDnsDomain", + "NameFullyQualifiedDN", + "NameSamCompatible", + "NameServicePrincipal", + "NameUniqueId", + "NameUnknown", + "NameUserPrincipal", + "Nanosleep", + "NetApiBufferFree", + "NetGetJoinInformation", + "NetSetupDomainName", + "NetSetupUnjoined", + "NetSetupUnknownStatus", + "NetSetupWorkgroupName", + "NetUserGetInfo", + "NetlinkMessage", + "NetlinkRIB", + "NetlinkRouteAttr", + "NetlinkRouteRequest", + "NewCallback", + "NewCallbackCDecl", + "NewLazyDLL", + "NlAttr", + "NlMsgerr", + "NlMsghdr", + "NsecToFiletime", + "NsecToTimespec", + "NsecToTimeval", + "Ntohs", + "OCRNL", + "OFDEL", + "OFILL", + "OFIOGETBMAP", + "OID_PKIX_KP_SERVER_AUTH", + "OID_SERVER_GATED_CRYPTO", + "OID_SGC_NETSCAPE", + "OLCUC", + "ONLCR", + "ONLRET", + "ONOCR", + "ONOEOT", + "OPEN_ALWAYS", + "OPEN_EXISTING", + "OPOST", + "O_ACCMODE", + "O_ALERT", + "O_ALT_IO", + "O_APPEND", + "O_ASYNC", + "O_CLOEXEC", + "O_CREAT", + "O_DIRECT", + "O_DIRECTORY", + "O_DP_GETRAWENCRYPTED", + "O_DSYNC", + "O_EVTONLY", + "O_EXCL", + "O_EXEC", + "O_EXLOCK", + "O_FSYNC", + "O_LARGEFILE", + "O_NDELAY", + "O_NOATIME", + "O_NOCTTY", + "O_NOFOLLOW", + "O_NONBLOCK", + "O_NOSIGPIPE", + "O_POPUP", + "O_RDONLY", + "O_RDWR", + "O_RSYNC", + "O_SHLOCK", + "O_SYMLINK", + "O_SYNC", + "O_TRUNC", + "O_TTY_INIT", + "O_WRONLY", + "Open", + "OpenCurrentProcessToken", + "OpenProcess", + "OpenProcessToken", + "Openat", + "Overlapped", + "PACKET_ADD_MEMBERSHIP", + "PACKET_BROADCAST", + "PACKET_DROP_MEMBERSHIP", + "PACKET_FASTROUTE", + "PACKET_HOST", + "PACKET_LOOPBACK", + "PACKET_MR_ALLMULTI", + "PACKET_MR_MULTICAST", + "PACKET_MR_PROMISC", + "PACKET_MULTICAST", + "PACKET_OTHERHOST", + "PACKET_OUTGOING", + "PACKET_RECV_OUTPUT", + "PACKET_RX_RING", + "PACKET_STATISTICS", + "PAGE_EXECUTE_READ", + "PAGE_EXECUTE_READWRITE", + "PAGE_EXECUTE_WRITECOPY", + "PAGE_READONLY", + "PAGE_READWRITE", + "PAGE_WRITECOPY", + "PARENB", + "PARMRK", + "PARODD", + "PENDIN", + "PFL_HIDDEN", + "PFL_MATCHES_PROTOCOL_ZERO", + "PFL_MULTIPLE_PROTO_ENTRIES", + "PFL_NETWORKDIRECT_PROVIDER", + "PFL_RECOMMENDED_PROTO_ENTRY", + "PF_FLUSH", + "PKCS_7_ASN_ENCODING", + "PMC5_PIPELINE_FLUSH", + "PRIO_PGRP", + "PRIO_PROCESS", + "PRIO_USER", + "PRI_IOFLUSH", + "PROCESS_QUERY_INFORMATION", + "PROCESS_TERMINATE", + "PROT_EXEC", + "PROT_GROWSDOWN", + "PROT_GROWSUP", + "PROT_NONE", + "PROT_READ", + "PROT_WRITE", + "PROV_DH_SCHANNEL", + "PROV_DSS", + "PROV_DSS_DH", + "PROV_EC_ECDSA_FULL", + "PROV_EC_ECDSA_SIG", + "PROV_EC_ECNRA_FULL", + "PROV_EC_ECNRA_SIG", + "PROV_FORTEZZA", + "PROV_INTEL_SEC", + "PROV_MS_EXCHANGE", + "PROV_REPLACE_OWF", + "PROV_RNG", + "PROV_RSA_AES", + "PROV_RSA_FULL", + "PROV_RSA_SCHANNEL", + "PROV_RSA_SIG", + "PROV_SPYRUS_LYNKS", + "PROV_SSL", + "PR_CAPBSET_DROP", + "PR_CAPBSET_READ", + "PR_CLEAR_SECCOMP_FILTER", + "PR_ENDIAN_BIG", + "PR_ENDIAN_LITTLE", + "PR_ENDIAN_PPC_LITTLE", + "PR_FPEMU_NOPRINT", + "PR_FPEMU_SIGFPE", + "PR_FP_EXC_ASYNC", + "PR_FP_EXC_DISABLED", + "PR_FP_EXC_DIV", + "PR_FP_EXC_INV", + "PR_FP_EXC_NONRECOV", + "PR_FP_EXC_OVF", + "PR_FP_EXC_PRECISE", + "PR_FP_EXC_RES", + "PR_FP_EXC_SW_ENABLE", + "PR_FP_EXC_UND", + "PR_GET_DUMPABLE", + "PR_GET_ENDIAN", + "PR_GET_FPEMU", + "PR_GET_FPEXC", + "PR_GET_KEEPCAPS", + "PR_GET_NAME", + "PR_GET_PDEATHSIG", + "PR_GET_SECCOMP", + "PR_GET_SECCOMP_FILTER", + "PR_GET_SECUREBITS", + "PR_GET_TIMERSLACK", + "PR_GET_TIMING", + "PR_GET_TSC", + "PR_GET_UNALIGN", + "PR_MCE_KILL", + "PR_MCE_KILL_CLEAR", + "PR_MCE_KILL_DEFAULT", + "PR_MCE_KILL_EARLY", + "PR_MCE_KILL_GET", + "PR_MCE_KILL_LATE", + "PR_MCE_KILL_SET", + "PR_SECCOMP_FILTER_EVENT", + "PR_SECCOMP_FILTER_SYSCALL", + "PR_SET_DUMPABLE", + "PR_SET_ENDIAN", + "PR_SET_FPEMU", + "PR_SET_FPEXC", + "PR_SET_KEEPCAPS", + "PR_SET_NAME", + "PR_SET_PDEATHSIG", + "PR_SET_PTRACER", + "PR_SET_SECCOMP", + "PR_SET_SECCOMP_FILTER", + "PR_SET_SECUREBITS", + "PR_SET_TIMERSLACK", + "PR_SET_TIMING", + "PR_SET_TSC", + "PR_SET_UNALIGN", + "PR_TASK_PERF_EVENTS_DISABLE", + "PR_TASK_PERF_EVENTS_ENABLE", + "PR_TIMING_STATISTICAL", + "PR_TIMING_TIMESTAMP", + "PR_TSC_ENABLE", + "PR_TSC_SIGSEGV", + "PR_UNALIGN_NOPRINT", + "PR_UNALIGN_SIGBUS", + "PTRACE_ARCH_PRCTL", + "PTRACE_ATTACH", + "PTRACE_CONT", + "PTRACE_DETACH", + "PTRACE_EVENT_CLONE", + "PTRACE_EVENT_EXEC", + "PTRACE_EVENT_EXIT", + "PTRACE_EVENT_FORK", + "PTRACE_EVENT_VFORK", + "PTRACE_EVENT_VFORK_DONE", + "PTRACE_GETCRUNCHREGS", + "PTRACE_GETEVENTMSG", + "PTRACE_GETFPREGS", + "PTRACE_GETFPXREGS", + "PTRACE_GETHBPREGS", + "PTRACE_GETREGS", + "PTRACE_GETREGSET", + "PTRACE_GETSIGINFO", + "PTRACE_GETVFPREGS", + "PTRACE_GETWMMXREGS", + "PTRACE_GET_THREAD_AREA", + "PTRACE_KILL", + "PTRACE_OLDSETOPTIONS", + "PTRACE_O_MASK", + "PTRACE_O_TRACECLONE", + "PTRACE_O_TRACEEXEC", + "PTRACE_O_TRACEEXIT", + "PTRACE_O_TRACEFORK", + "PTRACE_O_TRACESYSGOOD", + "PTRACE_O_TRACEVFORK", + "PTRACE_O_TRACEVFORKDONE", + "PTRACE_PEEKDATA", + "PTRACE_PEEKTEXT", + "PTRACE_PEEKUSR", + "PTRACE_POKEDATA", + "PTRACE_POKETEXT", + "PTRACE_POKEUSR", + "PTRACE_SETCRUNCHREGS", + "PTRACE_SETFPREGS", + "PTRACE_SETFPXREGS", + "PTRACE_SETHBPREGS", + "PTRACE_SETOPTIONS", + "PTRACE_SETREGS", + "PTRACE_SETREGSET", + "PTRACE_SETSIGINFO", + "PTRACE_SETVFPREGS", + "PTRACE_SETWMMXREGS", + "PTRACE_SET_SYSCALL", + "PTRACE_SET_THREAD_AREA", + "PTRACE_SINGLEBLOCK", + "PTRACE_SINGLESTEP", + "PTRACE_SYSCALL", + "PTRACE_SYSEMU", + "PTRACE_SYSEMU_SINGLESTEP", + "PTRACE_TRACEME", + "PT_ATTACH", + "PT_ATTACHEXC", + "PT_CONTINUE", + "PT_DATA_ADDR", + "PT_DENY_ATTACH", + "PT_DETACH", + "PT_FIRSTMACH", + "PT_FORCEQUOTA", + "PT_KILL", + "PT_MASK", + "PT_READ_D", + "PT_READ_I", + "PT_READ_U", + "PT_SIGEXC", + "PT_STEP", + "PT_TEXT_ADDR", + "PT_TEXT_END_ADDR", + "PT_THUPDATE", + "PT_TRACE_ME", + "PT_WRITE_D", + "PT_WRITE_I", + "PT_WRITE_U", + "ParseDirent", + "ParseNetlinkMessage", + "ParseNetlinkRouteAttr", + "ParseRoutingMessage", + "ParseRoutingSockaddr", + "ParseSocketControlMessage", + "ParseUnixCredentials", + "ParseUnixRights", + "PathMax", + "Pathconf", + "Pause", + "Pipe", + "Pipe2", + "PivotRoot", + "Pointer", + "PostQueuedCompletionStatus", + "Pread", + "Proc", + "ProcAttr", + "Process32First", + "Process32Next", + "ProcessEntry32", + "ProcessInformation", + "Protoent", + "PtraceAttach", + "PtraceCont", + "PtraceDetach", + "PtraceGetEventMsg", + "PtraceGetRegs", + "PtracePeekData", + "PtracePeekText", + "PtracePokeData", + "PtracePokeText", + "PtraceRegs", + "PtraceSetOptions", + "PtraceSetRegs", + "PtraceSingleStep", + "PtraceSyscall", + "Pwrite", + "REG_BINARY", + "REG_DWORD", + "REG_DWORD_BIG_ENDIAN", + "REG_DWORD_LITTLE_ENDIAN", + "REG_EXPAND_SZ", + "REG_FULL_RESOURCE_DESCRIPTOR", + "REG_LINK", + "REG_MULTI_SZ", + "REG_NONE", + "REG_QWORD", + "REG_QWORD_LITTLE_ENDIAN", + "REG_RESOURCE_LIST", + "REG_RESOURCE_REQUIREMENTS_LIST", + "REG_SZ", + "RLIMIT_AS", + "RLIMIT_CORE", + "RLIMIT_CPU", + "RLIMIT_CPU_USAGE_MONITOR", + "RLIMIT_DATA", + "RLIMIT_FSIZE", + "RLIMIT_NOFILE", + "RLIMIT_STACK", + "RLIM_INFINITY", + "RTAX_ADVMSS", + "RTAX_AUTHOR", + "RTAX_BRD", + "RTAX_CWND", + "RTAX_DST", + "RTAX_FEATURES", + "RTAX_FEATURE_ALLFRAG", + "RTAX_FEATURE_ECN", + "RTAX_FEATURE_SACK", + "RTAX_FEATURE_TIMESTAMP", + "RTAX_GATEWAY", + "RTAX_GENMASK", + "RTAX_HOPLIMIT", + "RTAX_IFA", + "RTAX_IFP", + "RTAX_INITCWND", + "RTAX_INITRWND", + "RTAX_LABEL", + "RTAX_LOCK", + "RTAX_MAX", + "RTAX_MTU", + "RTAX_NETMASK", + "RTAX_REORDERING", + "RTAX_RTO_MIN", + "RTAX_RTT", + "RTAX_RTTVAR", + "RTAX_SRC", + "RTAX_SRCMASK", + "RTAX_SSTHRESH", + "RTAX_TAG", + "RTAX_UNSPEC", + "RTAX_WINDOW", + "RTA_ALIGNTO", + "RTA_AUTHOR", + "RTA_BRD", + "RTA_CACHEINFO", + "RTA_DST", + "RTA_FLOW", + "RTA_GATEWAY", + "RTA_GENMASK", + "RTA_IFA", + "RTA_IFP", + "RTA_IIF", + "RTA_LABEL", + "RTA_MAX", + "RTA_METRICS", + "RTA_MULTIPATH", + "RTA_NETMASK", + "RTA_OIF", + "RTA_PREFSRC", + "RTA_PRIORITY", + "RTA_SRC", + "RTA_SRCMASK", + "RTA_TABLE", + "RTA_TAG", + "RTA_UNSPEC", + "RTCF_DIRECTSRC", + "RTCF_DOREDIRECT", + "RTCF_LOG", + "RTCF_MASQ", + "RTCF_NAT", + "RTCF_VALVE", + "RTF_ADDRCLASSMASK", + "RTF_ADDRCONF", + "RTF_ALLONLINK", + "RTF_ANNOUNCE", + "RTF_BLACKHOLE", + "RTF_BROADCAST", + "RTF_CACHE", + "RTF_CLONED", + "RTF_CLONING", + "RTF_CONDEMNED", + "RTF_DEFAULT", + "RTF_DELCLONE", + "RTF_DONE", + "RTF_DYNAMIC", + "RTF_FLOW", + "RTF_FMASK", + "RTF_GATEWAY", + "RTF_GWFLAG_COMPAT", + "RTF_HOST", + "RTF_IFREF", + "RTF_IFSCOPE", + "RTF_INTERFACE", + "RTF_IRTT", + "RTF_LINKRT", + "RTF_LLDATA", + "RTF_LLINFO", + "RTF_LOCAL", + "RTF_MASK", + "RTF_MODIFIED", + "RTF_MPATH", + "RTF_MPLS", + "RTF_MSS", + "RTF_MTU", + "RTF_MULTICAST", + "RTF_NAT", + "RTF_NOFORWARD", + "RTF_NONEXTHOP", + "RTF_NOPMTUDISC", + "RTF_PERMANENT_ARP", + "RTF_PINNED", + "RTF_POLICY", + "RTF_PRCLONING", + "RTF_PROTO1", + "RTF_PROTO2", + "RTF_PROTO3", + "RTF_PROXY", + "RTF_REINSTATE", + "RTF_REJECT", + "RTF_RNH_LOCKED", + "RTF_ROUTER", + "RTF_SOURCE", + "RTF_SRC", + "RTF_STATIC", + "RTF_STICKY", + "RTF_THROW", + "RTF_TUNNEL", + "RTF_UP", + "RTF_USETRAILERS", + "RTF_WASCLONED", + "RTF_WINDOW", + "RTF_XRESOLVE", + "RTM_ADD", + "RTM_BASE", + "RTM_CHANGE", + "RTM_CHGADDR", + "RTM_DELACTION", + "RTM_DELADDR", + "RTM_DELADDRLABEL", + "RTM_DELETE", + "RTM_DELLINK", + "RTM_DELMADDR", + "RTM_DELNEIGH", + "RTM_DELQDISC", + "RTM_DELROUTE", + "RTM_DELRULE", + "RTM_DELTCLASS", + "RTM_DELTFILTER", + "RTM_DESYNC", + "RTM_F_CLONED", + "RTM_F_EQUALIZE", + "RTM_F_NOTIFY", + "RTM_F_PREFIX", + "RTM_GET", + "RTM_GET2", + "RTM_GETACTION", + "RTM_GETADDR", + "RTM_GETADDRLABEL", + "RTM_GETANYCAST", + "RTM_GETDCB", + "RTM_GETLINK", + "RTM_GETMULTICAST", + "RTM_GETNEIGH", + "RTM_GETNEIGHTBL", + "RTM_GETQDISC", + "RTM_GETROUTE", + "RTM_GETRULE", + "RTM_GETTCLASS", + "RTM_GETTFILTER", + "RTM_IEEE80211", + "RTM_IFANNOUNCE", + "RTM_IFINFO", + "RTM_IFINFO2", + "RTM_LLINFO_UPD", + "RTM_LOCK", + "RTM_LOSING", + "RTM_MAX", + "RTM_MAXSIZE", + "RTM_MISS", + "RTM_NEWACTION", + "RTM_NEWADDR", + "RTM_NEWADDRLABEL", + "RTM_NEWLINK", + "RTM_NEWMADDR", + "RTM_NEWMADDR2", + "RTM_NEWNDUSEROPT", + "RTM_NEWNEIGH", + "RTM_NEWNEIGHTBL", + "RTM_NEWPREFIX", + "RTM_NEWQDISC", + "RTM_NEWROUTE", + "RTM_NEWRULE", + "RTM_NEWTCLASS", + "RTM_NEWTFILTER", + "RTM_NR_FAMILIES", + "RTM_NR_MSGTYPES", + "RTM_OIFINFO", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_OOIFINFO", + "RTM_REDIRECT", + "RTM_RESOLVE", + "RTM_RTTUNIT", + "RTM_SETDCB", + "RTM_SETGATE", + "RTM_SETLINK", + "RTM_SETNEIGHTBL", + "RTM_VERSION", + "RTNH_ALIGNTO", + "RTNH_F_DEAD", + "RTNH_F_ONLINK", + "RTNH_F_PERVASIVE", + "RTNLGRP_IPV4_IFADDR", + "RTNLGRP_IPV4_MROUTE", + "RTNLGRP_IPV4_ROUTE", + "RTNLGRP_IPV4_RULE", + "RTNLGRP_IPV6_IFADDR", + "RTNLGRP_IPV6_IFINFO", + "RTNLGRP_IPV6_MROUTE", + "RTNLGRP_IPV6_PREFIX", + "RTNLGRP_IPV6_ROUTE", + "RTNLGRP_IPV6_RULE", + "RTNLGRP_LINK", + "RTNLGRP_ND_USEROPT", + "RTNLGRP_NEIGH", + "RTNLGRP_NONE", + "RTNLGRP_NOTIFY", + "RTNLGRP_TC", + "RTN_ANYCAST", + "RTN_BLACKHOLE", + "RTN_BROADCAST", + "RTN_LOCAL", + "RTN_MAX", + "RTN_MULTICAST", + "RTN_NAT", + "RTN_PROHIBIT", + "RTN_THROW", + "RTN_UNICAST", + "RTN_UNREACHABLE", + "RTN_UNSPEC", + "RTN_XRESOLVE", + "RTPROT_BIRD", + "RTPROT_BOOT", + "RTPROT_DHCP", + "RTPROT_DNROUTED", + "RTPROT_GATED", + "RTPROT_KERNEL", + "RTPROT_MRT", + "RTPROT_NTK", + "RTPROT_RA", + "RTPROT_REDIRECT", + "RTPROT_STATIC", + "RTPROT_UNSPEC", + "RTPROT_XORP", + "RTPROT_ZEBRA", + "RTV_EXPIRE", + "RTV_HOPCOUNT", + "RTV_MTU", + "RTV_RPIPE", + "RTV_RTT", + "RTV_RTTVAR", + "RTV_SPIPE", + "RTV_SSTHRESH", + "RTV_WEIGHT", + "RT_CACHING_CONTEXT", + "RT_CLASS_DEFAULT", + "RT_CLASS_LOCAL", + "RT_CLASS_MAIN", + "RT_CLASS_MAX", + "RT_CLASS_UNSPEC", + "RT_DEFAULT_FIB", + "RT_NORTREF", + "RT_SCOPE_HOST", + "RT_SCOPE_LINK", + "RT_SCOPE_NOWHERE", + "RT_SCOPE_SITE", + "RT_SCOPE_UNIVERSE", + "RT_TABLEID_MAX", + "RT_TABLE_COMPAT", + "RT_TABLE_DEFAULT", + "RT_TABLE_LOCAL", + "RT_TABLE_MAIN", + "RT_TABLE_MAX", + "RT_TABLE_UNSPEC", + "RUSAGE_CHILDREN", + "RUSAGE_SELF", + "RUSAGE_THREAD", + "Radvisory_t", + "RawConn", + "RawSockaddr", + "RawSockaddrAny", + "RawSockaddrDatalink", + "RawSockaddrInet4", + "RawSockaddrInet6", + "RawSockaddrLinklayer", + "RawSockaddrNetlink", + "RawSockaddrUnix", + "RawSyscall", + "RawSyscall6", + "Read", + "ReadConsole", + "ReadDirectoryChanges", + "ReadDirent", + "ReadFile", + "Readlink", + "Reboot", + "Recvfrom", + "Recvmsg", + "RegCloseKey", + "RegEnumKeyEx", + "RegOpenKeyEx", + "RegQueryInfoKey", + "RegQueryValueEx", + "RemoveDirectory", + "Removexattr", + "Rename", + "Renameat", + "Revoke", + "Rlimit", + "Rmdir", + "RouteMessage", + "RouteRIB", + "RoutingMessage", + "RtAttr", + "RtGenmsg", + "RtMetrics", + "RtMsg", + "RtMsghdr", + "RtNexthop", + "Rusage", + "SCM_BINTIME", + "SCM_CREDENTIALS", + "SCM_CREDS", + "SCM_RIGHTS", + "SCM_TIMESTAMP", + "SCM_TIMESTAMPING", + "SCM_TIMESTAMPNS", + "SCM_TIMESTAMP_MONOTONIC", + "SHUT_RD", + "SHUT_RDWR", + "SHUT_WR", + "SID", + "SIDAndAttributes", + "SIGABRT", + "SIGALRM", + "SIGBUS", + "SIGCHLD", + "SIGCLD", + "SIGCONT", + "SIGEMT", + "SIGFPE", + "SIGHUP", + "SIGILL", + "SIGINFO", + "SIGINT", + "SIGIO", + "SIGIOT", + "SIGKILL", + "SIGLIBRT", + "SIGLWP", + "SIGPIPE", + "SIGPOLL", + "SIGPROF", + "SIGPWR", + "SIGQUIT", + "SIGSEGV", + "SIGSTKFLT", + "SIGSTOP", + "SIGSYS", + "SIGTERM", + "SIGTHR", + "SIGTRAP", + "SIGTSTP", + "SIGTTIN", + "SIGTTOU", + "SIGUNUSED", + "SIGURG", + "SIGUSR1", + "SIGUSR2", + "SIGVTALRM", + "SIGWINCH", + "SIGXCPU", + "SIGXFSZ", + "SIOCADDDLCI", + "SIOCADDMULTI", + "SIOCADDRT", + "SIOCAIFADDR", + "SIOCAIFGROUP", + "SIOCALIFADDR", + "SIOCARPIPLL", + "SIOCATMARK", + "SIOCAUTOADDR", + "SIOCAUTONETMASK", + "SIOCBRDGADD", + "SIOCBRDGADDS", + "SIOCBRDGARL", + "SIOCBRDGDADDR", + "SIOCBRDGDEL", + "SIOCBRDGDELS", + "SIOCBRDGFLUSH", + "SIOCBRDGFRL", + "SIOCBRDGGCACHE", + "SIOCBRDGGFD", + "SIOCBRDGGHT", + "SIOCBRDGGIFFLGS", + "SIOCBRDGGMA", + "SIOCBRDGGPARAM", + "SIOCBRDGGPRI", + "SIOCBRDGGRL", + "SIOCBRDGGSIFS", + "SIOCBRDGGTO", + "SIOCBRDGIFS", + "SIOCBRDGRTS", + "SIOCBRDGSADDR", + "SIOCBRDGSCACHE", + "SIOCBRDGSFD", + "SIOCBRDGSHT", + "SIOCBRDGSIFCOST", + "SIOCBRDGSIFFLGS", + "SIOCBRDGSIFPRIO", + "SIOCBRDGSMA", + "SIOCBRDGSPRI", + "SIOCBRDGSPROTO", + "SIOCBRDGSTO", + "SIOCBRDGSTXHC", + "SIOCDARP", + "SIOCDELDLCI", + "SIOCDELMULTI", + "SIOCDELRT", + "SIOCDEVPRIVATE", + "SIOCDIFADDR", + "SIOCDIFGROUP", + "SIOCDIFPHYADDR", + "SIOCDLIFADDR", + "SIOCDRARP", + "SIOCGARP", + "SIOCGDRVSPEC", + "SIOCGETKALIVE", + "SIOCGETLABEL", + "SIOCGETPFLOW", + "SIOCGETPFSYNC", + "SIOCGETSGCNT", + "SIOCGETVIFCNT", + "SIOCGETVLAN", + "SIOCGHIWAT", + "SIOCGIFADDR", + "SIOCGIFADDRPREF", + "SIOCGIFALIAS", + "SIOCGIFALTMTU", + "SIOCGIFASYNCMAP", + "SIOCGIFBOND", + "SIOCGIFBR", + "SIOCGIFBRDADDR", + "SIOCGIFCAP", + "SIOCGIFCONF", + "SIOCGIFCOUNT", + "SIOCGIFDATA", + "SIOCGIFDESCR", + "SIOCGIFDEVMTU", + "SIOCGIFDLT", + "SIOCGIFDSTADDR", + "SIOCGIFENCAP", + "SIOCGIFFIB", + "SIOCGIFFLAGS", + "SIOCGIFGATTR", + "SIOCGIFGENERIC", + "SIOCGIFGMEMB", + "SIOCGIFGROUP", + "SIOCGIFHARDMTU", + "SIOCGIFHWADDR", + "SIOCGIFINDEX", + "SIOCGIFKPI", + "SIOCGIFMAC", + "SIOCGIFMAP", + "SIOCGIFMEDIA", + "SIOCGIFMEM", + "SIOCGIFMETRIC", + "SIOCGIFMTU", + "SIOCGIFNAME", + "SIOCGIFNETMASK", + "SIOCGIFPDSTADDR", + "SIOCGIFPFLAGS", + "SIOCGIFPHYS", + "SIOCGIFPRIORITY", + "SIOCGIFPSRCADDR", + "SIOCGIFRDOMAIN", + "SIOCGIFRTLABEL", + "SIOCGIFSLAVE", + "SIOCGIFSTATUS", + "SIOCGIFTIMESLOT", + "SIOCGIFTXQLEN", + "SIOCGIFVLAN", + "SIOCGIFWAKEFLAGS", + "SIOCGIFXFLAGS", + "SIOCGLIFADDR", + "SIOCGLIFPHYADDR", + "SIOCGLIFPHYRTABLE", + "SIOCGLIFPHYTTL", + "SIOCGLINKSTR", + "SIOCGLOWAT", + "SIOCGPGRP", + "SIOCGPRIVATE_0", + "SIOCGPRIVATE_1", + "SIOCGRARP", + "SIOCGSPPPPARAMS", + "SIOCGSTAMP", + "SIOCGSTAMPNS", + "SIOCGVH", + "SIOCGVNETID", + "SIOCIFCREATE", + "SIOCIFCREATE2", + "SIOCIFDESTROY", + "SIOCIFGCLONERS", + "SIOCINITIFADDR", + "SIOCPROTOPRIVATE", + "SIOCRSLVMULTI", + "SIOCRTMSG", + "SIOCSARP", + "SIOCSDRVSPEC", + "SIOCSETKALIVE", + "SIOCSETLABEL", + "SIOCSETPFLOW", + "SIOCSETPFSYNC", + "SIOCSETVLAN", + "SIOCSHIWAT", + "SIOCSIFADDR", + "SIOCSIFADDRPREF", + "SIOCSIFALTMTU", + "SIOCSIFASYNCMAP", + "SIOCSIFBOND", + "SIOCSIFBR", + "SIOCSIFBRDADDR", + "SIOCSIFCAP", + "SIOCSIFDESCR", + "SIOCSIFDSTADDR", + "SIOCSIFENCAP", + "SIOCSIFFIB", + "SIOCSIFFLAGS", + "SIOCSIFGATTR", + "SIOCSIFGENERIC", + "SIOCSIFHWADDR", + "SIOCSIFHWBROADCAST", + "SIOCSIFKPI", + "SIOCSIFLINK", + "SIOCSIFLLADDR", + "SIOCSIFMAC", + "SIOCSIFMAP", + "SIOCSIFMEDIA", + "SIOCSIFMEM", + "SIOCSIFMETRIC", + "SIOCSIFMTU", + "SIOCSIFNAME", + "SIOCSIFNETMASK", + "SIOCSIFPFLAGS", + "SIOCSIFPHYADDR", + "SIOCSIFPHYS", + "SIOCSIFPRIORITY", + "SIOCSIFRDOMAIN", + "SIOCSIFRTLABEL", + "SIOCSIFRVNET", + "SIOCSIFSLAVE", + "SIOCSIFTIMESLOT", + "SIOCSIFTXQLEN", + "SIOCSIFVLAN", + "SIOCSIFVNET", + "SIOCSIFXFLAGS", + "SIOCSLIFPHYADDR", + "SIOCSLIFPHYRTABLE", + "SIOCSLIFPHYTTL", + "SIOCSLINKSTR", + "SIOCSLOWAT", + "SIOCSPGRP", + "SIOCSRARP", + "SIOCSSPPPPARAMS", + "SIOCSVH", + "SIOCSVNETID", + "SIOCZIFDATA", + "SIO_GET_EXTENSION_FUNCTION_POINTER", + "SIO_GET_INTERFACE_LIST", + "SIO_KEEPALIVE_VALS", + "SIO_UDP_CONNRESET", + "SOCK_CLOEXEC", + "SOCK_DCCP", + "SOCK_DGRAM", + "SOCK_FLAGS_MASK", + "SOCK_MAXADDRLEN", + "SOCK_NONBLOCK", + "SOCK_NOSIGPIPE", + "SOCK_PACKET", + "SOCK_RAW", + "SOCK_RDM", + "SOCK_SEQPACKET", + "SOCK_STREAM", + "SOL_AAL", + "SOL_ATM", + "SOL_DECNET", + "SOL_ICMPV6", + "SOL_IP", + "SOL_IPV6", + "SOL_IRDA", + "SOL_PACKET", + "SOL_RAW", + "SOL_SOCKET", + "SOL_TCP", + "SOL_X25", + "SOMAXCONN", + "SO_ACCEPTCONN", + "SO_ACCEPTFILTER", + "SO_ATTACH_FILTER", + "SO_BINDANY", + "SO_BINDTODEVICE", + "SO_BINTIME", + "SO_BROADCAST", + "SO_BSDCOMPAT", + "SO_DEBUG", + "SO_DETACH_FILTER", + "SO_DOMAIN", + "SO_DONTROUTE", + "SO_DONTTRUNC", + "SO_ERROR", + "SO_KEEPALIVE", + "SO_LABEL", + "SO_LINGER", + "SO_LINGER_SEC", + "SO_LISTENINCQLEN", + "SO_LISTENQLEN", + "SO_LISTENQLIMIT", + "SO_MARK", + "SO_NETPROC", + "SO_NKE", + "SO_NOADDRERR", + "SO_NOHEADER", + "SO_NOSIGPIPE", + "SO_NOTIFYCONFLICT", + "SO_NO_CHECK", + "SO_NO_DDP", + "SO_NO_OFFLOAD", + "SO_NP_EXTENSIONS", + "SO_NREAD", + "SO_NUMRCVPKT", + "SO_NWRITE", + "SO_OOBINLINE", + "SO_OVERFLOWED", + "SO_PASSCRED", + "SO_PASSSEC", + "SO_PEERCRED", + "SO_PEERLABEL", + "SO_PEERNAME", + "SO_PEERSEC", + "SO_PRIORITY", + "SO_PROTOCOL", + "SO_PROTOTYPE", + "SO_RANDOMPORT", + "SO_RCVBUF", + "SO_RCVBUFFORCE", + "SO_RCVLOWAT", + "SO_RCVTIMEO", + "SO_RESTRICTIONS", + "SO_RESTRICT_DENYIN", + "SO_RESTRICT_DENYOUT", + "SO_RESTRICT_DENYSET", + "SO_REUSEADDR", + "SO_REUSEPORT", + "SO_REUSESHAREUID", + "SO_RTABLE", + "SO_RXQ_OVFL", + "SO_SECURITY_AUTHENTICATION", + "SO_SECURITY_ENCRYPTION_NETWORK", + "SO_SECURITY_ENCRYPTION_TRANSPORT", + "SO_SETFIB", + "SO_SNDBUF", + "SO_SNDBUFFORCE", + "SO_SNDLOWAT", + "SO_SNDTIMEO", + "SO_SPLICE", + "SO_TIMESTAMP", + "SO_TIMESTAMPING", + "SO_TIMESTAMPNS", + "SO_TIMESTAMP_MONOTONIC", + "SO_TYPE", + "SO_UPCALLCLOSEWAIT", + "SO_UPDATE_ACCEPT_CONTEXT", + "SO_UPDATE_CONNECT_CONTEXT", + "SO_USELOOPBACK", + "SO_USER_COOKIE", + "SO_VENDOR", + "SO_WANTMORE", + "SO_WANTOOBFLAG", + "SSLExtraCertChainPolicyPara", + "STANDARD_RIGHTS_ALL", + "STANDARD_RIGHTS_EXECUTE", + "STANDARD_RIGHTS_READ", + "STANDARD_RIGHTS_REQUIRED", + "STANDARD_RIGHTS_WRITE", + "STARTF_USESHOWWINDOW", + "STARTF_USESTDHANDLES", + "STD_ERROR_HANDLE", + "STD_INPUT_HANDLE", + "STD_OUTPUT_HANDLE", + "SUBLANG_ENGLISH_US", + "SW_FORCEMINIMIZE", + "SW_HIDE", + "SW_MAXIMIZE", + "SW_MINIMIZE", + "SW_NORMAL", + "SW_RESTORE", + "SW_SHOW", + "SW_SHOWDEFAULT", + "SW_SHOWMAXIMIZED", + "SW_SHOWMINIMIZED", + "SW_SHOWMINNOACTIVE", + "SW_SHOWNA", + "SW_SHOWNOACTIVATE", + "SW_SHOWNORMAL", + "SYMBOLIC_LINK_FLAG_DIRECTORY", + "SYNCHRONIZE", + "SYSCTL_VERSION", + "SYSCTL_VERS_0", + "SYSCTL_VERS_1", + "SYSCTL_VERS_MASK", + "SYS_ABORT2", + "SYS_ACCEPT", + "SYS_ACCEPT4", + "SYS_ACCEPT_NOCANCEL", + "SYS_ACCESS", + "SYS_ACCESS_EXTENDED", + "SYS_ACCT", + "SYS_ADD_KEY", + "SYS_ADD_PROFIL", + "SYS_ADJFREQ", + "SYS_ADJTIME", + "SYS_ADJTIMEX", + "SYS_AFS_SYSCALL", + "SYS_AIO_CANCEL", + "SYS_AIO_ERROR", + "SYS_AIO_FSYNC", + "SYS_AIO_MLOCK", + "SYS_AIO_READ", + "SYS_AIO_RETURN", + "SYS_AIO_SUSPEND", + "SYS_AIO_SUSPEND_NOCANCEL", + "SYS_AIO_WAITCOMPLETE", + "SYS_AIO_WRITE", + "SYS_ALARM", + "SYS_ARCH_PRCTL", + "SYS_ARM_FADVISE64_64", + "SYS_ARM_SYNC_FILE_RANGE", + "SYS_ATGETMSG", + "SYS_ATPGETREQ", + "SYS_ATPGETRSP", + "SYS_ATPSNDREQ", + "SYS_ATPSNDRSP", + "SYS_ATPUTMSG", + "SYS_ATSOCKET", + "SYS_AUDIT", + "SYS_AUDITCTL", + "SYS_AUDITON", + "SYS_AUDIT_SESSION_JOIN", + "SYS_AUDIT_SESSION_PORT", + "SYS_AUDIT_SESSION_SELF", + "SYS_BDFLUSH", + "SYS_BIND", + "SYS_BINDAT", + "SYS_BREAK", + "SYS_BRK", + "SYS_BSDTHREAD_CREATE", + "SYS_BSDTHREAD_REGISTER", + "SYS_BSDTHREAD_TERMINATE", + "SYS_CAPGET", + "SYS_CAPSET", + "SYS_CAP_ENTER", + "SYS_CAP_FCNTLS_GET", + "SYS_CAP_FCNTLS_LIMIT", + "SYS_CAP_GETMODE", + "SYS_CAP_GETRIGHTS", + "SYS_CAP_IOCTLS_GET", + "SYS_CAP_IOCTLS_LIMIT", + "SYS_CAP_NEW", + "SYS_CAP_RIGHTS_GET", + "SYS_CAP_RIGHTS_LIMIT", + "SYS_CHDIR", + "SYS_CHFLAGS", + "SYS_CHFLAGSAT", + "SYS_CHMOD", + "SYS_CHMOD_EXTENDED", + "SYS_CHOWN", + "SYS_CHOWN32", + "SYS_CHROOT", + "SYS_CHUD", + "SYS_CLOCK_ADJTIME", + "SYS_CLOCK_GETCPUCLOCKID2", + "SYS_CLOCK_GETRES", + "SYS_CLOCK_GETTIME", + "SYS_CLOCK_NANOSLEEP", + "SYS_CLOCK_SETTIME", + "SYS_CLONE", + "SYS_CLOSE", + "SYS_CLOSEFROM", + "SYS_CLOSE_NOCANCEL", + "SYS_CONNECT", + "SYS_CONNECTAT", + "SYS_CONNECT_NOCANCEL", + "SYS_COPYFILE", + "SYS_CPUSET", + "SYS_CPUSET_GETAFFINITY", + "SYS_CPUSET_GETID", + "SYS_CPUSET_SETAFFINITY", + "SYS_CPUSET_SETID", + "SYS_CREAT", + "SYS_CREATE_MODULE", + "SYS_CSOPS", + "SYS_CSOPS_AUDITTOKEN", + "SYS_DELETE", + "SYS_DELETE_MODULE", + "SYS_DUP", + "SYS_DUP2", + "SYS_DUP3", + "SYS_EACCESS", + "SYS_EPOLL_CREATE", + "SYS_EPOLL_CREATE1", + "SYS_EPOLL_CTL", + "SYS_EPOLL_CTL_OLD", + "SYS_EPOLL_PWAIT", + "SYS_EPOLL_WAIT", + "SYS_EPOLL_WAIT_OLD", + "SYS_EVENTFD", + "SYS_EVENTFD2", + "SYS_EXCHANGEDATA", + "SYS_EXECVE", + "SYS_EXIT", + "SYS_EXIT_GROUP", + "SYS_EXTATTRCTL", + "SYS_EXTATTR_DELETE_FD", + "SYS_EXTATTR_DELETE_FILE", + "SYS_EXTATTR_DELETE_LINK", + "SYS_EXTATTR_GET_FD", + "SYS_EXTATTR_GET_FILE", + "SYS_EXTATTR_GET_LINK", + "SYS_EXTATTR_LIST_FD", + "SYS_EXTATTR_LIST_FILE", + "SYS_EXTATTR_LIST_LINK", + "SYS_EXTATTR_SET_FD", + "SYS_EXTATTR_SET_FILE", + "SYS_EXTATTR_SET_LINK", + "SYS_FACCESSAT", + "SYS_FADVISE64", + "SYS_FADVISE64_64", + "SYS_FALLOCATE", + "SYS_FANOTIFY_INIT", + "SYS_FANOTIFY_MARK", + "SYS_FCHDIR", + "SYS_FCHFLAGS", + "SYS_FCHMOD", + "SYS_FCHMODAT", + "SYS_FCHMOD_EXTENDED", + "SYS_FCHOWN", + "SYS_FCHOWN32", + "SYS_FCHOWNAT", + "SYS_FCHROOT", + "SYS_FCNTL", + "SYS_FCNTL64", + "SYS_FCNTL_NOCANCEL", + "SYS_FDATASYNC", + "SYS_FEXECVE", + "SYS_FFCLOCK_GETCOUNTER", + "SYS_FFCLOCK_GETESTIMATE", + "SYS_FFCLOCK_SETESTIMATE", + "SYS_FFSCTL", + "SYS_FGETATTRLIST", + "SYS_FGETXATTR", + "SYS_FHOPEN", + "SYS_FHSTAT", + "SYS_FHSTATFS", + "SYS_FILEPORT_MAKEFD", + "SYS_FILEPORT_MAKEPORT", + "SYS_FKTRACE", + "SYS_FLISTXATTR", + "SYS_FLOCK", + "SYS_FORK", + "SYS_FPATHCONF", + "SYS_FREEBSD6_FTRUNCATE", + "SYS_FREEBSD6_LSEEK", + "SYS_FREEBSD6_MMAP", + "SYS_FREEBSD6_PREAD", + "SYS_FREEBSD6_PWRITE", + "SYS_FREEBSD6_TRUNCATE", + "SYS_FREMOVEXATTR", + "SYS_FSCTL", + "SYS_FSETATTRLIST", + "SYS_FSETXATTR", + "SYS_FSGETPATH", + "SYS_FSTAT", + "SYS_FSTAT64", + "SYS_FSTAT64_EXTENDED", + "SYS_FSTATAT", + "SYS_FSTATAT64", + "SYS_FSTATFS", + "SYS_FSTATFS64", + "SYS_FSTATV", + "SYS_FSTATVFS1", + "SYS_FSTAT_EXTENDED", + "SYS_FSYNC", + "SYS_FSYNC_NOCANCEL", + "SYS_FSYNC_RANGE", + "SYS_FTIME", + "SYS_FTRUNCATE", + "SYS_FTRUNCATE64", + "SYS_FUTEX", + "SYS_FUTIMENS", + "SYS_FUTIMES", + "SYS_FUTIMESAT", + "SYS_GETATTRLIST", + "SYS_GETAUDIT", + "SYS_GETAUDIT_ADDR", + "SYS_GETAUID", + "SYS_GETCONTEXT", + "SYS_GETCPU", + "SYS_GETCWD", + "SYS_GETDENTS", + "SYS_GETDENTS64", + "SYS_GETDIRENTRIES", + "SYS_GETDIRENTRIES64", + "SYS_GETDIRENTRIESATTR", + "SYS_GETDTABLECOUNT", + "SYS_GETDTABLESIZE", + "SYS_GETEGID", + "SYS_GETEGID32", + "SYS_GETEUID", + "SYS_GETEUID32", + "SYS_GETFH", + "SYS_GETFSSTAT", + "SYS_GETFSSTAT64", + "SYS_GETGID", + "SYS_GETGID32", + "SYS_GETGROUPS", + "SYS_GETGROUPS32", + "SYS_GETHOSTUUID", + "SYS_GETITIMER", + "SYS_GETLCID", + "SYS_GETLOGIN", + "SYS_GETLOGINCLASS", + "SYS_GETPEERNAME", + "SYS_GETPGID", + "SYS_GETPGRP", + "SYS_GETPID", + "SYS_GETPMSG", + "SYS_GETPPID", + "SYS_GETPRIORITY", + "SYS_GETRESGID", + "SYS_GETRESGID32", + "SYS_GETRESUID", + "SYS_GETRESUID32", + "SYS_GETRLIMIT", + "SYS_GETRTABLE", + "SYS_GETRUSAGE", + "SYS_GETSGROUPS", + "SYS_GETSID", + "SYS_GETSOCKNAME", + "SYS_GETSOCKOPT", + "SYS_GETTHRID", + "SYS_GETTID", + "SYS_GETTIMEOFDAY", + "SYS_GETUID", + "SYS_GETUID32", + "SYS_GETVFSSTAT", + "SYS_GETWGROUPS", + "SYS_GETXATTR", + "SYS_GET_KERNEL_SYMS", + "SYS_GET_MEMPOLICY", + "SYS_GET_ROBUST_LIST", + "SYS_GET_THREAD_AREA", + "SYS_GSSD_SYSCALL", + "SYS_GTTY", + "SYS_IDENTITYSVC", + "SYS_IDLE", + "SYS_INITGROUPS", + "SYS_INIT_MODULE", + "SYS_INOTIFY_ADD_WATCH", + "SYS_INOTIFY_INIT", + "SYS_INOTIFY_INIT1", + "SYS_INOTIFY_RM_WATCH", + "SYS_IOCTL", + "SYS_IOPERM", + "SYS_IOPL", + "SYS_IOPOLICYSYS", + "SYS_IOPRIO_GET", + "SYS_IOPRIO_SET", + "SYS_IO_CANCEL", + "SYS_IO_DESTROY", + "SYS_IO_GETEVENTS", + "SYS_IO_SETUP", + "SYS_IO_SUBMIT", + "SYS_IPC", + "SYS_ISSETUGID", + "SYS_JAIL", + "SYS_JAIL_ATTACH", + "SYS_JAIL_GET", + "SYS_JAIL_REMOVE", + "SYS_JAIL_SET", + "SYS_KAS_INFO", + "SYS_KDEBUG_TRACE", + "SYS_KENV", + "SYS_KEVENT", + "SYS_KEVENT64", + "SYS_KEXEC_LOAD", + "SYS_KEYCTL", + "SYS_KILL", + "SYS_KLDFIND", + "SYS_KLDFIRSTMOD", + "SYS_KLDLOAD", + "SYS_KLDNEXT", + "SYS_KLDSTAT", + "SYS_KLDSYM", + "SYS_KLDUNLOAD", + "SYS_KLDUNLOADF", + "SYS_KMQ_NOTIFY", + "SYS_KMQ_OPEN", + "SYS_KMQ_SETATTR", + "SYS_KMQ_TIMEDRECEIVE", + "SYS_KMQ_TIMEDSEND", + "SYS_KMQ_UNLINK", + "SYS_KQUEUE", + "SYS_KQUEUE1", + "SYS_KSEM_CLOSE", + "SYS_KSEM_DESTROY", + "SYS_KSEM_GETVALUE", + "SYS_KSEM_INIT", + "SYS_KSEM_OPEN", + "SYS_KSEM_POST", + "SYS_KSEM_TIMEDWAIT", + "SYS_KSEM_TRYWAIT", + "SYS_KSEM_UNLINK", + "SYS_KSEM_WAIT", + "SYS_KTIMER_CREATE", + "SYS_KTIMER_DELETE", + "SYS_KTIMER_GETOVERRUN", + "SYS_KTIMER_GETTIME", + "SYS_KTIMER_SETTIME", + "SYS_KTRACE", + "SYS_LCHFLAGS", + "SYS_LCHMOD", + "SYS_LCHOWN", + "SYS_LCHOWN32", + "SYS_LEDGER", + "SYS_LGETFH", + "SYS_LGETXATTR", + "SYS_LINK", + "SYS_LINKAT", + "SYS_LIO_LISTIO", + "SYS_LISTEN", + "SYS_LISTXATTR", + "SYS_LLISTXATTR", + "SYS_LOCK", + "SYS_LOOKUP_DCOOKIE", + "SYS_LPATHCONF", + "SYS_LREMOVEXATTR", + "SYS_LSEEK", + "SYS_LSETXATTR", + "SYS_LSTAT", + "SYS_LSTAT64", + "SYS_LSTAT64_EXTENDED", + "SYS_LSTATV", + "SYS_LSTAT_EXTENDED", + "SYS_LUTIMES", + "SYS_MAC_SYSCALL", + "SYS_MADVISE", + "SYS_MADVISE1", + "SYS_MAXSYSCALL", + "SYS_MBIND", + "SYS_MIGRATE_PAGES", + "SYS_MINCORE", + "SYS_MINHERIT", + "SYS_MKCOMPLEX", + "SYS_MKDIR", + "SYS_MKDIRAT", + "SYS_MKDIR_EXTENDED", + "SYS_MKFIFO", + "SYS_MKFIFOAT", + "SYS_MKFIFO_EXTENDED", + "SYS_MKNOD", + "SYS_MKNODAT", + "SYS_MLOCK", + "SYS_MLOCKALL", + "SYS_MMAP", + "SYS_MMAP2", + "SYS_MODCTL", + "SYS_MODFIND", + "SYS_MODFNEXT", + "SYS_MODIFY_LDT", + "SYS_MODNEXT", + "SYS_MODSTAT", + "SYS_MODWATCH", + "SYS_MOUNT", + "SYS_MOVE_PAGES", + "SYS_MPROTECT", + "SYS_MPX", + "SYS_MQUERY", + "SYS_MQ_GETSETATTR", + "SYS_MQ_NOTIFY", + "SYS_MQ_OPEN", + "SYS_MQ_TIMEDRECEIVE", + "SYS_MQ_TIMEDSEND", + "SYS_MQ_UNLINK", + "SYS_MREMAP", + "SYS_MSGCTL", + "SYS_MSGGET", + "SYS_MSGRCV", + "SYS_MSGRCV_NOCANCEL", + "SYS_MSGSND", + "SYS_MSGSND_NOCANCEL", + "SYS_MSGSYS", + "SYS_MSYNC", + "SYS_MSYNC_NOCANCEL", + "SYS_MUNLOCK", + "SYS_MUNLOCKALL", + "SYS_MUNMAP", + "SYS_NAME_TO_HANDLE_AT", + "SYS_NANOSLEEP", + "SYS_NEWFSTATAT", + "SYS_NFSCLNT", + "SYS_NFSSERVCTL", + "SYS_NFSSVC", + "SYS_NFSTAT", + "SYS_NICE", + "SYS_NLM_SYSCALL", + "SYS_NLSTAT", + "SYS_NMOUNT", + "SYS_NSTAT", + "SYS_NTP_ADJTIME", + "SYS_NTP_GETTIME", + "SYS_NUMA_GETAFFINITY", + "SYS_NUMA_SETAFFINITY", + "SYS_OABI_SYSCALL_BASE", + "SYS_OBREAK", + "SYS_OLDFSTAT", + "SYS_OLDLSTAT", + "SYS_OLDOLDUNAME", + "SYS_OLDSTAT", + "SYS_OLDUNAME", + "SYS_OPEN", + "SYS_OPENAT", + "SYS_OPENBSD_POLL", + "SYS_OPEN_BY_HANDLE_AT", + "SYS_OPEN_DPROTECTED_NP", + "SYS_OPEN_EXTENDED", + "SYS_OPEN_NOCANCEL", + "SYS_OVADVISE", + "SYS_PACCEPT", + "SYS_PATHCONF", + "SYS_PAUSE", + "SYS_PCICONFIG_IOBASE", + "SYS_PCICONFIG_READ", + "SYS_PCICONFIG_WRITE", + "SYS_PDFORK", + "SYS_PDGETPID", + "SYS_PDKILL", + "SYS_PERF_EVENT_OPEN", + "SYS_PERSONALITY", + "SYS_PID_HIBERNATE", + "SYS_PID_RESUME", + "SYS_PID_SHUTDOWN_SOCKETS", + "SYS_PID_SUSPEND", + "SYS_PIPE", + "SYS_PIPE2", + "SYS_PIVOT_ROOT", + "SYS_PMC_CONTROL", + "SYS_PMC_GET_INFO", + "SYS_POLL", + "SYS_POLLTS", + "SYS_POLL_NOCANCEL", + "SYS_POSIX_FADVISE", + "SYS_POSIX_FALLOCATE", + "SYS_POSIX_OPENPT", + "SYS_POSIX_SPAWN", + "SYS_PPOLL", + "SYS_PRCTL", + "SYS_PREAD", + "SYS_PREAD64", + "SYS_PREADV", + "SYS_PREAD_NOCANCEL", + "SYS_PRLIMIT64", + "SYS_PROCCTL", + "SYS_PROCESS_POLICY", + "SYS_PROCESS_VM_READV", + "SYS_PROCESS_VM_WRITEV", + "SYS_PROC_INFO", + "SYS_PROF", + "SYS_PROFIL", + "SYS_PSELECT", + "SYS_PSELECT6", + "SYS_PSET_ASSIGN", + "SYS_PSET_CREATE", + "SYS_PSET_DESTROY", + "SYS_PSYNCH_CVBROAD", + "SYS_PSYNCH_CVCLRPREPOST", + "SYS_PSYNCH_CVSIGNAL", + "SYS_PSYNCH_CVWAIT", + "SYS_PSYNCH_MUTEXDROP", + "SYS_PSYNCH_MUTEXWAIT", + "SYS_PSYNCH_RW_DOWNGRADE", + "SYS_PSYNCH_RW_LONGRDLOCK", + "SYS_PSYNCH_RW_RDLOCK", + "SYS_PSYNCH_RW_UNLOCK", + "SYS_PSYNCH_RW_UNLOCK2", + "SYS_PSYNCH_RW_UPGRADE", + "SYS_PSYNCH_RW_WRLOCK", + "SYS_PSYNCH_RW_YIELDWRLOCK", + "SYS_PTRACE", + "SYS_PUTPMSG", + "SYS_PWRITE", + "SYS_PWRITE64", + "SYS_PWRITEV", + "SYS_PWRITE_NOCANCEL", + "SYS_QUERY_MODULE", + "SYS_QUOTACTL", + "SYS_RASCTL", + "SYS_RCTL_ADD_RULE", + "SYS_RCTL_GET_LIMITS", + "SYS_RCTL_GET_RACCT", + "SYS_RCTL_GET_RULES", + "SYS_RCTL_REMOVE_RULE", + "SYS_READ", + "SYS_READAHEAD", + "SYS_READDIR", + "SYS_READLINK", + "SYS_READLINKAT", + "SYS_READV", + "SYS_READV_NOCANCEL", + "SYS_READ_NOCANCEL", + "SYS_REBOOT", + "SYS_RECV", + "SYS_RECVFROM", + "SYS_RECVFROM_NOCANCEL", + "SYS_RECVMMSG", + "SYS_RECVMSG", + "SYS_RECVMSG_NOCANCEL", + "SYS_REMAP_FILE_PAGES", + "SYS_REMOVEXATTR", + "SYS_RENAME", + "SYS_RENAMEAT", + "SYS_REQUEST_KEY", + "SYS_RESTART_SYSCALL", + "SYS_REVOKE", + "SYS_RFORK", + "SYS_RMDIR", + "SYS_RTPRIO", + "SYS_RTPRIO_THREAD", + "SYS_RT_SIGACTION", + "SYS_RT_SIGPENDING", + "SYS_RT_SIGPROCMASK", + "SYS_RT_SIGQUEUEINFO", + "SYS_RT_SIGRETURN", + "SYS_RT_SIGSUSPEND", + "SYS_RT_SIGTIMEDWAIT", + "SYS_RT_TGSIGQUEUEINFO", + "SYS_SBRK", + "SYS_SCHED_GETAFFINITY", + "SYS_SCHED_GETPARAM", + "SYS_SCHED_GETSCHEDULER", + "SYS_SCHED_GET_PRIORITY_MAX", + "SYS_SCHED_GET_PRIORITY_MIN", + "SYS_SCHED_RR_GET_INTERVAL", + "SYS_SCHED_SETAFFINITY", + "SYS_SCHED_SETPARAM", + "SYS_SCHED_SETSCHEDULER", + "SYS_SCHED_YIELD", + "SYS_SCTP_GENERIC_RECVMSG", + "SYS_SCTP_GENERIC_SENDMSG", + "SYS_SCTP_GENERIC_SENDMSG_IOV", + "SYS_SCTP_PEELOFF", + "SYS_SEARCHFS", + "SYS_SECURITY", + "SYS_SELECT", + "SYS_SELECT_NOCANCEL", + "SYS_SEMCONFIG", + "SYS_SEMCTL", + "SYS_SEMGET", + "SYS_SEMOP", + "SYS_SEMSYS", + "SYS_SEMTIMEDOP", + "SYS_SEM_CLOSE", + "SYS_SEM_DESTROY", + "SYS_SEM_GETVALUE", + "SYS_SEM_INIT", + "SYS_SEM_OPEN", + "SYS_SEM_POST", + "SYS_SEM_TRYWAIT", + "SYS_SEM_UNLINK", + "SYS_SEM_WAIT", + "SYS_SEM_WAIT_NOCANCEL", + "SYS_SEND", + "SYS_SENDFILE", + "SYS_SENDFILE64", + "SYS_SENDMMSG", + "SYS_SENDMSG", + "SYS_SENDMSG_NOCANCEL", + "SYS_SENDTO", + "SYS_SENDTO_NOCANCEL", + "SYS_SETATTRLIST", + "SYS_SETAUDIT", + "SYS_SETAUDIT_ADDR", + "SYS_SETAUID", + "SYS_SETCONTEXT", + "SYS_SETDOMAINNAME", + "SYS_SETEGID", + "SYS_SETEUID", + "SYS_SETFIB", + "SYS_SETFSGID", + "SYS_SETFSGID32", + "SYS_SETFSUID", + "SYS_SETFSUID32", + "SYS_SETGID", + "SYS_SETGID32", + "SYS_SETGROUPS", + "SYS_SETGROUPS32", + "SYS_SETHOSTNAME", + "SYS_SETITIMER", + "SYS_SETLCID", + "SYS_SETLOGIN", + "SYS_SETLOGINCLASS", + "SYS_SETNS", + "SYS_SETPGID", + "SYS_SETPRIORITY", + "SYS_SETPRIVEXEC", + "SYS_SETREGID", + "SYS_SETREGID32", + "SYS_SETRESGID", + "SYS_SETRESGID32", + "SYS_SETRESUID", + "SYS_SETRESUID32", + "SYS_SETREUID", + "SYS_SETREUID32", + "SYS_SETRLIMIT", + "SYS_SETRTABLE", + "SYS_SETSGROUPS", + "SYS_SETSID", + "SYS_SETSOCKOPT", + "SYS_SETTID", + "SYS_SETTID_WITH_PID", + "SYS_SETTIMEOFDAY", + "SYS_SETUID", + "SYS_SETUID32", + "SYS_SETWGROUPS", + "SYS_SETXATTR", + "SYS_SET_MEMPOLICY", + "SYS_SET_ROBUST_LIST", + "SYS_SET_THREAD_AREA", + "SYS_SET_TID_ADDRESS", + "SYS_SGETMASK", + "SYS_SHARED_REGION_CHECK_NP", + "SYS_SHARED_REGION_MAP_AND_SLIDE_NP", + "SYS_SHMAT", + "SYS_SHMCTL", + "SYS_SHMDT", + "SYS_SHMGET", + "SYS_SHMSYS", + "SYS_SHM_OPEN", + "SYS_SHM_UNLINK", + "SYS_SHUTDOWN", + "SYS_SIGACTION", + "SYS_SIGALTSTACK", + "SYS_SIGNAL", + "SYS_SIGNALFD", + "SYS_SIGNALFD4", + "SYS_SIGPENDING", + "SYS_SIGPROCMASK", + "SYS_SIGQUEUE", + "SYS_SIGQUEUEINFO", + "SYS_SIGRETURN", + "SYS_SIGSUSPEND", + "SYS_SIGSUSPEND_NOCANCEL", + "SYS_SIGTIMEDWAIT", + "SYS_SIGWAIT", + "SYS_SIGWAITINFO", + "SYS_SOCKET", + "SYS_SOCKETCALL", + "SYS_SOCKETPAIR", + "SYS_SPLICE", + "SYS_SSETMASK", + "SYS_SSTK", + "SYS_STACK_SNAPSHOT", + "SYS_STAT", + "SYS_STAT64", + "SYS_STAT64_EXTENDED", + "SYS_STATFS", + "SYS_STATFS64", + "SYS_STATV", + "SYS_STATVFS1", + "SYS_STAT_EXTENDED", + "SYS_STIME", + "SYS_STTY", + "SYS_SWAPCONTEXT", + "SYS_SWAPCTL", + "SYS_SWAPOFF", + "SYS_SWAPON", + "SYS_SYMLINK", + "SYS_SYMLINKAT", + "SYS_SYNC", + "SYS_SYNCFS", + "SYS_SYNC_FILE_RANGE", + "SYS_SYSARCH", + "SYS_SYSCALL", + "SYS_SYSCALL_BASE", + "SYS_SYSFS", + "SYS_SYSINFO", + "SYS_SYSLOG", + "SYS_TEE", + "SYS_TGKILL", + "SYS_THREAD_SELFID", + "SYS_THR_CREATE", + "SYS_THR_EXIT", + "SYS_THR_KILL", + "SYS_THR_KILL2", + "SYS_THR_NEW", + "SYS_THR_SELF", + "SYS_THR_SET_NAME", + "SYS_THR_SUSPEND", + "SYS_THR_WAKE", + "SYS_TIME", + "SYS_TIMERFD_CREATE", + "SYS_TIMERFD_GETTIME", + "SYS_TIMERFD_SETTIME", + "SYS_TIMER_CREATE", + "SYS_TIMER_DELETE", + "SYS_TIMER_GETOVERRUN", + "SYS_TIMER_GETTIME", + "SYS_TIMER_SETTIME", + "SYS_TIMES", + "SYS_TKILL", + "SYS_TRUNCATE", + "SYS_TRUNCATE64", + "SYS_TUXCALL", + "SYS_UGETRLIMIT", + "SYS_ULIMIT", + "SYS_UMASK", + "SYS_UMASK_EXTENDED", + "SYS_UMOUNT", + "SYS_UMOUNT2", + "SYS_UNAME", + "SYS_UNDELETE", + "SYS_UNLINK", + "SYS_UNLINKAT", + "SYS_UNMOUNT", + "SYS_UNSHARE", + "SYS_USELIB", + "SYS_USTAT", + "SYS_UTIME", + "SYS_UTIMENSAT", + "SYS_UTIMES", + "SYS_UTRACE", + "SYS_UUIDGEN", + "SYS_VADVISE", + "SYS_VFORK", + "SYS_VHANGUP", + "SYS_VM86", + "SYS_VM86OLD", + "SYS_VMSPLICE", + "SYS_VM_PRESSURE_MONITOR", + "SYS_VSERVER", + "SYS_WAIT4", + "SYS_WAIT4_NOCANCEL", + "SYS_WAIT6", + "SYS_WAITEVENT", + "SYS_WAITID", + "SYS_WAITID_NOCANCEL", + "SYS_WAITPID", + "SYS_WATCHEVENT", + "SYS_WORKQ_KERNRETURN", + "SYS_WORKQ_OPEN", + "SYS_WRITE", + "SYS_WRITEV", + "SYS_WRITEV_NOCANCEL", + "SYS_WRITE_NOCANCEL", + "SYS_YIELD", + "SYS__LLSEEK", + "SYS__LWP_CONTINUE", + "SYS__LWP_CREATE", + "SYS__LWP_CTL", + "SYS__LWP_DETACH", + "SYS__LWP_EXIT", + "SYS__LWP_GETNAME", + "SYS__LWP_GETPRIVATE", + "SYS__LWP_KILL", + "SYS__LWP_PARK", + "SYS__LWP_SELF", + "SYS__LWP_SETNAME", + "SYS__LWP_SETPRIVATE", + "SYS__LWP_SUSPEND", + "SYS__LWP_UNPARK", + "SYS__LWP_UNPARK_ALL", + "SYS__LWP_WAIT", + "SYS__LWP_WAKEUP", + "SYS__NEWSELECT", + "SYS__PSET_BIND", + "SYS__SCHED_GETAFFINITY", + "SYS__SCHED_GETPARAM", + "SYS__SCHED_SETAFFINITY", + "SYS__SCHED_SETPARAM", + "SYS__SYSCTL", + "SYS__UMTX_LOCK", + "SYS__UMTX_OP", + "SYS__UMTX_UNLOCK", + "SYS___ACL_ACLCHECK_FD", + "SYS___ACL_ACLCHECK_FILE", + "SYS___ACL_ACLCHECK_LINK", + "SYS___ACL_DELETE_FD", + "SYS___ACL_DELETE_FILE", + "SYS___ACL_DELETE_LINK", + "SYS___ACL_GET_FD", + "SYS___ACL_GET_FILE", + "SYS___ACL_GET_LINK", + "SYS___ACL_SET_FD", + "SYS___ACL_SET_FILE", + "SYS___ACL_SET_LINK", + "SYS___CAP_RIGHTS_GET", + "SYS___CLONE", + "SYS___DISABLE_THREADSIGNAL", + "SYS___GETCWD", + "SYS___GETLOGIN", + "SYS___GET_TCB", + "SYS___MAC_EXECVE", + "SYS___MAC_GETFSSTAT", + "SYS___MAC_GET_FD", + "SYS___MAC_GET_FILE", + "SYS___MAC_GET_LCID", + "SYS___MAC_GET_LCTX", + "SYS___MAC_GET_LINK", + "SYS___MAC_GET_MOUNT", + "SYS___MAC_GET_PID", + "SYS___MAC_GET_PROC", + "SYS___MAC_MOUNT", + "SYS___MAC_SET_FD", + "SYS___MAC_SET_FILE", + "SYS___MAC_SET_LCTX", + "SYS___MAC_SET_LINK", + "SYS___MAC_SET_PROC", + "SYS___MAC_SYSCALL", + "SYS___OLD_SEMWAIT_SIGNAL", + "SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL", + "SYS___POSIX_CHOWN", + "SYS___POSIX_FCHOWN", + "SYS___POSIX_LCHOWN", + "SYS___POSIX_RENAME", + "SYS___PTHREAD_CANCELED", + "SYS___PTHREAD_CHDIR", + "SYS___PTHREAD_FCHDIR", + "SYS___PTHREAD_KILL", + "SYS___PTHREAD_MARKCANCEL", + "SYS___PTHREAD_SIGMASK", + "SYS___QUOTACTL", + "SYS___SEMCTL", + "SYS___SEMWAIT_SIGNAL", + "SYS___SEMWAIT_SIGNAL_NOCANCEL", + "SYS___SETLOGIN", + "SYS___SETUGID", + "SYS___SET_TCB", + "SYS___SIGACTION_SIGTRAMP", + "SYS___SIGTIMEDWAIT", + "SYS___SIGWAIT", + "SYS___SIGWAIT_NOCANCEL", + "SYS___SYSCTL", + "SYS___TFORK", + "SYS___THREXIT", + "SYS___THRSIGDIVERT", + "SYS___THRSLEEP", + "SYS___THRWAKEUP", + "S_ARCH1", + "S_ARCH2", + "S_BLKSIZE", + "S_IEXEC", + "S_IFBLK", + "S_IFCHR", + "S_IFDIR", + "S_IFIFO", + "S_IFLNK", + "S_IFMT", + "S_IFREG", + "S_IFSOCK", + "S_IFWHT", + "S_IREAD", + "S_IRGRP", + "S_IROTH", + "S_IRUSR", + "S_IRWXG", + "S_IRWXO", + "S_IRWXU", + "S_ISGID", + "S_ISTXT", + "S_ISUID", + "S_ISVTX", + "S_IWGRP", + "S_IWOTH", + "S_IWRITE", + "S_IWUSR", + "S_IXGRP", + "S_IXOTH", + "S_IXUSR", + "S_LOGIN_SET", + "SecurityAttributes", + "Seek", + "Select", + "Sendfile", + "Sendmsg", + "SendmsgN", + "Sendto", + "Servent", + "SetBpf", + "SetBpfBuflen", + "SetBpfDatalink", + "SetBpfHeadercmpl", + "SetBpfImmediate", + "SetBpfInterface", + "SetBpfPromisc", + "SetBpfTimeout", + "SetCurrentDirectory", + "SetEndOfFile", + "SetEnvironmentVariable", + "SetFileAttributes", + "SetFileCompletionNotificationModes", + "SetFilePointer", + "SetFileTime", + "SetHandleInformation", + "SetKevent", + "SetLsfPromisc", + "SetNonblock", + "Setdomainname", + "Setegid", + "Setenv", + "Seteuid", + "Setfsgid", + "Setfsuid", + "Setgid", + "Setgroups", + "Sethostname", + "Setlogin", + "Setpgid", + "Setpriority", + "Setprivexec", + "Setregid", + "Setresgid", + "Setresuid", + "Setreuid", + "Setrlimit", + "Setsid", + "Setsockopt", + "SetsockoptByte", + "SetsockoptICMPv6Filter", + "SetsockoptIPMreq", + "SetsockoptIPMreqn", + "SetsockoptIPv6Mreq", + "SetsockoptInet4Addr", + "SetsockoptInt", + "SetsockoptLinger", + "SetsockoptString", + "SetsockoptTimeval", + "Settimeofday", + "Setuid", + "Setxattr", + "Shutdown", + "SidTypeAlias", + "SidTypeComputer", + "SidTypeDeletedAccount", + "SidTypeDomain", + "SidTypeGroup", + "SidTypeInvalid", + "SidTypeLabel", + "SidTypeUnknown", + "SidTypeUser", + "SidTypeWellKnownGroup", + "Signal", + "SizeofBpfHdr", + "SizeofBpfInsn", + "SizeofBpfProgram", + "SizeofBpfStat", + "SizeofBpfVersion", + "SizeofBpfZbuf", + "SizeofBpfZbufHeader", + "SizeofCmsghdr", + "SizeofICMPv6Filter", + "SizeofIPMreq", + "SizeofIPMreqn", + "SizeofIPv6MTUInfo", + "SizeofIPv6Mreq", + "SizeofIfAddrmsg", + "SizeofIfAnnounceMsghdr", + "SizeofIfData", + "SizeofIfInfomsg", + "SizeofIfMsghdr", + "SizeofIfaMsghdr", + "SizeofIfmaMsghdr", + "SizeofIfmaMsghdr2", + "SizeofInet4Pktinfo", + "SizeofInet6Pktinfo", + "SizeofInotifyEvent", + "SizeofLinger", + "SizeofMsghdr", + "SizeofNlAttr", + "SizeofNlMsgerr", + "SizeofNlMsghdr", + "SizeofRtAttr", + "SizeofRtGenmsg", + "SizeofRtMetrics", + "SizeofRtMsg", + "SizeofRtMsghdr", + "SizeofRtNexthop", + "SizeofSockFilter", + "SizeofSockFprog", + "SizeofSockaddrAny", + "SizeofSockaddrDatalink", + "SizeofSockaddrInet4", + "SizeofSockaddrInet6", + "SizeofSockaddrLinklayer", + "SizeofSockaddrNetlink", + "SizeofSockaddrUnix", + "SizeofTCPInfo", + "SizeofUcred", + "SlicePtrFromStrings", + "SockFilter", + "SockFprog", + "Sockaddr", + "SockaddrDatalink", + "SockaddrGen", + "SockaddrInet4", + "SockaddrInet6", + "SockaddrLinklayer", + "SockaddrNetlink", + "SockaddrUnix", + "Socket", + "SocketControlMessage", + "SocketDisableIPv6", + "Socketpair", + "Splice", + "StartProcess", + "StartupInfo", + "Stat", + "Stat_t", + "Statfs", + "Statfs_t", + "Stderr", + "Stdin", + "Stdout", + "StringBytePtr", + "StringByteSlice", + "StringSlicePtr", + "StringToSid", + "StringToUTF16", + "StringToUTF16Ptr", + "Symlink", + "Sync", + "SyncFileRange", + "SysProcAttr", + "SysProcIDMap", + "Syscall", + "Syscall12", + "Syscall15", + "Syscall18", + "Syscall6", + "Syscall9", + "SyscallN", + "Sysctl", + "SysctlUint32", + "Sysctlnode", + "Sysinfo", + "Sysinfo_t", + "Systemtime", + "TCGETS", + "TCIFLUSH", + "TCIOFLUSH", + "TCOFLUSH", + "TCPInfo", + "TCPKeepalive", + "TCP_CA_NAME_MAX", + "TCP_CONGCTL", + "TCP_CONGESTION", + "TCP_CONNECTIONTIMEOUT", + "TCP_CORK", + "TCP_DEFER_ACCEPT", + "TCP_ENABLE_ECN", + "TCP_INFO", + "TCP_KEEPALIVE", + "TCP_KEEPCNT", + "TCP_KEEPIDLE", + "TCP_KEEPINIT", + "TCP_KEEPINTVL", + "TCP_LINGER2", + "TCP_MAXBURST", + "TCP_MAXHLEN", + "TCP_MAXOLEN", + "TCP_MAXSEG", + "TCP_MAXWIN", + "TCP_MAX_SACK", + "TCP_MAX_WINSHIFT", + "TCP_MD5SIG", + "TCP_MD5SIG_MAXKEYLEN", + "TCP_MINMSS", + "TCP_MINMSSOVERLOAD", + "TCP_MSS", + "TCP_NODELAY", + "TCP_NOOPT", + "TCP_NOPUSH", + "TCP_NOTSENT_LOWAT", + "TCP_NSTATES", + "TCP_QUICKACK", + "TCP_RXT_CONNDROPTIME", + "TCP_RXT_FINDROP", + "TCP_SACK_ENABLE", + "TCP_SENDMOREACKS", + "TCP_SYNCNT", + "TCP_VENDOR", + "TCP_WINDOW_CLAMP", + "TCSAFLUSH", + "TCSETS", + "TF_DISCONNECT", + "TF_REUSE_SOCKET", + "TF_USE_DEFAULT_WORKER", + "TF_USE_KERNEL_APC", + "TF_USE_SYSTEM_THREAD", + "TF_WRITE_BEHIND", + "TH32CS_INHERIT", + "TH32CS_SNAPALL", + "TH32CS_SNAPHEAPLIST", + "TH32CS_SNAPMODULE", + "TH32CS_SNAPMODULE32", + "TH32CS_SNAPPROCESS", + "TH32CS_SNAPTHREAD", + "TIME_ZONE_ID_DAYLIGHT", + "TIME_ZONE_ID_STANDARD", + "TIME_ZONE_ID_UNKNOWN", + "TIOCCBRK", + "TIOCCDTR", + "TIOCCONS", + "TIOCDCDTIMESTAMP", + "TIOCDRAIN", + "TIOCDSIMICROCODE", + "TIOCEXCL", + "TIOCEXT", + "TIOCFLAG_CDTRCTS", + "TIOCFLAG_CLOCAL", + "TIOCFLAG_CRTSCTS", + "TIOCFLAG_MDMBUF", + "TIOCFLAG_PPS", + "TIOCFLAG_SOFTCAR", + "TIOCFLUSH", + "TIOCGDEV", + "TIOCGDRAINWAIT", + "TIOCGETA", + "TIOCGETD", + "TIOCGFLAGS", + "TIOCGICOUNT", + "TIOCGLCKTRMIOS", + "TIOCGLINED", + "TIOCGPGRP", + "TIOCGPTN", + "TIOCGQSIZE", + "TIOCGRANTPT", + "TIOCGRS485", + "TIOCGSERIAL", + "TIOCGSID", + "TIOCGSIZE", + "TIOCGSOFTCAR", + "TIOCGTSTAMP", + "TIOCGWINSZ", + "TIOCINQ", + "TIOCIXOFF", + "TIOCIXON", + "TIOCLINUX", + "TIOCMBIC", + "TIOCMBIS", + "TIOCMGDTRWAIT", + "TIOCMGET", + "TIOCMIWAIT", + "TIOCMODG", + "TIOCMODS", + "TIOCMSDTRWAIT", + "TIOCMSET", + "TIOCM_CAR", + "TIOCM_CD", + "TIOCM_CTS", + "TIOCM_DCD", + "TIOCM_DSR", + "TIOCM_DTR", + "TIOCM_LE", + "TIOCM_RI", + "TIOCM_RNG", + "TIOCM_RTS", + "TIOCM_SR", + "TIOCM_ST", + "TIOCNOTTY", + "TIOCNXCL", + "TIOCOUTQ", + "TIOCPKT", + "TIOCPKT_DATA", + "TIOCPKT_DOSTOP", + "TIOCPKT_FLUSHREAD", + "TIOCPKT_FLUSHWRITE", + "TIOCPKT_IOCTL", + "TIOCPKT_NOSTOP", + "TIOCPKT_START", + "TIOCPKT_STOP", + "TIOCPTMASTER", + "TIOCPTMGET", + "TIOCPTSNAME", + "TIOCPTYGNAME", + "TIOCPTYGRANT", + "TIOCPTYUNLK", + "TIOCRCVFRAME", + "TIOCREMOTE", + "TIOCSBRK", + "TIOCSCONS", + "TIOCSCTTY", + "TIOCSDRAINWAIT", + "TIOCSDTR", + "TIOCSERCONFIG", + "TIOCSERGETLSR", + "TIOCSERGETMULTI", + "TIOCSERGSTRUCT", + "TIOCSERGWILD", + "TIOCSERSETMULTI", + "TIOCSERSWILD", + "TIOCSER_TEMT", + "TIOCSETA", + "TIOCSETAF", + "TIOCSETAW", + "TIOCSETD", + "TIOCSFLAGS", + "TIOCSIG", + "TIOCSLCKTRMIOS", + "TIOCSLINED", + "TIOCSPGRP", + "TIOCSPTLCK", + "TIOCSQSIZE", + "TIOCSRS485", + "TIOCSSERIAL", + "TIOCSSIZE", + "TIOCSSOFTCAR", + "TIOCSTART", + "TIOCSTAT", + "TIOCSTI", + "TIOCSTOP", + "TIOCSTSTAMP", + "TIOCSWINSZ", + "TIOCTIMESTAMP", + "TIOCUCNTL", + "TIOCVHANGUP", + "TIOCXMTFRAME", + "TOKEN_ADJUST_DEFAULT", + "TOKEN_ADJUST_GROUPS", + "TOKEN_ADJUST_PRIVILEGES", + "TOKEN_ADJUST_SESSIONID", + "TOKEN_ALL_ACCESS", + "TOKEN_ASSIGN_PRIMARY", + "TOKEN_DUPLICATE", + "TOKEN_EXECUTE", + "TOKEN_IMPERSONATE", + "TOKEN_QUERY", + "TOKEN_QUERY_SOURCE", + "TOKEN_READ", + "TOKEN_WRITE", + "TOSTOP", + "TRUNCATE_EXISTING", + "TUNATTACHFILTER", + "TUNDETACHFILTER", + "TUNGETFEATURES", + "TUNGETIFF", + "TUNGETSNDBUF", + "TUNGETVNETHDRSZ", + "TUNSETDEBUG", + "TUNSETGROUP", + "TUNSETIFF", + "TUNSETLINK", + "TUNSETNOCSUM", + "TUNSETOFFLOAD", + "TUNSETOWNER", + "TUNSETPERSIST", + "TUNSETSNDBUF", + "TUNSETTXFILTER", + "TUNSETVNETHDRSZ", + "Tee", + "TerminateProcess", + "Termios", + "Tgkill", + "Time", + "Time_t", + "Times", + "Timespec", + "TimespecToNsec", + "Timeval", + "Timeval32", + "TimevalToNsec", + "Timex", + "Timezoneinformation", + "Tms", + "Token", + "TokenAccessInformation", + "TokenAuditPolicy", + "TokenDefaultDacl", + "TokenElevation", + "TokenElevationType", + "TokenGroups", + "TokenGroupsAndPrivileges", + "TokenHasRestrictions", + "TokenImpersonationLevel", + "TokenIntegrityLevel", + "TokenLinkedToken", + "TokenLogonSid", + "TokenMandatoryPolicy", + "TokenOrigin", + "TokenOwner", + "TokenPrimaryGroup", + "TokenPrivileges", + "TokenRestrictedSids", + "TokenSandBoxInert", + "TokenSessionId", + "TokenSessionReference", + "TokenSource", + "TokenStatistics", + "TokenType", + "TokenUIAccess", + "TokenUser", + "TokenVirtualizationAllowed", + "TokenVirtualizationEnabled", + "Tokenprimarygroup", + "Tokenuser", + "TranslateAccountName", + "TranslateName", + "TransmitFile", + "TransmitFileBuffers", + "Truncate", + "UNIX_PATH_MAX", + "USAGE_MATCH_TYPE_AND", + "USAGE_MATCH_TYPE_OR", + "UTF16FromString", + "UTF16PtrFromString", + "UTF16ToString", + "Ucred", + "Umask", + "Uname", + "Undelete", + "UnixCredentials", + "UnixRights", + "Unlink", + "Unlinkat", + "UnmapViewOfFile", + "Unmount", + "Unsetenv", + "Unshare", + "UserInfo10", + "Ustat", + "Ustat_t", + "Utimbuf", + "Utime", + "Utimes", + "UtimesNano", + "Utsname", + "VDISCARD", + "VDSUSP", + "VEOF", + "VEOL", + "VEOL2", + "VERASE", + "VERASE2", + "VINTR", + "VKILL", + "VLNEXT", + "VMIN", + "VQUIT", + "VREPRINT", + "VSTART", + "VSTATUS", + "VSTOP", + "VSUSP", + "VSWTC", + "VT0", + "VT1", + "VTDLY", + "VTIME", + "VWERASE", + "VirtualLock", + "VirtualUnlock", + "WAIT_ABANDONED", + "WAIT_FAILED", + "WAIT_OBJECT_0", + "WAIT_TIMEOUT", + "WALL", + "WALLSIG", + "WALTSIG", + "WCLONE", + "WCONTINUED", + "WCOREFLAG", + "WEXITED", + "WLINUXCLONE", + "WNOHANG", + "WNOTHREAD", + "WNOWAIT", + "WNOZOMBIE", + "WOPTSCHECKED", + "WORDSIZE", + "WSABuf", + "WSACleanup", + "WSADESCRIPTION_LEN", + "WSAData", + "WSAEACCES", + "WSAECONNABORTED", + "WSAECONNRESET", + "WSAEnumProtocols", + "WSAID_CONNECTEX", + "WSAIoctl", + "WSAPROTOCOL_LEN", + "WSAProtocolChain", + "WSAProtocolInfo", + "WSARecv", + "WSARecvFrom", + "WSASYS_STATUS_LEN", + "WSASend", + "WSASendTo", + "WSASendto", + "WSAStartup", + "WSTOPPED", + "WTRAPPED", + "WUNTRACED", + "Wait4", + "WaitForSingleObject", + "WaitStatus", + "Win32FileAttributeData", + "Win32finddata", + "Write", + "WriteConsole", + "WriteFile", + "X509_ASN_ENCODING", + "XCASE", + "XP1_CONNECTIONLESS", + "XP1_CONNECT_DATA", + "XP1_DISCONNECT_DATA", + "XP1_EXPEDITED_DATA", + "XP1_GRACEFUL_CLOSE", + "XP1_GUARANTEED_DELIVERY", + "XP1_GUARANTEED_ORDER", + "XP1_IFS_HANDLES", + "XP1_MESSAGE_ORIENTED", + "XP1_MULTIPOINT_CONTROL_PLANE", + "XP1_MULTIPOINT_DATA_PLANE", + "XP1_PARTIAL_MESSAGE", + "XP1_PSEUDO_STREAM", + "XP1_QOS_SUPPORTED", + "XP1_SAN_SUPPORT_SDP", + "XP1_SUPPORT_BROADCAST", + "XP1_SUPPORT_MULTIPOINT", + "XP1_UNI_RECV", + "XP1_UNI_SEND", + }, + "syscall/js": { + "CopyBytesToGo", + "CopyBytesToJS", + "Error", + "Func", + "FuncOf", + "Global", + "Null", + "Type", + "TypeBoolean", + "TypeFunction", + "TypeNull", + "TypeNumber", + "TypeObject", + "TypeString", + "TypeSymbol", + "TypeUndefined", + "Undefined", + "Value", + "ValueError", + "ValueOf", + }, + "testing": { + "AllocsPerRun", + "B", + "Benchmark", + "BenchmarkResult", + "Cover", + "CoverBlock", + "CoverMode", + "Coverage", + "F", + "Init", + "InternalBenchmark", + "InternalExample", + "InternalFuzzTarget", + "InternalTest", + "M", + "Main", + "MainStart", + "PB", + "RegisterCover", + "RunBenchmarks", + "RunExamples", + "RunTests", + "Short", + "T", + "TB", + "Testing", + "Verbose", + }, + "testing/fstest": { + "MapFS", + "MapFile", + "TestFS", + }, + "testing/iotest": { + "DataErrReader", + "ErrReader", + "ErrTimeout", + "HalfReader", + "NewReadLogger", + "NewWriteLogger", + "OneByteReader", + "TestReader", + "TimeoutReader", + "TruncateWriter", + }, + "testing/quick": { + "Check", + "CheckEqual", + "CheckEqualError", + "CheckError", + "Config", + "Generator", + "SetupError", + "Value", + }, + "testing/slogtest": { + "TestHandler", + }, + "text/scanner": { + "Char", + "Comment", + "EOF", + "Float", + "GoTokens", + "GoWhitespace", + "Ident", + "Int", + "Position", + "RawString", + "ScanChars", + "ScanComments", + "ScanFloats", + "ScanIdents", + "ScanInts", + "ScanRawStrings", + "ScanStrings", + "Scanner", + "SkipComments", + "String", + "TokenString", + }, + "text/tabwriter": { + "AlignRight", + "Debug", + "DiscardEmptyColumns", + "Escape", + "FilterHTML", + "NewWriter", + "StripEscape", + "TabIndent", + "Writer", + }, + "text/template": { + "ExecError", + "FuncMap", + "HTMLEscape", + "HTMLEscapeString", + "HTMLEscaper", + "IsTrue", + "JSEscape", + "JSEscapeString", + "JSEscaper", + "Must", + "New", + "ParseFS", + "ParseFiles", + "ParseGlob", + "Template", + "URLQueryEscaper", + }, + "text/template/parse": { + "ActionNode", + "BoolNode", + "BranchNode", + "BreakNode", + "ChainNode", + "CommandNode", + "CommentNode", + "ContinueNode", + "DotNode", + "FieldNode", + "IdentifierNode", + "IfNode", + "IsEmptyTree", + "ListNode", + "Mode", + "New", + "NewIdentifier", + "NilNode", + "Node", + "NodeAction", + "NodeBool", + "NodeBreak", + "NodeChain", + "NodeCommand", + "NodeComment", + "NodeContinue", + "NodeDot", + "NodeField", + "NodeIdentifier", + "NodeIf", + "NodeList", + "NodeNil", + "NodeNumber", + "NodePipe", + "NodeRange", + "NodeString", + "NodeTemplate", + "NodeText", + "NodeType", + "NodeVariable", + "NodeWith", + "NumberNode", + "Parse", + "ParseComments", + "PipeNode", + "Pos", + "RangeNode", + "SkipFuncCheck", + "StringNode", + "TemplateNode", + "TextNode", + "Tree", + "VariableNode", + "WithNode", + }, + "time": { + "ANSIC", + "After", + "AfterFunc", + "April", + "August", + "Date", + "DateOnly", + "DateTime", + "December", + "Duration", + "February", + "FixedZone", + "Friday", + "Hour", + "January", + "July", + "June", + "Kitchen", + "Layout", + "LoadLocation", + "LoadLocationFromTZData", + "Local", + "Location", + "March", + "May", + "Microsecond", + "Millisecond", + "Minute", + "Monday", + "Month", + "Nanosecond", + "NewTicker", + "NewTimer", + "November", + "Now", + "October", + "Parse", + "ParseDuration", + "ParseError", + "ParseInLocation", + "RFC1123", + "RFC1123Z", + "RFC3339", + "RFC3339Nano", + "RFC822", + "RFC822Z", + "RFC850", + "RubyDate", + "Saturday", + "Second", + "September", + "Since", + "Sleep", + "Stamp", + "StampMicro", + "StampMilli", + "StampNano", + "Sunday", + "Thursday", + "Tick", + "Ticker", + "Time", + "TimeOnly", + "Timer", + "Tuesday", + "UTC", + "Unix", + "UnixDate", + "UnixMicro", + "UnixMilli", + "Until", + "Wednesday", + "Weekday", + }, + "unicode": { + "ASCII_Hex_Digit", + "Adlam", + "Ahom", + "Anatolian_Hieroglyphs", + "Arabic", + "Armenian", + "Avestan", + "AzeriCase", + "Balinese", + "Bamum", + "Bassa_Vah", + "Batak", + "Bengali", + "Bhaiksuki", + "Bidi_Control", + "Bopomofo", + "Brahmi", + "Braille", + "Buginese", + "Buhid", + "C", + "Canadian_Aboriginal", + "Carian", + "CaseRange", + "CaseRanges", + "Categories", + "Caucasian_Albanian", + "Cc", + "Cf", + "Chakma", + "Cham", + "Cherokee", + "Chorasmian", + "Co", + "Common", + "Coptic", + "Cs", + "Cuneiform", + "Cypriot", + "Cypro_Minoan", + "Cyrillic", + "Dash", + "Deprecated", + "Deseret", + "Devanagari", + "Diacritic", + "Digit", + "Dives_Akuru", + "Dogra", + "Duployan", + "Egyptian_Hieroglyphs", + "Elbasan", + "Elymaic", + "Ethiopic", + "Extender", + "FoldCategory", + "FoldScript", + "Georgian", + "Glagolitic", + "Gothic", + "Grantha", + "GraphicRanges", + "Greek", + "Gujarati", + "Gunjala_Gondi", + "Gurmukhi", + "Han", + "Hangul", + "Hanifi_Rohingya", + "Hanunoo", + "Hatran", + "Hebrew", + "Hex_Digit", + "Hiragana", + "Hyphen", + "IDS_Binary_Operator", + "IDS_Trinary_Operator", + "Ideographic", + "Imperial_Aramaic", + "In", + "Inherited", + "Inscriptional_Pahlavi", + "Inscriptional_Parthian", + "Is", + "IsControl", + "IsDigit", + "IsGraphic", + "IsLetter", + "IsLower", + "IsMark", + "IsNumber", + "IsOneOf", + "IsPrint", + "IsPunct", + "IsSpace", + "IsSymbol", + "IsTitle", + "IsUpper", + "Javanese", + "Join_Control", + "Kaithi", + "Kannada", + "Katakana", + "Kawi", + "Kayah_Li", + "Kharoshthi", + "Khitan_Small_Script", + "Khmer", + "Khojki", + "Khudawadi", + "L", + "Lao", + "Latin", + "Lepcha", + "Letter", + "Limbu", + "Linear_A", + "Linear_B", + "Lisu", + "Ll", + "Lm", + "Lo", + "Logical_Order_Exception", + "Lower", + "LowerCase", + "Lt", + "Lu", + "Lycian", + "Lydian", + "M", + "Mahajani", + "Makasar", + "Malayalam", + "Mandaic", + "Manichaean", + "Marchen", + "Mark", + "Masaram_Gondi", + "MaxASCII", + "MaxCase", + "MaxLatin1", + "MaxRune", + "Mc", + "Me", + "Medefaidrin", + "Meetei_Mayek", + "Mende_Kikakui", + "Meroitic_Cursive", + "Meroitic_Hieroglyphs", + "Miao", + "Mn", + "Modi", + "Mongolian", + "Mro", + "Multani", + "Myanmar", + "N", + "Nabataean", + "Nag_Mundari", + "Nandinagari", + "Nd", + "New_Tai_Lue", + "Newa", + "Nko", + "Nl", + "No", + "Noncharacter_Code_Point", + "Number", + "Nushu", + "Nyiakeng_Puachue_Hmong", + "Ogham", + "Ol_Chiki", + "Old_Hungarian", + "Old_Italic", + "Old_North_Arabian", + "Old_Permic", + "Old_Persian", + "Old_Sogdian", + "Old_South_Arabian", + "Old_Turkic", + "Old_Uyghur", + "Oriya", + "Osage", + "Osmanya", + "Other", + "Other_Alphabetic", + "Other_Default_Ignorable_Code_Point", + "Other_Grapheme_Extend", + "Other_ID_Continue", + "Other_ID_Start", + "Other_Lowercase", + "Other_Math", + "Other_Uppercase", + "P", + "Pahawh_Hmong", + "Palmyrene", + "Pattern_Syntax", + "Pattern_White_Space", + "Pau_Cin_Hau", + "Pc", + "Pd", + "Pe", + "Pf", + "Phags_Pa", + "Phoenician", + "Pi", + "Po", + "Prepended_Concatenation_Mark", + "PrintRanges", + "Properties", + "Ps", + "Psalter_Pahlavi", + "Punct", + "Quotation_Mark", + "Radical", + "Range16", + "Range32", + "RangeTable", + "Regional_Indicator", + "Rejang", + "ReplacementChar", + "Runic", + "S", + "STerm", + "Samaritan", + "Saurashtra", + "Sc", + "Scripts", + "Sentence_Terminal", + "Sharada", + "Shavian", + "Siddham", + "SignWriting", + "SimpleFold", + "Sinhala", + "Sk", + "Sm", + "So", + "Soft_Dotted", + "Sogdian", + "Sora_Sompeng", + "Soyombo", + "Space", + "SpecialCase", + "Sundanese", + "Syloti_Nagri", + "Symbol", + "Syriac", + "Tagalog", + "Tagbanwa", + "Tai_Le", + "Tai_Tham", + "Tai_Viet", + "Takri", + "Tamil", + "Tangsa", + "Tangut", + "Telugu", + "Terminal_Punctuation", + "Thaana", + "Thai", + "Tibetan", + "Tifinagh", + "Tirhuta", + "Title", + "TitleCase", + "To", + "ToLower", + "ToTitle", + "ToUpper", + "Toto", + "TurkishCase", + "Ugaritic", + "Unified_Ideograph", + "Upper", + "UpperCase", + "UpperLower", + "Vai", + "Variation_Selector", + "Version", + "Vithkuqi", + "Wancho", + "Warang_Citi", + "White_Space", + "Yezidi", + "Yi", + "Z", + "Zanabazar_Square", + "Zl", + "Zp", + "Zs", + }, + "unicode/utf16": { + "AppendRune", + "Decode", + "DecodeRune", + "Encode", + "EncodeRune", + "IsSurrogate", + }, + "unicode/utf8": { + "AppendRune", + "DecodeLastRune", + "DecodeLastRuneInString", + "DecodeRune", + "DecodeRuneInString", + "EncodeRune", + "FullRune", + "FullRuneInString", + "MaxRune", + "RuneCount", + "RuneCountInString", + "RuneError", + "RuneLen", + "RuneSelf", + "RuneStart", + "UTFMax", + "Valid", + "ValidRune", + "ValidString", + }, + "unsafe": { + "Add", + "Alignof", + "Offsetof", + "Pointer", + "Sizeof", + "Slice", + "SliceData", + "String", + "StringData", + }, +} diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go index d9950b1f0..44719de17 100644 --- a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go +++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go @@ -5,10 +5,6 @@ // Package packagesinternal exposes internal-only fields from go/packages. package packagesinternal -import ( - "golang.org/x/tools/internal/gocommand" -) - var GetForTest = func(p interface{}) string { return "" } var GetDepsErrors = func(p interface{}) []*PackageError { return nil } @@ -18,10 +14,6 @@ type PackageError struct { Err string // the error itself } -var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } - -var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} - var TypecheckCgo int var DepsErrors int // must be set as a LoadMode to call GetDepsErrors var ForTest int // must be set as a LoadMode to call GetForTest diff --git a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go b/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go index a3fb2d4f2..7e638ec24 100644 --- a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go +++ b/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go @@ -7,7 +7,9 @@ package tokeninternal import ( + "fmt" "go/token" + "sort" "sync" "unsafe" ) @@ -57,3 +59,93 @@ func GetLines(file *token.File) []int { panic("unexpected token.File size") } } + +// AddExistingFiles adds the specified files to the FileSet if they +// are not already present. It panics if any pair of files in the +// resulting FileSet would overlap. +func AddExistingFiles(fset *token.FileSet, files []*token.File) { + // Punch through the FileSet encapsulation. + type tokenFileSet struct { + // This type remained essentially consistent from go1.16 to go1.21. + mutex sync.RWMutex + base int + files []*token.File + _ *token.File // changed to atomic.Pointer[token.File] in go1.19 + } + + // If the size of token.FileSet changes, this will fail to compile. + const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) + var _ [-delta * delta]int + + type uP = unsafe.Pointer + var ptr *tokenFileSet + *(*uP)(uP(&ptr)) = uP(fset) + ptr.mutex.Lock() + defer ptr.mutex.Unlock() + + // Merge and sort. + newFiles := append(ptr.files, files...) + sort.Slice(newFiles, func(i, j int) bool { + return newFiles[i].Base() < newFiles[j].Base() + }) + + // Reject overlapping files. + // Discard adjacent identical files. + out := newFiles[:0] + for i, file := range newFiles { + if i > 0 { + prev := newFiles[i-1] + if file == prev { + continue + } + if prev.Base()+prev.Size()+1 > file.Base() { + panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)", + prev.Name(), prev.Base(), prev.Base()+prev.Size(), + file.Name(), file.Base(), file.Base()+file.Size())) + } + } + out = append(out, file) + } + newFiles = out + + ptr.files = newFiles + + // Advance FileSet.Base(). + if len(newFiles) > 0 { + last := newFiles[len(newFiles)-1] + newBase := last.Base() + last.Size() + 1 + if ptr.base < newBase { + ptr.base = newBase + } + } +} + +// FileSetFor returns a new FileSet containing a sequence of new Files with +// the same base, size, and line as the input files, for use in APIs that +// require a FileSet. +// +// Precondition: the input files must be non-overlapping, and sorted in order +// of their Base. +func FileSetFor(files ...*token.File) *token.FileSet { + fset := token.NewFileSet() + for _, f := range files { + f2 := fset.AddFile(f.Name(), f.Base(), f.Size()) + lines := GetLines(f) + f2.SetLines(lines) + } + return fset +} + +// CloneFileSet creates a new FileSet holding all files in fset. It does not +// create copies of the token.Files in fset: they are added to the resulting +// FileSet unmodified. +func CloneFileSet(fset *token.FileSet) *token.FileSet { + var files []*token.File + fset.Iterate(func(f *token.File) bool { + files = append(files, f) + return true + }) + newFileSet := token.NewFileSet() + AddExistingFiles(newFileSet, files) + return newFileSet +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/common.go b/vendor/golang.org/x/tools/internal/typeparams/common.go index 25a1426d3..cdab98853 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/common.go +++ b/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -23,6 +23,7 @@ package typeparams import ( + "fmt" "go/ast" "go/token" "go/types" @@ -41,7 +42,7 @@ func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Ex switch e := n.(type) { case *ast.IndexExpr: return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack - case *IndexListExpr: + case *ast.IndexListExpr: return e.X, e.Lbrack, e.Indices, e.Rbrack } return nil, token.NoPos, nil, token.NoPos @@ -62,7 +63,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke Rbrack: rbrack, } default: - return &IndexListExpr{ + return &ast.IndexListExpr{ X: x, Lbrack: lbrack, Indices: indices, @@ -73,7 +74,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke // IsTypeParam reports whether t is a type parameter. func IsTypeParam(t types.Type) bool { - _, ok := t.(*TypeParam) + _, ok := t.(*types.TypeParam) return ok } @@ -87,7 +88,6 @@ func IsTypeParam(t types.Type) bool { func OriginMethod(fn *types.Func) *types.Func { recv := fn.Type().(*types.Signature).Recv() if recv == nil { - return fn } base := recv.Type() @@ -100,12 +100,37 @@ func OriginMethod(fn *types.Func) *types.Func { // Receiver is a *types.Interface. return fn } - if ForNamed(named).Len() == 0 { + if named.TypeParams().Len() == 0 { // Receiver base has no type parameters, so we can avoid the lookup below. return fn } - orig := NamedTypeOrigin(named) + orig := named.Origin() gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) + + // This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In: + // package p + // type T *int + // func (*T) f() {} + // LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}. + // Here we make them consistent by force. + // (The go/types bug is general, but this workaround is reached only + // for generic T thanks to the early return above.) + if gfn == nil { + mset := types.NewMethodSet(types.NewPointer(orig)) + for i := 0; i < mset.Len(); i++ { + m := mset.At(i) + if m.Obj().Id() == fn.Id() { + gfn = m.Obj() + break + } + } + } + + // In golang/go#61196, we observe another crash, this time inexplicable. + if gfn == nil { + panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods())) + } + return gfn.(*types.Func) } @@ -132,7 +157,7 @@ func OriginMethod(fn *types.Func) *types.Func { // // In this case, GenericAssignableTo reports that instantiations of Container // are assignable to the corresponding instantiation of Interface. -func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { +func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { // If V and T are not both named, or do not have matching non-empty type // parameter lists, fall back on types.AssignableTo. @@ -142,9 +167,9 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { return types.AssignableTo(V, T) } - vtparams := ForNamed(VN) - ttparams := ForNamed(TN) - if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { + vtparams := VN.TypeParams() + ttparams := TN.TypeParams() + if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 { return types.AssignableTo(V, T) } @@ -157,7 +182,7 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { // Minor optimization: ensure we share a context across the two // instantiations below. if ctxt == nil { - ctxt = NewContext() + ctxt = types.NewContext() } var targs []types.Type @@ -165,12 +190,12 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { targs = append(targs, vtparams.At(i)) } - vinst, err := Instantiate(ctxt, V, targs, true) + vinst, err := types.Instantiate(ctxt, V, targs, true) if err != nil { panic("type parameters should satisfy their own constraints") } - tinst, err := Instantiate(ctxt, T, targs, true) + tinst, err := types.Instantiate(ctxt, T, targs, true) if err != nil { return false } diff --git a/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/vendor/golang.org/x/tools/internal/typeparams/coretype.go index 993135ec9..7ea8840ea 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/coretype.go +++ b/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -81,13 +81,13 @@ func CoreType(T types.Type) types.Type { // restrictions may be arbitrarily complex. For example, consider the // following: // -// type A interface{ ~string|~[]byte } +// type A interface{ ~string|~[]byte } // -// type B interface{ int|string } +// type B interface{ int|string } // -// type C interface { ~string|~int } +// type C interface { ~string|~int } // -// type T[P interface{ A|B; C }] int +// type T[P interface{ A|B; C }] int // // In this example, the structural type restriction of P is ~string|int: A|B // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, @@ -108,15 +108,15 @@ func CoreType(T types.Type) types.Type { // // _NormalTerms makes no guarantees about the order of terms, except that it // is deterministic. -func _NormalTerms(typ types.Type) ([]*Term, error) { +func _NormalTerms(typ types.Type) ([]*types.Term, error) { switch typ := typ.(type) { - case *TypeParam: + case *types.TypeParam: return StructuralTerms(typ) - case *Union: + case *types.Union: return UnionTermSet(typ) case *types.Interface: return InterfaceTermSet(typ) default: - return []*Term{NewTerm(false, typ)}, nil + return []*types.Term{types.NewTerm(false, typ)}, nil } } diff --git a/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go b/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go deleted file mode 100644 index 18212390e..000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = false diff --git a/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go b/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go deleted file mode 100644 index d67148823..000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -// Note: this constant is in a separate file as this is the only acceptable -// diff between the <1.18 API of this package and the 1.18 API. - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = true diff --git a/vendor/golang.org/x/tools/internal/typeparams/normalize.go b/vendor/golang.org/x/tools/internal/typeparams/normalize.go index 9c631b651..93c80fdc9 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/normalize.go +++ b/vendor/golang.org/x/tools/internal/typeparams/normalize.go @@ -60,7 +60,7 @@ var ErrEmptyTypeSet = errors.New("empty type set") // // StructuralTerms makes no guarantees about the order of terms, except that it // is deterministic. -func StructuralTerms(tparam *TypeParam) ([]*Term, error) { +func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { constraint := tparam.Constraint() if constraint == nil { return nil, fmt.Errorf("%s has nil constraint", tparam) @@ -78,7 +78,7 @@ func StructuralTerms(tparam *TypeParam) ([]*Term, error) { // // See the documentation of StructuralTerms for more information on // normalization. -func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { +func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { return computeTermSet(iface) } @@ -88,11 +88,11 @@ func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { // // See the documentation of StructuralTerms for more information on // normalization. -func UnionTermSet(union *Union) ([]*Term, error) { +func UnionTermSet(union *types.Union) ([]*types.Term, error) { return computeTermSet(union) } -func computeTermSet(typ types.Type) ([]*Term, error) { +func computeTermSet(typ types.Type) ([]*types.Term, error) { tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) if err != nil { return nil, err @@ -103,9 +103,9 @@ func computeTermSet(typ types.Type) ([]*Term, error) { if tset.terms.isAll() { return nil, nil } - var terms []*Term + var terms []*types.Term for _, term := range tset.terms { - terms = append(terms, NewTerm(term.tilde, term.typ)) + terms = append(terms, types.NewTerm(term.tilde, term.typ)) } return terms, nil } @@ -162,7 +162,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in tset.terms = allTermlist for i := 0; i < u.NumEmbeddeds(); i++ { embedded := u.EmbeddedType(i) - if _, ok := embedded.Underlying().(*TypeParam); ok { + if _, ok := embedded.Underlying().(*types.TypeParam); ok { return nil, fmt.Errorf("invalid embedded type %T", embedded) } tset2, err := computeTermSetInternal(embedded, seen, depth+1) @@ -171,7 +171,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in } tset.terms = tset.terms.intersect(tset2.terms) } - case *Union: + case *types.Union: // The term set of a union is the union of term sets of its terms. tset.terms = nil for i := 0; i < u.Len(); i++ { @@ -184,7 +184,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in return nil, err } terms = tset2.terms - case *TypeParam, *Union: + case *types.TypeParam, *types.Union: // A stand-alone type parameter or union is not permitted as union // term. return nil, fmt.Errorf("invalid union term %T", t) @@ -199,7 +199,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) } } - case *TypeParam: + case *types.TypeParam: panic("unreachable") default: // For all other types, the term set is just a single non-tilde term diff --git a/vendor/golang.org/x/tools/internal/typeparams/termlist.go b/vendor/golang.org/x/tools/internal/typeparams/termlist.go index 933106a23..cbd12f801 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/termlist.go +++ b/vendor/golang.org/x/tools/internal/typeparams/termlist.go @@ -30,7 +30,7 @@ func (xl termlist) String() string { var buf bytes.Buffer for i, x := range xl { if i > 0 { - buf.WriteString(" ∪ ") + buf.WriteString(" | ") } buf.WriteString(x.String()) } diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go deleted file mode 100644 index b4788978f..000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -import ( - "go/ast" - "go/token" - "go/types" -) - -func unsupported() { - panic("type parameters are unsupported at this go version") -} - -// IndexListExpr is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type IndexListExpr struct { - ast.Expr - X ast.Expr // expression - Lbrack token.Pos // position of "[" - Indices []ast.Expr // index expressions - Rbrack token.Pos // position of "]" -} - -// ForTypeSpec returns an empty field list, as type parameters on not supported -// at this Go version. -func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { - return nil -} - -// ForFuncType returns an empty field list, as type parameters are not -// supported at this Go version. -func ForFuncType(*ast.FuncType) *ast.FieldList { - return nil -} - -// TypeParam is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type TypeParam struct{ types.Type } - -func (*TypeParam) Index() int { unsupported(); return 0 } -func (*TypeParam) Constraint() types.Type { unsupported(); return nil } -func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil } - -// TypeParamList is a placeholder for an empty type parameter list. -type TypeParamList struct{} - -func (*TypeParamList) Len() int { return 0 } -func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } - -// TypeList is a placeholder for an empty type list. -type TypeList struct{} - -func (*TypeList) Len() int { return 0 } -func (*TypeList) At(int) types.Type { unsupported(); return nil } - -// NewTypeParam is unsupported at this Go version, and panics. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - unsupported() - return nil -} - -// SetTypeParamConstraint is unsupported at this Go version, and panics. -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - unsupported() -} - -// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or -// typeParams is non-empty. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - if len(recvTypeParams) != 0 || len(typeParams) != 0 { - panic("signatures cannot have type parameters at this Go version") - } - return types.NewSignature(recv, params, results, variadic) -} - -// ForSignature returns an empty slice. -func ForSignature(*types.Signature) *TypeParamList { - return nil -} - -// RecvTypeParams returns a nil slice. -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return nil -} - -// IsComparable returns false, as no interfaces are type-restricted at this Go -// version. -func IsComparable(*types.Interface) bool { - return false -} - -// IsMethodSet returns true, as no interfaces are type-restricted at this Go -// version. -func IsMethodSet(*types.Interface) bool { - return true -} - -// IsImplicit returns false, as no interfaces are implicit at this Go version. -func IsImplicit(*types.Interface) bool { - return false -} - -// MarkImplicit does nothing, because this Go version does not have implicit -// interfaces. -func MarkImplicit(*types.Interface) {} - -// ForNamed returns an empty type parameter list, as type parameters are not -// supported at this Go version. -func ForNamed(*types.Named) *TypeParamList { - return nil -} - -// SetForNamed panics if tparams is non-empty. -func SetForNamed(_ *types.Named, tparams []*TypeParam) { - if len(tparams) > 0 { - unsupported() - } -} - -// NamedTypeArgs returns nil. -func NamedTypeArgs(*types.Named) *TypeList { - return nil -} - -// NamedTypeOrigin is the identity method at this Go version. -func NamedTypeOrigin(named *types.Named) types.Type { - return named -} - -// Term holds information about a structural type restriction. -type Term struct { - tilde bool - typ types.Type -} - -func (m *Term) Tilde() bool { return m.tilde } -func (m *Term) Type() types.Type { return m.typ } -func (m *Term) String() string { - pre := "" - if m.tilde { - pre = "~" - } - return pre + m.typ.String() -} - -// NewTerm is unsupported at this Go version, and panics. -func NewTerm(tilde bool, typ types.Type) *Term { - return &Term{tilde, typ} -} - -// Union is a placeholder type, as type parameters are not supported at this Go -// version. Its methods panic on use. -type Union struct{ types.Type } - -func (*Union) Len() int { return 0 } -func (*Union) Term(i int) *Term { unsupported(); return nil } - -// NewUnion is unsupported at this Go version, and panics. -func NewUnion(terms []*Term) *Union { - unsupported() - return nil -} - -// InitInstanceInfo is a noop at this Go version. -func InitInstanceInfo(*types.Info) {} - -// Instance is a placeholder type, as type parameters are not supported at this -// Go version. -type Instance struct { - TypeArgs *TypeList - Type types.Type -} - -// GetInstances returns a nil map, as type parameters are not supported at this -// Go version. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } - -// Context is a placeholder type, as type parameters are not supported at -// this Go version. -type Context struct{} - -// NewContext returns a placeholder Context instance. -func NewContext() *Context { - return &Context{} -} - -// Instantiate is unsupported on this Go version, and panics. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - unsupported() - return nil, nil -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go deleted file mode 100644 index 114a36b86..000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -import ( - "go/ast" - "go/types" -) - -// IndexListExpr is an alias for ast.IndexListExpr. -type IndexListExpr = ast.IndexListExpr - -// ForTypeSpec returns n.TypeParams. -func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// ForFuncType returns n.TypeParams. -func ForFuncType(n *ast.FuncType) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// TypeParam is an alias for types.TypeParam -type TypeParam = types.TypeParam - -// TypeParamList is an alias for types.TypeParamList -type TypeParamList = types.TypeParamList - -// TypeList is an alias for types.TypeList -type TypeList = types.TypeList - -// NewTypeParam calls types.NewTypeParam. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - return types.NewTypeParam(name, constraint) -} - -// SetTypeParamConstraint calls tparam.SetConstraint(constraint). -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - tparam.SetConstraint(constraint) -} - -// NewSignatureType calls types.NewSignatureType. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) -} - -// ForSignature returns sig.TypeParams() -func ForSignature(sig *types.Signature) *TypeParamList { - return sig.TypeParams() -} - -// RecvTypeParams returns sig.RecvTypeParams(). -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return sig.RecvTypeParams() -} - -// IsComparable calls iface.IsComparable(). -func IsComparable(iface *types.Interface) bool { - return iface.IsComparable() -} - -// IsMethodSet calls iface.IsMethodSet(). -func IsMethodSet(iface *types.Interface) bool { - return iface.IsMethodSet() -} - -// IsImplicit calls iface.IsImplicit(). -func IsImplicit(iface *types.Interface) bool { - return iface.IsImplicit() -} - -// MarkImplicit calls iface.MarkImplicit(). -func MarkImplicit(iface *types.Interface) { - iface.MarkImplicit() -} - -// ForNamed extracts the (possibly empty) type parameter object list from -// named. -func ForNamed(named *types.Named) *TypeParamList { - return named.TypeParams() -} - -// SetForNamed sets the type params tparams on n. Each tparam must be of -// dynamic type *types.TypeParam. -func SetForNamed(n *types.Named, tparams []*TypeParam) { - n.SetTypeParams(tparams) -} - -// NamedTypeArgs returns named.TypeArgs(). -func NamedTypeArgs(named *types.Named) *TypeList { - return named.TypeArgs() -} - -// NamedTypeOrigin returns named.Orig(). -func NamedTypeOrigin(named *types.Named) types.Type { - return named.Origin() -} - -// Term is an alias for types.Term. -type Term = types.Term - -// NewTerm calls types.NewTerm. -func NewTerm(tilde bool, typ types.Type) *Term { - return types.NewTerm(tilde, typ) -} - -// Union is an alias for types.Union -type Union = types.Union - -// NewUnion calls types.NewUnion. -func NewUnion(terms []*Term) *Union { - return types.NewUnion(terms) -} - -// InitInstanceInfo initializes info to record information about type and -// function instances. -func InitInstanceInfo(info *types.Info) { - info.Instances = make(map[*ast.Ident]types.Instance) -} - -// Instance is an alias for types.Instance. -type Instance = types.Instance - -// GetInstances returns info.Instances. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { - return info.Instances -} - -// Context is an alias for types.Context. -type Context = types.Context - -// NewContext calls types.NewContext. -func NewContext() *Context { - return types.NewContext() -} - -// Instantiate calls types.Instantiate. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - return types.Instantiate(ctxt, typ, targs, validate) -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go index 7ddee28d9..7350bb702 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go +++ b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go @@ -10,11 +10,10 @@ import "go/types" // A term describes elementary type sets: // -// ∅: (*term)(nil) == ∅ // set of no types (empty set) -// ð“¤: &term{} == 𓤠// set of all types (ð“¤niverse) -// T: &term{false, T} == {T} // set of type T -// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t -// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// ð“¤: &term{} == 𓤠// set of all types (ð“¤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t type term struct { tilde bool // valid if typ != nil typ types.Type diff --git a/vendor/golang.org/x/tools/internal/versions/gover.go b/vendor/golang.org/x/tools/internal/versions/gover.go new file mode 100644 index 000000000..bbabcd22e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/gover.go @@ -0,0 +1,172 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a fork of internal/gover for use by x/tools until +// go1.21 and earlier are no longer supported by x/tools. + +package versions + +import "strings" + +// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type gover struct { + major string // decimal + minor string // decimal or "" + patch string // decimal or "" + kind string // "", "alpha", "beta", "rc" + pre string // decimal or "" +} + +// compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func compare(x, y string) int { + vx := parse(x) + vy := parse(y) + + if c := cmpInt(vx.major, vy.major); c != 0 { + return c + } + if c := cmpInt(vx.minor, vy.minor); c != 0 { + return c + } + if c := cmpInt(vx.patch, vy.patch); c != 0 { + return c + } + if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := cmpInt(vx.pre, vy.pre); c != 0 { + return c + } + return 0 +} + +// lang returns the Go language version. For example, lang("1.2.3") == "1.2". +func lang(x string) string { + v := parse(x) + if v.minor == "" || v.major == "1" && v.minor == "0" { + return v.major + } + return v.major + "." + v.minor +} + +// isValid reports whether the version x is valid. +func isValid(x string) bool { + return parse(x) != gover{} +} + +// parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func parse(x string) gover { + var v gover + + // Parse major version. + var ok bool + v.major, x, ok = cutInt(x) + if !ok { + return gover{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.minor = "0" + v.patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return gover{} + } + + // Parse minor version. + v.minor, x, ok = cutInt(x[1:]) + if !ok { + return gover{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if cmpInt(v.minor, "21") < 0 { + v.patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return gover{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return gover{} + } + i++ + } + if i == 0 { + return gover{} + } + v.kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.pre, x, ok = cutInt(x) + if !ok || x != "" { + return gover{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func cmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/types.go b/vendor/golang.org/x/tools/internal/versions/types.go new file mode 100644 index 000000000..562eef21f --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types.go @@ -0,0 +1,19 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package versions + +import ( + "go/types" +) + +// GoVersion returns the Go version of the type package. +// It returns zero if no version can be determined. +func GoVersion(pkg *types.Package) string { + // TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25. + if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok { + return pkg.GoVersion() + } + return "" +} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go121.go b/vendor/golang.org/x/tools/internal/versions/types_go121.go new file mode 100644 index 000000000..a7b79207a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go121.go @@ -0,0 +1,20 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.22 +// +build !go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions always reports the a file's Go version as the +// zero version at this Go version. +func FileVersions(info *types.Info, file *ast.File) string { return "" } + +// InitFileVersions is a noop at this Go version. +func InitFileVersions(*types.Info) {} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go122.go b/vendor/golang.org/x/tools/internal/versions/types_go122.go new file mode 100644 index 000000000..7b9ba89a8 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go122.go @@ -0,0 +1,24 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 +// +build go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions maps a file to the file's semantic Go version. +// The reported version is the zero version if a version cannot be determined. +func FileVersions(info *types.Info, file *ast.File) string { + return info.FileVersions[file] +} + +// InitFileVersions initializes info to record Go versions for Go files. +func InitFileVersions(info *types.Info) { + info.FileVersions = make(map[*ast.File]string) +} diff --git a/vendor/golang.org/x/tools/internal/versions/versions.go b/vendor/golang.org/x/tools/internal/versions/versions.go new file mode 100644 index 000000000..e16f6c33a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/versions.go @@ -0,0 +1,52 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package versions + +// Note: If we use build tags to use go/versions when go >=1.22, +// we run into go.dev/issue/53737. Under some operations users would see an +// import of "go/versions" even if they would not compile the file. +// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include +// For this reason, this library just a clone of go/versions for the moment. + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { + v := lang(stripGo(x)) + if v == "" { + return "" + } + return x[:2+len(v)] // "go"+v without allocation +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return isValid(stripGo(x)) } + +// stripGo converts from a "go1.21" version to a "1.21" version. +// If v does not start with "go", stripGo returns the empty string (a known invalid version). +func stripGo(v string) string { + if len(v) < 2 || v[:2] != "go" { + return "" + } + return v[2:] +} diff --git a/vendor/google.golang.org/appengine/internal/api.go b/vendor/google.golang.org/appengine/internal/api.go index 721053c20..0569f5dd4 100644 --- a/vendor/google.golang.org/appengine/internal/api.go +++ b/vendor/google.golang.org/appengine/internal/api.go @@ -2,12 +2,14 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build !appengine // +build !appengine package internal import ( "bytes" + "context" "errors" "fmt" "io/ioutil" @@ -24,7 +26,6 @@ import ( "time" "github.com/golang/protobuf/proto" - netcontext "golang.org/x/net/context" basepb "google.golang.org/appengine/internal/base" logpb "google.golang.org/appengine/internal/log" @@ -32,8 +33,7 @@ import ( ) const ( - apiPath = "/rpc_http" - defaultTicketSuffix = "/default.20150612t184001.0" + apiPath = "/rpc_http" ) var ( @@ -65,21 +65,22 @@ var ( IdleConnTimeout: 90 * time.Second, }, } - - defaultTicketOnce sync.Once - defaultTicket string - backgroundContextOnce sync.Once - backgroundContext netcontext.Context ) -func apiURL() *url.URL { +func apiURL(ctx context.Context) *url.URL { host, port := "appengine.googleapis.internal", "10001" if h := os.Getenv("API_HOST"); h != "" { host = h } + if hostOverride := ctx.Value(apiHostOverrideKey); hostOverride != nil { + host = hostOverride.(string) + } if p := os.Getenv("API_PORT"); p != "" { port = p } + if portOverride := ctx.Value(apiPortOverrideKey); portOverride != nil { + port = portOverride.(string) + } return &url.URL{ Scheme: "http", Host: host + ":" + port, @@ -87,82 +88,97 @@ func apiURL() *url.URL { } } -func handleHTTP(w http.ResponseWriter, r *http.Request) { - c := &context{ - req: r, - outHeader: w.Header(), - apiURL: apiURL(), - } - r = r.WithContext(withContext(r.Context(), c)) - c.req = r - - stopFlushing := make(chan int) +// Middleware wraps an http handler so that it can make GAE API calls +func Middleware(next http.Handler) http.Handler { + return handleHTTPMiddleware(executeRequestSafelyMiddleware(next)) +} - // Patch up RemoteAddr so it looks reasonable. - if addr := r.Header.Get(userIPHeader); addr != "" { - r.RemoteAddr = addr - } else if addr = r.Header.Get(remoteAddrHeader); addr != "" { - r.RemoteAddr = addr - } else { - // Should not normally reach here, but pick a sensible default anyway. - r.RemoteAddr = "127.0.0.1" - } - // The address in the headers will most likely be of these forms: - // 123.123.123.123 - // 2001:db8::1 - // net/http.Request.RemoteAddr is specified to be in "IP:port" form. - if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { - // Assume the remote address is only a host; add a default port. - r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") - } +func handleHTTPMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c := &aeContext{ + req: r, + outHeader: w.Header(), + } + r = r.WithContext(withContext(r.Context(), c)) + c.req = r + + stopFlushing := make(chan int) + + // Patch up RemoteAddr so it looks reasonable. + if addr := r.Header.Get(userIPHeader); addr != "" { + r.RemoteAddr = addr + } else if addr = r.Header.Get(remoteAddrHeader); addr != "" { + r.RemoteAddr = addr + } else { + // Should not normally reach here, but pick a sensible default anyway. + r.RemoteAddr = "127.0.0.1" + } + // The address in the headers will most likely be of these forms: + // 123.123.123.123 + // 2001:db8::1 + // net/http.Request.RemoteAddr is specified to be in "IP:port" form. + if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { + // Assume the remote address is only a host; add a default port. + r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") + } - // Start goroutine responsible for flushing app logs. - // This is done after adding c to ctx.m (and stopped before removing it) - // because flushing logs requires making an API call. - go c.logFlusher(stopFlushing) + if logToLogservice() { + // Start goroutine responsible for flushing app logs. + // This is done after adding c to ctx.m (and stopped before removing it) + // because flushing logs requires making an API call. + go c.logFlusher(stopFlushing) + } - executeRequestSafely(c, r) - c.outHeader = nil // make sure header changes aren't respected any more + next.ServeHTTP(c, r) + c.outHeader = nil // make sure header changes aren't respected any more - stopFlushing <- 1 // any logging beyond this point will be dropped + flushed := make(chan struct{}) + if logToLogservice() { + stopFlushing <- 1 // any logging beyond this point will be dropped - // Flush any pending logs asynchronously. - c.pendingLogs.Lock() - flushes := c.pendingLogs.flushes - if len(c.pendingLogs.lines) > 0 { - flushes++ - } - c.pendingLogs.Unlock() - flushed := make(chan struct{}) - go func() { - defer close(flushed) - // Force a log flush, because with very short requests we - // may not ever flush logs. - c.flushLog(true) - }() - w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) + // Flush any pending logs asynchronously. + c.pendingLogs.Lock() + flushes := c.pendingLogs.flushes + if len(c.pendingLogs.lines) > 0 { + flushes++ + } + c.pendingLogs.Unlock() + go func() { + defer close(flushed) + // Force a log flush, because with very short requests we + // may not ever flush logs. + c.flushLog(true) + }() + w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) + } - // Avoid nil Write call if c.Write is never called. - if c.outCode != 0 { - w.WriteHeader(c.outCode) - } - if c.outBody != nil { - w.Write(c.outBody) - } - // Wait for the last flush to complete before returning, - // otherwise the security ticket will not be valid. - <-flushed + // Avoid nil Write call if c.Write is never called. + if c.outCode != 0 { + w.WriteHeader(c.outCode) + } + if c.outBody != nil { + w.Write(c.outBody) + } + if logToLogservice() { + // Wait for the last flush to complete before returning, + // otherwise the security ticket will not be valid. + <-flushed + } + }) } -func executeRequestSafely(c *context, r *http.Request) { - defer func() { - if x := recover(); x != nil { - logf(c, 4, "%s", renderPanic(x)) // 4 == critical - c.outCode = 500 - } - }() +func executeRequestSafelyMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if x := recover(); x != nil { + c := w.(*aeContext) + logf(c, 4, "%s", renderPanic(x)) // 4 == critical + c.outCode = 500 + } + }() - http.DefaultServeMux.ServeHTTP(c, r) + next.ServeHTTP(w, r) + }) } func renderPanic(x interface{}) string { @@ -204,9 +220,9 @@ func renderPanic(x interface{}) string { return string(buf) } -// context represents the context of an in-flight HTTP request. +// aeContext represents the aeContext of an in-flight HTTP request. // It implements the appengine.Context and http.ResponseWriter interfaces. -type context struct { +type aeContext struct { req *http.Request outCode int @@ -218,8 +234,6 @@ type context struct { lines []*logpb.UserAppLogLine flushes int } - - apiURL *url.URL } var contextKey = "holds a *context" @@ -227,8 +241,8 @@ var contextKey = "holds a *context" // jointContext joins two contexts in a superficial way. // It takes values and timeouts from a base context, and only values from another context. type jointContext struct { - base netcontext.Context - valuesOnly netcontext.Context + base context.Context + valuesOnly context.Context } func (c jointContext) Deadline() (time.Time, bool) { @@ -252,94 +266,54 @@ func (c jointContext) Value(key interface{}) interface{} { // fromContext returns the App Engine context or nil if ctx is not // derived from an App Engine context. -func fromContext(ctx netcontext.Context) *context { - c, _ := ctx.Value(&contextKey).(*context) +func fromContext(ctx context.Context) *aeContext { + c, _ := ctx.Value(&contextKey).(*aeContext) return c } -func withContext(parent netcontext.Context, c *context) netcontext.Context { - ctx := netcontext.WithValue(parent, &contextKey, c) +func withContext(parent context.Context, c *aeContext) context.Context { + ctx := context.WithValue(parent, &contextKey, c) if ns := c.req.Header.Get(curNamespaceHeader); ns != "" { ctx = withNamespace(ctx, ns) } return ctx } -func toContext(c *context) netcontext.Context { - return withContext(netcontext.Background(), c) +func toContext(c *aeContext) context.Context { + return withContext(context.Background(), c) } -func IncomingHeaders(ctx netcontext.Context) http.Header { +func IncomingHeaders(ctx context.Context) http.Header { if c := fromContext(ctx); c != nil { return c.req.Header } return nil } -func ReqContext(req *http.Request) netcontext.Context { +func ReqContext(req *http.Request) context.Context { return req.Context() } -func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { +func WithContext(parent context.Context, req *http.Request) context.Context { return jointContext{ base: parent, valuesOnly: req.Context(), } } -// DefaultTicket returns a ticket used for background context or dev_appserver. -func DefaultTicket() string { - defaultTicketOnce.Do(func() { - if IsDevAppServer() { - defaultTicket = "testapp" + defaultTicketSuffix - return - } - appID := partitionlessAppID() - escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1) - majVersion := VersionID(nil) - if i := strings.Index(majVersion, "."); i > 0 { - majVersion = majVersion[:i] - } - defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID()) - }) - return defaultTicket -} - -func BackgroundContext() netcontext.Context { - backgroundContextOnce.Do(func() { - // Compute background security ticket. - ticket := DefaultTicket() - - c := &context{ - req: &http.Request{ - Header: http.Header{ - ticketHeader: []string{ticket}, - }, - }, - apiURL: apiURL(), - } - backgroundContext = toContext(c) - - // TODO(dsymonds): Wire up the shutdown handler to do a final flush. - go c.logFlusher(make(chan int)) - }) - - return backgroundContext -} - // RegisterTestRequest registers the HTTP request req for testing, such that -// any API calls are sent to the provided URL. It returns a closure to delete -// the registration. +// any API calls are sent to the provided URL. // It should only be used by aetest package. -func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) { - c := &context{ - req: req, - apiURL: apiURL, - } - ctx := withContext(decorate(req.Context()), c) - req = req.WithContext(ctx) - c.req = req - return req, func() {} +func RegisterTestRequest(req *http.Request, apiURL *url.URL, appID string) *http.Request { + ctx := req.Context() + ctx = withAPIHostOverride(ctx, apiURL.Hostname()) + ctx = withAPIPortOverride(ctx, apiURL.Port()) + ctx = WithAppIDOverride(ctx, appID) + + // use the unregistered request as a placeholder so that withContext can read the headers + c := &aeContext{req: req} + c.req = req.WithContext(withContext(ctx, c)) + return c.req } var errTimeout = &CallError{ @@ -348,7 +322,7 @@ var errTimeout = &CallError{ Timeout: true, } -func (c *context) Header() http.Header { return c.outHeader } +func (c *aeContext) Header() http.Header { return c.outHeader } // Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status // codes do not permit a response body (nor response entity headers such as @@ -365,7 +339,7 @@ func bodyAllowedForStatus(status int) bool { return true } -func (c *context) Write(b []byte) (int, error) { +func (c *aeContext) Write(b []byte) (int, error) { if c.outCode == 0 { c.WriteHeader(http.StatusOK) } @@ -376,7 +350,7 @@ func (c *context) Write(b []byte) (int, error) { return len(b), nil } -func (c *context) WriteHeader(code int) { +func (c *aeContext) WriteHeader(code int) { if c.outCode != 0 { logf(c, 3, "WriteHeader called multiple times on request.") // error level return @@ -384,10 +358,11 @@ func (c *context) WriteHeader(code int) { c.outCode = code } -func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) { +func post(ctx context.Context, body []byte, timeout time.Duration) (b []byte, err error) { + apiURL := apiURL(ctx) hreq := &http.Request{ Method: "POST", - URL: c.apiURL, + URL: apiURL, Header: http.Header{ apiEndpointHeader: apiEndpointHeaderValue, apiMethodHeader: apiMethodHeaderValue, @@ -396,13 +371,16 @@ func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) }, Body: ioutil.NopCloser(bytes.NewReader(body)), ContentLength: int64(len(body)), - Host: c.apiURL.Host, - } - if info := c.req.Header.Get(dapperHeader); info != "" { - hreq.Header.Set(dapperHeader, info) + Host: apiURL.Host, } - if info := c.req.Header.Get(traceHeader); info != "" { - hreq.Header.Set(traceHeader, info) + c := fromContext(ctx) + if c != nil { + if info := c.req.Header.Get(dapperHeader); info != "" { + hreq.Header.Set(dapperHeader, info) + } + if info := c.req.Header.Get(traceHeader); info != "" { + hreq.Header.Set(traceHeader, info) + } } tr := apiHTTPClient.Transport.(*http.Transport) @@ -444,7 +422,7 @@ func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) return hrespBody, nil } -func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { +func Call(ctx context.Context, service, method string, in, out proto.Message) error { if ns := NamespaceFromContext(ctx); ns != "" { if fn, ok := NamespaceMods[service]; ok { fn(in, ns) @@ -463,15 +441,11 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message) } c := fromContext(ctx) - if c == nil { - // Give a good error message rather than a panic lower down. - return errNotAppEngineContext - } // Apply transaction modifications if we're in a transaction. if t := transactionFromContext(ctx); t != nil { if t.finished { - return errors.New("transaction context has expired") + return errors.New("transaction aeContext has expired") } applyTransaction(in, &t.transaction) } @@ -487,20 +461,13 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message) return err } - ticket := c.req.Header.Get(ticketHeader) - // Use a test ticket under test environment. - if ticket == "" { - if appid := ctx.Value(&appIDOverrideKey); appid != nil { - ticket = appid.(string) + defaultTicketSuffix + ticket := "" + if c != nil { + ticket = c.req.Header.Get(ticketHeader) + if dri := c.req.Header.Get(devRequestIdHeader); IsDevAppServer() && dri != "" { + ticket = dri } } - // Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver. - if ticket == "" { - ticket = DefaultTicket() - } - if dri := c.req.Header.Get(devRequestIdHeader); IsDevAppServer() && dri != "" { - ticket = dri - } req := &remotepb.Request{ ServiceName: &service, Method: &method, @@ -512,7 +479,7 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message) return err } - hrespBody, err := c.post(hreqBody, timeout) + hrespBody, err := post(ctx, hreqBody, timeout) if err != nil { return err } @@ -549,11 +516,11 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message) return proto.Unmarshal(res.Response, out) } -func (c *context) Request() *http.Request { +func (c *aeContext) Request() *http.Request { return c.req } -func (c *context) addLogLine(ll *logpb.UserAppLogLine) { +func (c *aeContext) addLogLine(ll *logpb.UserAppLogLine) { // Truncate long log lines. // TODO(dsymonds): Check if this is still necessary. const lim = 8 << 10 @@ -575,18 +542,20 @@ var logLevelName = map[int64]string{ 4: "CRITICAL", } -func logf(c *context, level int64, format string, args ...interface{}) { +func logf(c *aeContext, level int64, format string, args ...interface{}) { if c == nil { - panic("not an App Engine context") + panic("not an App Engine aeContext") } s := fmt.Sprintf(format, args...) s = strings.TrimRight(s, "\n") // Remove any trailing newline characters. - c.addLogLine(&logpb.UserAppLogLine{ - TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3), - Level: &level, - Message: &s, - }) - // Only duplicate log to stderr if not running on App Engine second generation + if logToLogservice() { + c.addLogLine(&logpb.UserAppLogLine{ + TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3), + Level: &level, + Message: &s, + }) + } + // Log to stdout if not deployed if !IsSecondGen() { log.Print(logLevelName[level] + ": " + s) } @@ -594,7 +563,7 @@ func logf(c *context, level int64, format string, args ...interface{}) { // flushLog attempts to flush any pending logs to the appserver. // It should not be called concurrently. -func (c *context) flushLog(force bool) (flushed bool) { +func (c *aeContext) flushLog(force bool) (flushed bool) { c.pendingLogs.Lock() // Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious. n, rem := 0, 30<<20 @@ -655,7 +624,7 @@ const ( forceFlushInterval = 60 * time.Second ) -func (c *context) logFlusher(stop <-chan int) { +func (c *aeContext) logFlusher(stop <-chan int) { lastFlush := time.Now() tick := time.NewTicker(flushInterval) for { @@ -673,6 +642,12 @@ func (c *context) logFlusher(stop <-chan int) { } } -func ContextForTesting(req *http.Request) netcontext.Context { - return toContext(&context{req: req}) +func ContextForTesting(req *http.Request) context.Context { + return toContext(&aeContext{req: req}) +} + +func logToLogservice() bool { + // TODO: replace logservice with json structured logs to $LOG_DIR/app.log.json + // where $LOG_DIR is /var/log in prod and some tmpdir in dev + return os.Getenv("LOG_TO_LOGSERVICE") != "0" } diff --git a/vendor/google.golang.org/appengine/internal/api_classic.go b/vendor/google.golang.org/appengine/internal/api_classic.go index f0f40b2e3..87c33c798 100644 --- a/vendor/google.golang.org/appengine/internal/api_classic.go +++ b/vendor/google.golang.org/appengine/internal/api_classic.go @@ -2,11 +2,13 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build appengine // +build appengine package internal import ( + "context" "errors" "fmt" "net/http" @@ -17,20 +19,19 @@ import ( basepb "appengine_internal/base" "github.com/golang/protobuf/proto" - netcontext "golang.org/x/net/context" ) var contextKey = "holds an appengine.Context" // fromContext returns the App Engine context or nil if ctx is not // derived from an App Engine context. -func fromContext(ctx netcontext.Context) appengine.Context { +func fromContext(ctx context.Context) appengine.Context { c, _ := ctx.Value(&contextKey).(appengine.Context) return c } // This is only for classic App Engine adapters. -func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) { +func ClassicContextFromContext(ctx context.Context) (appengine.Context, error) { c := fromContext(ctx) if c == nil { return nil, errNotAppEngineContext @@ -38,8 +39,8 @@ func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error return c, nil } -func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context { - ctx := netcontext.WithValue(parent, &contextKey, c) +func withContext(parent context.Context, c appengine.Context) context.Context { + ctx := context.WithValue(parent, &contextKey, c) s := &basepb.StringProto{} c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil) @@ -50,7 +51,7 @@ func withContext(parent netcontext.Context, c appengine.Context) netcontext.Cont return ctx } -func IncomingHeaders(ctx netcontext.Context) http.Header { +func IncomingHeaders(ctx context.Context) http.Header { if c := fromContext(ctx); c != nil { if req, ok := c.Request().(*http.Request); ok { return req.Header @@ -59,11 +60,11 @@ func IncomingHeaders(ctx netcontext.Context) http.Header { return nil } -func ReqContext(req *http.Request) netcontext.Context { - return WithContext(netcontext.Background(), req) +func ReqContext(req *http.Request) context.Context { + return WithContext(context.Background(), req) } -func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { +func WithContext(parent context.Context, req *http.Request) context.Context { c := appengine.NewContext(req) return withContext(parent, c) } @@ -83,11 +84,11 @@ func (t *testingContext) Call(service, method string, _, _ appengine_internal.Pr } func (t *testingContext) Request() interface{} { return t.req } -func ContextForTesting(req *http.Request) netcontext.Context { - return withContext(netcontext.Background(), &testingContext{req: req}) +func ContextForTesting(req *http.Request) context.Context { + return withContext(context.Background(), &testingContext{req: req}) } -func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { +func Call(ctx context.Context, service, method string, in, out proto.Message) error { if ns := NamespaceFromContext(ctx); ns != "" { if fn, ok := NamespaceMods[service]; ok { fn(in, ns) @@ -144,8 +145,8 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message) return err } -func handleHTTP(w http.ResponseWriter, r *http.Request) { - panic("handleHTTP called; this should be impossible") +func Middleware(next http.Handler) http.Handler { + panic("Middleware called; this should be impossible") } func logf(c appengine.Context, level int64, format string, args ...interface{}) { diff --git a/vendor/google.golang.org/appengine/internal/api_common.go b/vendor/google.golang.org/appengine/internal/api_common.go index e0c0b214b..5b95c13d9 100644 --- a/vendor/google.golang.org/appengine/internal/api_common.go +++ b/vendor/google.golang.org/appengine/internal/api_common.go @@ -5,20 +5,26 @@ package internal import ( + "context" "errors" "os" "github.com/golang/protobuf/proto" - netcontext "golang.org/x/net/context" ) +type ctxKey string + +func (c ctxKey) String() string { + return "appengine context key: " + string(c) +} + var errNotAppEngineContext = errors.New("not an App Engine context") -type CallOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error +type CallOverrideFunc func(ctx context.Context, service, method string, in, out proto.Message) error var callOverrideKey = "holds []CallOverrideFunc" -func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Context { +func WithCallOverride(ctx context.Context, f CallOverrideFunc) context.Context { // We avoid appending to any existing call override // so we don't risk overwriting a popped stack below. var cofs []CallOverrideFunc @@ -26,10 +32,10 @@ func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Con cofs = append(cofs, uf...) } cofs = append(cofs, f) - return netcontext.WithValue(ctx, &callOverrideKey, cofs) + return context.WithValue(ctx, &callOverrideKey, cofs) } -func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netcontext.Context, bool) { +func callOverrideFromContext(ctx context.Context) (CallOverrideFunc, context.Context, bool) { cofs, _ := ctx.Value(&callOverrideKey).([]CallOverrideFunc) if len(cofs) == 0 { return nil, nil, false @@ -37,7 +43,7 @@ func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netconte // We found a list of overrides; grab the last, and reconstitute a // context that will hide it. f := cofs[len(cofs)-1] - ctx = netcontext.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1]) + ctx = context.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1]) return f, ctx, true } @@ -45,23 +51,35 @@ type logOverrideFunc func(level int64, format string, args ...interface{}) var logOverrideKey = "holds a logOverrideFunc" -func WithLogOverride(ctx netcontext.Context, f logOverrideFunc) netcontext.Context { - return netcontext.WithValue(ctx, &logOverrideKey, f) +func WithLogOverride(ctx context.Context, f logOverrideFunc) context.Context { + return context.WithValue(ctx, &logOverrideKey, f) } var appIDOverrideKey = "holds a string, being the full app ID" -func WithAppIDOverride(ctx netcontext.Context, appID string) netcontext.Context { - return netcontext.WithValue(ctx, &appIDOverrideKey, appID) +func WithAppIDOverride(ctx context.Context, appID string) context.Context { + return context.WithValue(ctx, &appIDOverrideKey, appID) +} + +var apiHostOverrideKey = ctxKey("holds a string, being the alternate API_HOST") + +func withAPIHostOverride(ctx context.Context, apiHost string) context.Context { + return context.WithValue(ctx, apiHostOverrideKey, apiHost) +} + +var apiPortOverrideKey = ctxKey("holds a string, being the alternate API_PORT") + +func withAPIPortOverride(ctx context.Context, apiPort string) context.Context { + return context.WithValue(ctx, apiPortOverrideKey, apiPort) } var namespaceKey = "holds the namespace string" -func withNamespace(ctx netcontext.Context, ns string) netcontext.Context { - return netcontext.WithValue(ctx, &namespaceKey, ns) +func withNamespace(ctx context.Context, ns string) context.Context { + return context.WithValue(ctx, &namespaceKey, ns) } -func NamespaceFromContext(ctx netcontext.Context) string { +func NamespaceFromContext(ctx context.Context) string { // If there's no namespace, return the empty string. ns, _ := ctx.Value(&namespaceKey).(string) return ns @@ -70,14 +88,14 @@ func NamespaceFromContext(ctx netcontext.Context) string { // FullyQualifiedAppID returns the fully-qualified application ID. // This may contain a partition prefix (e.g. "s~" for High Replication apps), // or a domain prefix (e.g. "example.com:"). -func FullyQualifiedAppID(ctx netcontext.Context) string { +func FullyQualifiedAppID(ctx context.Context) string { if id, ok := ctx.Value(&appIDOverrideKey).(string); ok { return id } return fullyQualifiedAppID(ctx) } -func Logf(ctx netcontext.Context, level int64, format string, args ...interface{}) { +func Logf(ctx context.Context, level int64, format string, args ...interface{}) { if f, ok := ctx.Value(&logOverrideKey).(logOverrideFunc); ok { f(level, format, args...) return @@ -90,7 +108,7 @@ func Logf(ctx netcontext.Context, level int64, format string, args ...interface{ } // NamespacedContext wraps a Context to support namespaces. -func NamespacedContext(ctx netcontext.Context, namespace string) netcontext.Context { +func NamespacedContext(ctx context.Context, namespace string) context.Context { return withNamespace(ctx, namespace) } diff --git a/vendor/google.golang.org/appengine/internal/identity.go b/vendor/google.golang.org/appengine/internal/identity.go index 9b4134e42..0f95aa91d 100644 --- a/vendor/google.golang.org/appengine/internal/identity.go +++ b/vendor/google.golang.org/appengine/internal/identity.go @@ -5,9 +5,8 @@ package internal import ( + "context" "os" - - netcontext "golang.org/x/net/context" ) var ( @@ -23,7 +22,7 @@ var ( // AppID is the implementation of the wrapper function of the same name in // ../identity.go. See that file for commentary. -func AppID(c netcontext.Context) string { +func AppID(c context.Context) string { return appID(FullyQualifiedAppID(c)) } @@ -35,7 +34,7 @@ func IsStandard() bool { return appengineStandard || IsSecondGen() } -// IsStandard is the implementation of the wrapper function of the same name in +// IsSecondGen is the implementation of the wrapper function of the same name in // ../appengine.go. See that file for commentary. func IsSecondGen() bool { // Second-gen runtimes set $GAE_ENV so we use that to check if we're on a second-gen runtime. diff --git a/vendor/google.golang.org/appengine/internal/identity_classic.go b/vendor/google.golang.org/appengine/internal/identity_classic.go index 4e979f45e..5ad3548bf 100644 --- a/vendor/google.golang.org/appengine/internal/identity_classic.go +++ b/vendor/google.golang.org/appengine/internal/identity_classic.go @@ -2,21 +2,22 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build appengine // +build appengine package internal import ( - "appengine" + "context" - netcontext "golang.org/x/net/context" + "appengine" ) func init() { appengineStandard = true } -func DefaultVersionHostname(ctx netcontext.Context) string { +func DefaultVersionHostname(ctx context.Context) string { c := fromContext(ctx) if c == nil { panic(errNotAppEngineContext) @@ -24,12 +25,12 @@ func DefaultVersionHostname(ctx netcontext.Context) string { return appengine.DefaultVersionHostname(c) } -func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() } -func ServerSoftware() string { return appengine.ServerSoftware() } -func InstanceID() string { return appengine.InstanceID() } -func IsDevAppServer() bool { return appengine.IsDevAppServer() } +func Datacenter(_ context.Context) string { return appengine.Datacenter() } +func ServerSoftware() string { return appengine.ServerSoftware() } +func InstanceID() string { return appengine.InstanceID() } +func IsDevAppServer() bool { return appengine.IsDevAppServer() } -func RequestID(ctx netcontext.Context) string { +func RequestID(ctx context.Context) string { c := fromContext(ctx) if c == nil { panic(errNotAppEngineContext) @@ -37,14 +38,14 @@ func RequestID(ctx netcontext.Context) string { return appengine.RequestID(c) } -func ModuleName(ctx netcontext.Context) string { +func ModuleName(ctx context.Context) string { c := fromContext(ctx) if c == nil { panic(errNotAppEngineContext) } return appengine.ModuleName(c) } -func VersionID(ctx netcontext.Context) string { +func VersionID(ctx context.Context) string { c := fromContext(ctx) if c == nil { panic(errNotAppEngineContext) @@ -52,7 +53,7 @@ func VersionID(ctx netcontext.Context) string { return appengine.VersionID(c) } -func fullyQualifiedAppID(ctx netcontext.Context) string { +func fullyQualifiedAppID(ctx context.Context) string { c := fromContext(ctx) if c == nil { panic(errNotAppEngineContext) diff --git a/vendor/google.golang.org/appengine/internal/identity_flex.go b/vendor/google.golang.org/appengine/internal/identity_flex.go index d5e2e7b5e..4201b6b58 100644 --- a/vendor/google.golang.org/appengine/internal/identity_flex.go +++ b/vendor/google.golang.org/appengine/internal/identity_flex.go @@ -2,6 +2,7 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build appenginevm // +build appenginevm package internal diff --git a/vendor/google.golang.org/appengine/internal/identity_vm.go b/vendor/google.golang.org/appengine/internal/identity_vm.go index 5d8067263..18ddda3a4 100644 --- a/vendor/google.golang.org/appengine/internal/identity_vm.go +++ b/vendor/google.golang.org/appengine/internal/identity_vm.go @@ -2,17 +2,17 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build !appengine // +build !appengine package internal import ( + "context" "log" "net/http" "os" "strings" - - netcontext "golang.org/x/net/context" ) // These functions are implementations of the wrapper functions @@ -24,7 +24,7 @@ const ( hDatacenter = "X-AppEngine-Datacenter" ) -func ctxHeaders(ctx netcontext.Context) http.Header { +func ctxHeaders(ctx context.Context) http.Header { c := fromContext(ctx) if c == nil { return nil @@ -32,15 +32,15 @@ func ctxHeaders(ctx netcontext.Context) http.Header { return c.Request().Header } -func DefaultVersionHostname(ctx netcontext.Context) string { +func DefaultVersionHostname(ctx context.Context) string { return ctxHeaders(ctx).Get(hDefaultVersionHostname) } -func RequestID(ctx netcontext.Context) string { +func RequestID(ctx context.Context) string { return ctxHeaders(ctx).Get(hRequestLogId) } -func Datacenter(ctx netcontext.Context) string { +func Datacenter(ctx context.Context) string { if dc := ctxHeaders(ctx).Get(hDatacenter); dc != "" { return dc } @@ -71,7 +71,7 @@ func ServerSoftware() string { // TODO(dsymonds): Remove the metadata fetches. -func ModuleName(_ netcontext.Context) string { +func ModuleName(_ context.Context) string { if s := os.Getenv("GAE_MODULE_NAME"); s != "" { return s } @@ -81,7 +81,7 @@ func ModuleName(_ netcontext.Context) string { return string(mustGetMetadata("instance/attributes/gae_backend_name")) } -func VersionID(_ netcontext.Context) string { +func VersionID(_ context.Context) string { if s1, s2 := os.Getenv("GAE_MODULE_VERSION"), os.Getenv("GAE_MINOR_VERSION"); s1 != "" && s2 != "" { return s1 + "." + s2 } @@ -112,7 +112,7 @@ func partitionlessAppID() string { return string(mustGetMetadata("instance/attributes/gae_project")) } -func fullyQualifiedAppID(_ netcontext.Context) string { +func fullyQualifiedAppID(_ context.Context) string { if s := os.Getenv("GAE_APPLICATION"); s != "" { return s } @@ -130,5 +130,5 @@ func fullyQualifiedAppID(_ netcontext.Context) string { } func IsDevAppServer() bool { - return os.Getenv("RUN_WITH_DEVAPPSERVER") != "" + return os.Getenv("RUN_WITH_DEVAPPSERVER") != "" || os.Getenv("GAE_ENV") == "localdev" } diff --git a/vendor/google.golang.org/appengine/internal/main.go b/vendor/google.golang.org/appengine/internal/main.go index 1e765312f..afd0ae84f 100644 --- a/vendor/google.golang.org/appengine/internal/main.go +++ b/vendor/google.golang.org/appengine/internal/main.go @@ -2,6 +2,7 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build appengine // +build appengine package internal diff --git a/vendor/google.golang.org/appengine/internal/main_vm.go b/vendor/google.golang.org/appengine/internal/main_vm.go index ddb79a333..86a8caf06 100644 --- a/vendor/google.golang.org/appengine/internal/main_vm.go +++ b/vendor/google.golang.org/appengine/internal/main_vm.go @@ -2,6 +2,7 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. +//go:build !appengine // +build !appengine package internal @@ -29,7 +30,7 @@ func Main() { if IsDevAppServer() { host = "127.0.0.1" } - if err := http.ListenAndServe(host+":"+port, http.HandlerFunc(handleHTTP)); err != nil { + if err := http.ListenAndServe(host+":"+port, Middleware(http.DefaultServeMux)); err != nil { log.Fatalf("http.ListenAndServe: %v", err) } } diff --git a/vendor/google.golang.org/appengine/internal/transaction.go b/vendor/google.golang.org/appengine/internal/transaction.go index 9006ae653..2ae8ab9fa 100644 --- a/vendor/google.golang.org/appengine/internal/transaction.go +++ b/vendor/google.golang.org/appengine/internal/transaction.go @@ -7,11 +7,11 @@ package internal // This file implements hooks for applying datastore transactions. import ( + "context" "errors" "reflect" "github.com/golang/protobuf/proto" - netcontext "golang.org/x/net/context" basepb "google.golang.org/appengine/internal/base" pb "google.golang.org/appengine/internal/datastore" @@ -38,13 +38,13 @@ func applyTransaction(pb proto.Message, t *pb.Transaction) { var transactionKey = "used for *Transaction" -func transactionFromContext(ctx netcontext.Context) *transaction { +func transactionFromContext(ctx context.Context) *transaction { t, _ := ctx.Value(&transactionKey).(*transaction) return t } -func withTransaction(ctx netcontext.Context, t *transaction) netcontext.Context { - return netcontext.WithValue(ctx, &transactionKey, t) +func withTransaction(ctx context.Context, t *transaction) context.Context { + return context.WithValue(ctx, &transactionKey, t) } type transaction struct { @@ -54,7 +54,7 @@ type transaction struct { var ErrConcurrentTransaction = errors.New("internal: concurrent transaction") -func RunTransactionOnce(c netcontext.Context, f func(netcontext.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) { +func RunTransactionOnce(c context.Context, f func(context.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) { if transactionFromContext(c) != nil { return nil, errors.New("nested transactions are not supported") } diff --git a/vendor/google.golang.org/appengine/urlfetch/urlfetch.go b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go index 6ffe1e6d9..6c0d72418 100644 --- a/vendor/google.golang.org/appengine/urlfetch/urlfetch.go +++ b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go @@ -7,6 +7,7 @@ package urlfetch // import "google.golang.org/appengine/urlfetch" import ( + "context" "errors" "fmt" "io" @@ -18,7 +19,6 @@ import ( "time" "github.com/golang/protobuf/proto" - "golang.org/x/net/context" "google.golang.org/appengine/internal" pb "google.golang.org/appengine/internal/urlfetch" @@ -44,11 +44,10 @@ type Transport struct { var _ http.RoundTripper = (*Transport)(nil) // Client returns an *http.Client using a default urlfetch Transport. This -// client will have the default deadline of 5 seconds, and will check the -// validity of SSL certificates. +// client will check the validity of SSL certificates. // -// Any deadline of the provided context will be used for requests through this client; -// if the client does not have a deadline then a 5 second default is used. +// Any deadline of the provided context will be used for requests through this client. +// If the client does not have a deadline, then an App Engine default of 60 second is used. func Client(ctx context.Context) *http.Client { return &http.Client{ Transport: &Transport{ diff --git a/vendor/google.golang.org/genproto/googleapis/api/LICENSE b/vendor/google.golang.org/genproto/googleapis/api/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go index bb9d94893..10f35d10e 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.9 +// protoc v4.24.4 // source: google/api/client.proto package annotations @@ -53,6 +53,12 @@ const ( ClientLibraryOrganization_PHOTOS ClientLibraryOrganization = 3 // Street View Org. ClientLibraryOrganization_STREET_VIEW ClientLibraryOrganization = 4 + // Shopping Org. + ClientLibraryOrganization_SHOPPING ClientLibraryOrganization = 5 + // Geo Org. + ClientLibraryOrganization_GEO ClientLibraryOrganization = 6 + // Generative AI - https://developers.generativeai.google + ClientLibraryOrganization_GENERATIVE_AI ClientLibraryOrganization = 7 ) // Enum value maps for ClientLibraryOrganization. @@ -63,13 +69,19 @@ var ( 2: "ADS", 3: "PHOTOS", 4: "STREET_VIEW", + 5: "SHOPPING", + 6: "GEO", + 7: "GENERATIVE_AI", } ClientLibraryOrganization_value = map[string]int32{ "CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED": 0, - "CLOUD": 1, - "ADS": 2, - "PHOTOS": 3, - "STREET_VIEW": 4, + "CLOUD": 1, + "ADS": 2, + "PHOTOS": 3, + "STREET_VIEW": 4, + "SHOPPING": 5, + "GEO": 6, + "GENERATIVE_AI": 7, } ) @@ -370,7 +382,7 @@ type Publishing struct { // A list of API method settings, e.g. the behavior for methods that use the // long-running operation pattern. MethodSettings []*MethodSettings `protobuf:"bytes,2,rep,name=method_settings,json=methodSettings,proto3" json:"method_settings,omitempty"` - // Link to a place that API users can report issues. Example: + // Link to a *public* URI where users can report issues. Example: // https://issuetracker.google.com/issues/new?component=190865&template=1161103 NewIssueUri string `protobuf:"bytes,101,opt,name=new_issue_uri,json=newIssueUri,proto3" json:"new_issue_uri,omitempty"` // Link to product home page. Example: @@ -397,6 +409,9 @@ type Publishing struct { // Optional link to proto reference documentation. Example: // https://cloud.google.com/pubsub/lite/docs/reference/rpc ProtoReferenceDocumentationUri string `protobuf:"bytes,110,opt,name=proto_reference_documentation_uri,json=protoReferenceDocumentationUri,proto3" json:"proto_reference_documentation_uri,omitempty"` + // Optional link to REST reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rest + RestReferenceDocumentationUri string `protobuf:"bytes,111,opt,name=rest_reference_documentation_uri,json=restReferenceDocumentationUri,proto3" json:"rest_reference_documentation_uri,omitempty"` } func (x *Publishing) Reset() { @@ -501,6 +516,13 @@ func (x *Publishing) GetProtoReferenceDocumentationUri() string { return "" } +func (x *Publishing) GetRestReferenceDocumentationUri() string { + if x != nil { + return x.RestReferenceDocumentationUri + } + return "" +} + // Settings for Java client libraries. type JavaSettings struct { state protoimpl.MessageState @@ -1021,6 +1043,18 @@ type MethodSettings struct { // total_poll_timeout: // seconds: 54000 # 90 minutes LongRunning *MethodSettings_LongRunning `protobuf:"bytes,2,opt,name=long_running,json=longRunning,proto3" json:"long_running,omitempty"` + // List of top-level fields of the request message, that should be + // automatically populated by the client libraries based on their + // (google.api.field_info).format. Currently supported format: UUID4. + // + // Example of a YAML configuration: + // + // publishing: + // method_settings: + // - selector: google.example.v1.ExampleService.CreateExample + // auto_populated_fields: + // - request_id + AutoPopulatedFields []string `protobuf:"bytes,3,rep,name=auto_populated_fields,json=autoPopulatedFields,proto3" json:"auto_populated_fields,omitempty"` } func (x *MethodSettings) Reset() { @@ -1069,6 +1103,13 @@ func (x *MethodSettings) GetLongRunning() *MethodSettings_LongRunning { return nil } +func (x *MethodSettings) GetAutoPopulatedFields() []string { + if x != nil { + return x.AutoPopulatedFields + } + return nil +} + // Describes settings to use when generating API methods that use the // long-running operation pattern. // All default values below are from those used in the client library @@ -1179,6 +1220,14 @@ var file_google_api_client_proto_extTypes = []protoimpl.ExtensionInfo{ Tag: "bytes,1050,opt,name=oauth_scopes", Filename: "google/api/client.proto", }, + { + ExtendedType: (*descriptorpb.ServiceOptions)(nil), + ExtensionType: (*string)(nil), + Field: 525000001, + Name: "google.api.api_version", + Tag: "bytes,525000001,opt,name=api_version", + Filename: "google/api/client.proto", + }, } // Extension fields to descriptorpb.MethodOptions. @@ -1260,6 +1309,23 @@ var ( // // optional string oauth_scopes = 1050; E_OauthScopes = &file_google_api_client_proto_extTypes[2] + // The API version of this service, which should be sent by version-aware + // clients to the service. This allows services to abide by the schema and + // behavior of the service at the time this API version was deployed. + // The format of the API version must be treated as opaque by clients. + // Services may use a format with an apparent structure, but clients must + // not rely on this to determine components within an API version, or attempt + // to construct other valid API versions. Note that this is for upcoming + // functionality and may not be implemented for all services. + // + // Example: + // + // service Foo { + // option (google.api.api_version) = "v1_20230821_preview"; + // } + // + // optional string api_version = 525000001; + E_ApiVersion = &file_google_api_client_proto_extTypes[3] ) var File_google_api_client_proto protoreflect.FileDescriptor @@ -1324,7 +1390,7 @@ var file_google_api_client_proto_rawDesc = []byte{ 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x6f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0a, 0x67, 0x6f, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x22, 0xab, 0x04, 0x0a, 0x0a, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x73, 0x22, 0xf4, 0x04, 0x0a, 0x0a, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, @@ -1359,148 +1425,163 @@ var file_google_api_client_proto_rawDesc = []byte{ 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, - 0x69, 0x22, 0x9a, 0x02, 0x0a, 0x0c, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x70, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x5f, 0x0a, 0x13, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x06, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x1a, 0x44, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x12, 0x47, 0x0a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x72, 0x65, 0x73, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x69, 0x22, 0x9a, 0x02, 0x0a, 0x0c, 0x4a, + 0x61, 0x76, 0x61, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x12, 0x5f, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4a, + 0x61, 0x76, 0x61, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x49, - 0x0a, 0x0b, 0x43, 0x70, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, - 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x49, 0x0a, 0x0b, 0x50, 0x68, 0x70, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x4c, 0x0a, 0x0e, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x53, 0x65, + 0x72, 0x79, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x1a, 0x44, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x70, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0xae, - 0x04, 0x0a, 0x0e, 0x44, 0x6f, 0x74, 0x6e, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x6f, 0x6e, 0x22, 0x49, 0x0a, 0x0b, 0x50, 0x68, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x5a, 0x0a, - 0x10, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x74, 0x6e, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x11, 0x72, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x44, 0x6f, 0x74, 0x6e, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x67, 0x6e, 0x6f, - 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, - 0x35, 0x0a, 0x16, 0x68, 0x61, 0x6e, 0x64, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x5f, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x15, 0x68, 0x61, 0x6e, 0x64, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x52, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x4a, 0x0a, 0x0c, 0x52, 0x75, 0x62, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x4c, 0x0a, + 0x0e, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x48, 0x0a, 0x0a, 0x47, - 0x6f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x8e, 0x03, 0x0a, 0x0e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x49, 0x0a, 0x0c, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, - 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, - 0x6e, 0x67, 0x52, 0x0b, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x1a, - 0x94, 0x02, 0x0a, 0x0b, 0x4c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, - 0x47, 0x0a, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, - 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x6f, 0x6c, 0x6c, - 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x6c, - 0x61, 0x79, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0e, - 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0c, 0x6d, 0x61, 0x78, 0x50, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x47, 0x0a, - 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x6f, 0x6c, 0x6c, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2a, 0x79, 0x0a, 0x19, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x27, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x49, - 0x42, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, - 0x44, 0x53, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x48, 0x4f, 0x54, 0x4f, 0x53, 0x10, 0x03, - 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x52, 0x45, 0x45, 0x54, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, - 0x04, 0x2a, 0x67, 0x0a, 0x18, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x62, 0x72, 0x61, - 0x72, 0x79, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, - 0x26, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x52, 0x59, 0x5f, - 0x44, 0x45, 0x53, 0x54, 0x49, 0x4e, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x49, 0x54, - 0x48, 0x55, 0x42, 0x10, 0x0a, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x41, 0x43, 0x4b, 0x41, 0x47, 0x45, - 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x10, 0x14, 0x3a, 0x4a, 0x0a, 0x10, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, + 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x0c, 0x4e, + 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0xae, 0x04, 0x0a, 0x0e, 0x44, 0x6f, 0x74, 0x6e, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x5a, 0x0a, 0x10, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, + 0x74, 0x6e, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x52, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x11, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x74, 0x6e, 0x65, + 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x10, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x38, + 0x0a, 0x18, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x16, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x16, 0x68, 0x61, 0x6e, 0x64, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x68, 0x61, 0x6e, 0x64, 0x77, 0x72, + 0x69, 0x74, 0x74, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, + 0x42, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4a, 0x0a, 0x0c, 0x52, 0x75, 0x62, 0x79, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x48, 0x0a, 0x0a, 0x47, 0x6f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0xc2, + 0x03, 0x0a, 0x0e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x49, 0x0a, + 0x0c, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, + 0x4c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x6c, 0x6f, 0x6e, + 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x75, 0x74, 0x6f, + 0x5f, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x50, 0x6f, 0x70, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x94, 0x02, 0x0a, + 0x0b, 0x4c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x47, 0x0a, 0x12, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x6f, 0x6c, 0x6c, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0e, 0x6d, 0x61, 0x78, + 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6d, 0x61, + 0x78, 0x50, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x47, 0x0a, 0x12, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x2a, 0xa3, 0x01, 0x0a, 0x19, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x79, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2b, 0x0a, 0x27, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x49, 0x42, 0x52, + 0x41, 0x52, 0x59, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, + 0x0a, 0x05, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x44, 0x53, + 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x48, 0x4f, 0x54, 0x4f, 0x53, 0x10, 0x03, 0x12, 0x0f, + 0x0a, 0x0b, 0x53, 0x54, 0x52, 0x45, 0x45, 0x54, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x04, 0x12, + 0x0c, 0x0a, 0x08, 0x53, 0x48, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x07, 0x0a, + 0x03, 0x47, 0x45, 0x4f, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, + 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x49, 0x10, 0x07, 0x2a, 0x67, 0x0a, 0x18, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x26, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, + 0x4c, 0x49, 0x42, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x49, 0x4e, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x49, 0x54, 0x48, 0x55, 0x42, 0x10, 0x0a, 0x12, 0x13, 0x0a, + 0x0f, 0x50, 0x41, 0x43, 0x4b, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, + 0x10, 0x14, 0x3a, 0x4a, 0x0a, 0x10, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9b, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a, 0x43, + 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9b, - 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a, 0x43, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x99, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x43, 0x0a, 0x0c, 0x6f, - 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9a, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, - 0x42, 0x69, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, - 0x70, 0x69, 0x42, 0x0b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, - 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x99, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, + 0x6f, 0x73, 0x74, 0x3a, 0x43, 0x0a, 0x0c, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9a, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x61, 0x75, + 0x74, 0x68, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x3a, 0x44, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc1, 0xba, 0xab, 0xfa, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x69, + 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x42, 0x0b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1573,10 +1654,11 @@ var file_google_api_client_proto_depIdxs = []int32{ 20, // 28: google.api.method_signature:extendee -> google.protobuf.MethodOptions 21, // 29: google.api.default_host:extendee -> google.protobuf.ServiceOptions 21, // 30: google.api.oauth_scopes:extendee -> google.protobuf.ServiceOptions - 31, // [31:31] is the sub-list for method output_type - 31, // [31:31] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 28, // [28:31] is the sub-list for extension extendee + 21, // 31: google.api.api_version:extendee -> google.protobuf.ServiceOptions + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 28, // [28:32] is the sub-list for extension extendee 0, // [0:28] is the sub-list for field type_name } @@ -1750,7 +1832,7 @@ func file_google_api_client_proto_init() { RawDescriptor: file_google_api_client_proto_rawDesc, NumEnums: 2, NumMessages: 16, - NumExtensions: 3, + NumExtensions: 4, NumServices: 0, }, GoTypes: file_google_api_client_proto_goTypes, diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go index dbe2e2d0c..312d7eb49 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.9 +// protoc v4.24.4 // source: google/api/field_behavior.proto package annotations @@ -78,6 +78,19 @@ const ( // a non-empty value will be returned. The user will not be aware of what // non-empty value to expect. FieldBehavior_NON_EMPTY_DEFAULT FieldBehavior = 7 + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + FieldBehavior_IDENTIFIER FieldBehavior = 8 ) // Enum value maps for FieldBehavior. @@ -91,6 +104,7 @@ var ( 5: "IMMUTABLE", 6: "UNORDERED_LIST", 7: "NON_EMPTY_DEFAULT", + 8: "IDENTIFIER", } FieldBehavior_value = map[string]int32{ "FIELD_BEHAVIOR_UNSPECIFIED": 0, @@ -101,6 +115,7 @@ var ( "IMMUTABLE": 5, "UNORDERED_LIST": 6, "NON_EMPTY_DEFAULT": 7, + "IDENTIFIER": 8, } ) @@ -169,7 +184,7 @@ var file_google_api_field_behavior_proto_rawDesc = []byte{ 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, - 0xa6, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0xb6, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x1a, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x42, 0x45, 0x48, 0x41, 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, @@ -179,21 +194,22 @@ var file_google_api_field_behavior_proto_rawDesc = []byte{ 0x0a, 0x09, 0x49, 0x4d, 0x4d, 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x4e, 0x4f, 0x4e, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x5f, 0x44, - 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x07, 0x3a, 0x60, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, + 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x07, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x44, 0x45, 0x4e, + 0x54, 0x49, 0x46, 0x49, 0x45, 0x52, 0x10, 0x08, 0x3a, 0x64, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9c, 0x08, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x0d, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x42, 0x70, 0x0a, 0x0e, 0x63, 0x6f, - 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x12, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, - 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x42, 0x02, 0x10, 0x00, 0x52, + 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x42, 0x70, + 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x42, 0x12, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, + 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go new file mode 100644 index 000000000..6ff36206d --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go @@ -0,0 +1,295 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v4.24.4 +// source: google/api/field_info.proto + +package annotations + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The standard format of a field value. The supported formats are all backed +// by either an RFC defined by the IETF or a Google-defined AIP. +type FieldInfo_Format int32 + +const ( + // Default, unspecified value. + FieldInfo_FORMAT_UNSPECIFIED FieldInfo_Format = 0 + // Universally Unique Identifier, version 4, value as defined by + // https://datatracker.ietf.org/doc/html/rfc4122. The value may be + // normalized to entirely lowercase letters. For example, the value + // `F47AC10B-58CC-0372-8567-0E02B2C3D479` would be normalized to + // `f47ac10b-58cc-0372-8567-0e02b2c3d479`. + FieldInfo_UUID4 FieldInfo_Format = 1 + // Internet Protocol v4 value as defined by [RFC + // 791](https://datatracker.ietf.org/doc/html/rfc791). The value may be + // condensed, with leading zeros in each octet stripped. For example, + // `001.022.233.040` would be condensed to `1.22.233.40`. + FieldInfo_IPV4 FieldInfo_Format = 2 + // Internet Protocol v6 value as defined by [RFC + // 2460](https://datatracker.ietf.org/doc/html/rfc2460). The value may be + // normalized to entirely lowercase letters with zeros compressed, following + // [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952). For example, + // the value `2001:0DB8:0::0` would be normalized to `2001:db8::`. + FieldInfo_IPV6 FieldInfo_Format = 3 + // An IP address in either v4 or v6 format as described by the individual + // values defined herein. See the comments on the IPV4 and IPV6 types for + // allowed normalizations of each. + FieldInfo_IPV4_OR_IPV6 FieldInfo_Format = 4 +) + +// Enum value maps for FieldInfo_Format. +var ( + FieldInfo_Format_name = map[int32]string{ + 0: "FORMAT_UNSPECIFIED", + 1: "UUID4", + 2: "IPV4", + 3: "IPV6", + 4: "IPV4_OR_IPV6", + } + FieldInfo_Format_value = map[string]int32{ + "FORMAT_UNSPECIFIED": 0, + "UUID4": 1, + "IPV4": 2, + "IPV6": 3, + "IPV4_OR_IPV6": 4, + } +) + +func (x FieldInfo_Format) Enum() *FieldInfo_Format { + p := new(FieldInfo_Format) + *p = x + return p +} + +func (x FieldInfo_Format) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FieldInfo_Format) Descriptor() protoreflect.EnumDescriptor { + return file_google_api_field_info_proto_enumTypes[0].Descriptor() +} + +func (FieldInfo_Format) Type() protoreflect.EnumType { + return &file_google_api_field_info_proto_enumTypes[0] +} + +func (x FieldInfo_Format) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FieldInfo_Format.Descriptor instead. +func (FieldInfo_Format) EnumDescriptor() ([]byte, []int) { + return file_google_api_field_info_proto_rawDescGZIP(), []int{0, 0} +} + +// Rich semantic information of an API field beyond basic typing. +type FieldInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The standard format of a field value. This does not explicitly configure + // any API consumer, just documents the API's format for the field it is + // applied to. + Format FieldInfo_Format `protobuf:"varint,1,opt,name=format,proto3,enum=google.api.FieldInfo_Format" json:"format,omitempty"` +} + +func (x *FieldInfo) Reset() { + *x = FieldInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_field_info_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldInfo) ProtoMessage() {} + +func (x *FieldInfo) ProtoReflect() protoreflect.Message { + mi := &file_google_api_field_info_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldInfo.ProtoReflect.Descriptor instead. +func (*FieldInfo) Descriptor() ([]byte, []int) { + return file_google_api_field_info_proto_rawDescGZIP(), []int{0} +} + +func (x *FieldInfo) GetFormat() FieldInfo_Format { + if x != nil { + return x.Format + } + return FieldInfo_FORMAT_UNSPECIFIED +} + +var file_google_api_field_info_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.FieldOptions)(nil), + ExtensionType: (*FieldInfo)(nil), + Field: 291403980, + Name: "google.api.field_info", + Tag: "bytes,291403980,opt,name=field_info", + Filename: "google/api/field_info.proto", + }, +} + +// Extension fields to descriptorpb.FieldOptions. +var ( + // Rich semantic descriptor of an API field beyond the basic typing. + // + // Examples: + // + // string request_id = 1 [(google.api.field_info).format = UUID4]; + // string old_ip_address = 2 [(google.api.field_info).format = IPV4]; + // string new_ip_address = 3 [(google.api.field_info).format = IPV6]; + // string actual_ip_address = 4 [ + // (google.api.field_info).format = IPV4_OR_IPV6 + // ]; + // + // optional google.api.FieldInfo field_info = 291403980; + E_FieldInfo = &file_google_api_field_info_proto_extTypes[0] +) + +var File_google_api_field_info_proto protoreflect.FileDescriptor + +var file_google_api_field_info_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x01, 0x0a, 0x09, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x34, 0x0a, 0x06, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, + 0x51, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, 0x52, + 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x55, 0x49, 0x44, 0x34, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x49, 0x50, 0x56, 0x34, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x36, 0x10, 0x03, + 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x4f, 0x52, 0x5f, 0x49, 0x50, 0x56, 0x36, + 0x10, 0x04, 0x3a, 0x57, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xcc, 0xf1, 0xf9, 0x8a, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x6c, 0x0a, 0x0e, 0x63, + 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0e, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_google_api_field_info_proto_rawDescOnce sync.Once + file_google_api_field_info_proto_rawDescData = file_google_api_field_info_proto_rawDesc +) + +func file_google_api_field_info_proto_rawDescGZIP() []byte { + file_google_api_field_info_proto_rawDescOnce.Do(func() { + file_google_api_field_info_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_field_info_proto_rawDescData) + }) + return file_google_api_field_info_proto_rawDescData +} + +var file_google_api_field_info_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_google_api_field_info_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_google_api_field_info_proto_goTypes = []interface{}{ + (FieldInfo_Format)(0), // 0: google.api.FieldInfo.Format + (*FieldInfo)(nil), // 1: google.api.FieldInfo + (*descriptorpb.FieldOptions)(nil), // 2: google.protobuf.FieldOptions +} +var file_google_api_field_info_proto_depIdxs = []int32{ + 0, // 0: google.api.FieldInfo.format:type_name -> google.api.FieldInfo.Format + 2, // 1: google.api.field_info:extendee -> google.protobuf.FieldOptions + 1, // 2: google.api.field_info:type_name -> google.api.FieldInfo + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 2, // [2:3] is the sub-list for extension type_name + 1, // [1:2] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_google_api_field_info_proto_init() } +func file_google_api_field_info_proto_init() { + if File_google_api_field_info_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_api_field_info_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FieldInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_field_info_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 1, + NumServices: 0, + }, + GoTypes: file_google_api_field_info_proto_goTypes, + DependencyIndexes: file_google_api_field_info_proto_depIdxs, + EnumInfos: file_google_api_field_info_proto_enumTypes, + MessageInfos: file_google_api_field_info_proto_msgTypes, + ExtensionInfos: file_google_api_field_info_proto_extTypes, + }.Build() + File_google_api_field_info_proto = out.File + file_google_api_field_info_proto_rawDesc = nil + file_google_api_field_info_proto_goTypes = nil + file_google_api_field_info_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/LICENSE b/vendor/google.golang.org/genproto/googleapis/rpc/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/rpc/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/google.golang.org/grpc/README.md b/vendor/google.golang.org/grpc/README.md index 0e6ae69a5..ab0fbb79b 100644 --- a/vendor/google.golang.org/grpc/README.md +++ b/vendor/google.golang.org/grpc/README.md @@ -1,8 +1,8 @@ # gRPC-Go -[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go) [![GoDoc](https://pkg.go.dev/badge/google.golang.org/grpc)][API] [![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go) +[![codecov](https://codecov.io/gh/grpc/grpc-go/graph/badge.svg)](https://codecov.io/gh/grpc/grpc-go) The [Go][] implementation of [gRPC][]: A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. For more information see the @@ -14,21 +14,14 @@ RPC framework that puts mobile and HTTP/2 first. For more information see the ## Installation -With [Go module][] support (Go 1.11+), simply add the following import +Simply add the following import to your code, and then `go [build|run|test]` +will automatically fetch the necessary dependencies: + ```go import "google.golang.org/grpc" ``` -to your code, and then `go [build|run|test]` will automatically fetch the -necessary dependencies. - -Otherwise, to install the `grpc-go` package, run the following command: - -```console -$ go get -u google.golang.org/grpc -``` - > **Note:** If you are trying to access `grpc-go` from **China**, see the > [FAQ](#FAQ) below. @@ -56,15 +49,6 @@ To build Go code, there are several options: - Set up a VPN and access google.golang.org through that. -- Without Go module support: `git clone` the repo manually: - - ```sh - git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc - ``` - - You will need to do the same for all of grpc's dependencies in `golang.org`, - e.g. `golang.org/x/net`. - - With Go module support: it is possible to use the `replace` feature of `go mod` to create aliases for golang.org packages. In your project's directory: @@ -76,33 +60,13 @@ To build Go code, there are several options: ``` Again, this will need to be done for all transitive dependencies hosted on - golang.org as well. For details, refer to [golang/go issue #28652](https://github.com/golang/go/issues/28652). + golang.org as well. For details, refer to [golang/go issue + #28652](https://github.com/golang/go/issues/28652). ### Compiling error, undefined: grpc.SupportPackageIsVersion -#### If you are using Go modules: - -Ensure your gRPC-Go version is `require`d at the appropriate version in -the same module containing the generated `.pb.go` files. For example, -`SupportPackageIsVersion6` needs `v1.27.0`, so in your `go.mod` file: - -```go -module - -require ( - google.golang.org/grpc v1.27.0 -) -``` - -#### If you are *not* using Go modules: - -Update the `proto` package, gRPC package, and rebuild the `.proto` files: - -```sh -go get -u github.com/golang/protobuf/{proto,protoc-gen-go} -go get -u google.golang.org/grpc -protoc --go_out=plugins=grpc:. *.proto -``` +Please update to the latest version of gRPC-Go using +`go get google.golang.org/grpc`. ### How to turn on logging @@ -121,9 +85,11 @@ possible reasons, including: 1. mis-configured transport credentials, connection failed on handshaking 1. bytes disrupted, possibly by a proxy in between 1. server shutdown - 1. Keepalive parameters caused connection shutdown, for example if you have configured - your server to terminate connections regularly to [trigger DNS lookups](https://github.com/grpc/grpc-go/issues/3170#issuecomment-552517779). - If this is the case, you may want to increase your [MaxConnectionAgeGrace](https://pkg.go.dev/google.golang.org/grpc/keepalive?tab=doc#ServerParameters), + 1. Keepalive parameters caused connection shutdown, for example if you have + configured your server to terminate connections regularly to [trigger DNS + lookups](https://github.com/grpc/grpc-go/issues/3170#issuecomment-552517779). + If this is the case, you may want to increase your + [MaxConnectionAgeGrace](https://pkg.go.dev/google.golang.org/grpc/keepalive?tab=doc#ServerParameters), to allow longer RPC calls to finish. It can be tricky to debug this because the error happens on the client side but diff --git a/vendor/google.golang.org/grpc/attributes/attributes.go b/vendor/google.golang.org/grpc/attributes/attributes.go index 3efca4591..52d530d7a 100644 --- a/vendor/google.golang.org/grpc/attributes/attributes.go +++ b/vendor/google.golang.org/grpc/attributes/attributes.go @@ -34,26 +34,26 @@ import ( // key/value pairs. Keys must be hashable, and users should define their own // types for keys. Values should not be modified after they are added to an // Attributes or if they were received from one. If values implement 'Equal(o -// interface{}) bool', it will be called by (*Attributes).Equal to determine -// whether two values with the same key should be considered equal. +// any) bool', it will be called by (*Attributes).Equal to determine whether +// two values with the same key should be considered equal. type Attributes struct { - m map[interface{}]interface{} + m map[any]any } // New returns a new Attributes containing the key/value pair. -func New(key, value interface{}) *Attributes { - return &Attributes{m: map[interface{}]interface{}{key: value}} +func New(key, value any) *Attributes { + return &Attributes{m: map[any]any{key: value}} } // WithValue returns a new Attributes containing the previous keys and values // and the new key/value pair. If the same key appears multiple times, the // last value overwrites all previous values for that key. To remove an // existing key, use a nil value. value should not be modified later. -func (a *Attributes) WithValue(key, value interface{}) *Attributes { +func (a *Attributes) WithValue(key, value any) *Attributes { if a == nil { return New(key, value) } - n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+1)} + n := &Attributes{m: make(map[any]any, len(a.m)+1)} for k, v := range a.m { n.m[k] = v } @@ -63,20 +63,19 @@ func (a *Attributes) WithValue(key, value interface{}) *Attributes { // Value returns the value associated with these attributes for key, or nil if // no value is associated with key. The returned value should not be modified. -func (a *Attributes) Value(key interface{}) interface{} { +func (a *Attributes) Value(key any) any { if a == nil { return nil } return a.m[key] } -// Equal returns whether a and o are equivalent. If 'Equal(o interface{}) -// bool' is implemented for a value in the attributes, it is called to -// determine if the value matches the one stored in the other attributes. If -// Equal is not implemented, standard equality is used to determine if the two -// values are equal. Note that some types (e.g. maps) aren't comparable by -// default, so they must be wrapped in a struct, or in an alias type, with Equal -// defined. +// Equal returns whether a and o are equivalent. If 'Equal(o any) bool' is +// implemented for a value in the attributes, it is called to determine if the +// value matches the one stored in the other attributes. If Equal is not +// implemented, standard equality is used to determine if the two values are +// equal. Note that some types (e.g. maps) aren't comparable by default, so +// they must be wrapped in a struct, or in an alias type, with Equal defined. func (a *Attributes) Equal(o *Attributes) bool { if a == nil && o == nil { return true @@ -93,7 +92,7 @@ func (a *Attributes) Equal(o *Attributes) bool { // o missing element of a return false } - if eq, ok := v.(interface{ Equal(o interface{}) bool }); ok { + if eq, ok := v.(interface{ Equal(o any) bool }); ok { if !eq.Equal(ov) { return false } @@ -112,19 +111,31 @@ func (a *Attributes) String() string { sb.WriteString("{") first := true for k, v := range a.m { - var key, val string - if str, ok := k.(interface{ String() string }); ok { - key = str.String() - } - if str, ok := v.(interface{ String() string }); ok { - val = str.String() - } if !first { sb.WriteString(", ") } - sb.WriteString(fmt.Sprintf("%q: %q, ", key, val)) + sb.WriteString(fmt.Sprintf("%q: %q ", str(k), str(v))) first = false } sb.WriteString("}") return sb.String() } + +func str(x any) (s string) { + if v, ok := x.(fmt.Stringer); ok { + return fmt.Sprint(v) + } else if v, ok := x.(string); ok { + return v + } + return fmt.Sprintf("<%p>", x) +} + +// MarshalJSON helps implement the json.Marshaler interface, thereby rendering +// the Attributes correctly when printing (via pretty.JSON) structs containing +// Attributes as fields. +// +// Is it impossible to unmarshal attributes from a JSON representation and this +// method is meant only for debugging purposes. +func (a *Attributes) MarshalJSON() ([]byte, error) { + return []byte(a.String()), nil +} diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index 8f00523c0..f391744f7 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -30,6 +30,7 @@ import ( "google.golang.org/grpc/channelz" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal" "google.golang.org/grpc/metadata" "google.golang.org/grpc/resolver" @@ -39,6 +40,8 @@ import ( var ( // m is a map from name to balancer builder. m = make(map[string]Builder) + + logger = grpclog.Component("balancer") ) // Register registers the balancer builder to the balancer map. b.Name @@ -51,7 +54,14 @@ var ( // an init() function), and is not thread-safe. If multiple Balancers are // registered with the same name, the one registered last will take effect. func Register(b Builder) { - m[strings.ToLower(b.Name())] = b + name := strings.ToLower(b.Name()) + if name != b.Name() { + // TODO: Skip the use of strings.ToLower() to index the map after v1.59 + // is released to switch to case sensitive balancer registry. Also, + // remove this warning and update the docstrings for Register and Get. + logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon", b.Name()) + } + m[name] = b } // unregisterForTesting deletes the balancer with the given name from the @@ -70,6 +80,12 @@ func init() { // Note that the compare is done in a case-insensitive fashion. // If no builder is register with the name, nil will be returned. func Get(name string) Builder { + if strings.ToLower(name) != name { + // TODO: Skip the use of strings.ToLower() to index the map after v1.59 + // is released to switch to case sensitive balancer registry. Also, + // remove this warning and update the docstrings for Register and Get. + logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon", name) + } if b, ok := m[strings.ToLower(name)]; ok { return b } @@ -105,8 +121,8 @@ type SubConn interface { // // This will trigger a state transition for the SubConn. // - // Deprecated: This method is now part of the ClientConn interface and will - // eventually be removed from here. + // Deprecated: this method will be removed. Create new SubConns for new + // addresses instead. UpdateAddresses([]resolver.Address) // Connect starts the connecting for this SubConn. Connect() @@ -115,6 +131,13 @@ type SubConn interface { // creates a new one and returns it. Returns a close function which must // be called when the Producer is no longer needed. GetOrBuildProducer(ProducerBuilder) (p Producer, close func()) + // Shutdown shuts down the SubConn gracefully. Any started RPCs will be + // allowed to complete. No future calls should be made on the SubConn. + // One final state update will be delivered to the StateListener (or + // UpdateSubConnState; deprecated) with ConnectivityState of Shutdown to + // indicate the shutdown operation. This may be delivered before + // in-progress RPCs are complete and the actual connection is closed. + Shutdown() } // NewSubConnOptions contains options to create new SubConn. @@ -129,6 +152,11 @@ type NewSubConnOptions struct { // HealthCheckEnabled indicates whether health check service should be // enabled on this SubConn HealthCheckEnabled bool + // StateListener is called when the state of the subconn changes. If nil, + // Balancer.UpdateSubConnState will be called instead. Will never be + // invoked until after Connect() is called on the SubConn created with + // these options. + StateListener func(SubConnState) } // State contains the balancer's state relevant to the gRPC ClientConn. @@ -150,16 +178,24 @@ type ClientConn interface { // NewSubConn is called by balancer to create a new SubConn. // It doesn't block and wait for the connections to be established. // Behaviors of the SubConn can be controlled by options. + // + // Deprecated: please be aware that in a future version, SubConns will only + // support one address per SubConn. NewSubConn([]resolver.Address, NewSubConnOptions) (SubConn, error) // RemoveSubConn removes the SubConn from ClientConn. // The SubConn will be shutdown. + // + // Deprecated: use SubConn.Shutdown instead. RemoveSubConn(SubConn) // UpdateAddresses updates the addresses used in the passed in SubConn. // gRPC checks if the currently connected address is still in the new list. // If so, the connection will be kept. Else, the connection will be // gracefully closed, and a new connection will be created. // - // This will trigger a state transition for the SubConn. + // This may trigger a state transition for the SubConn. + // + // Deprecated: this method will be removed. Create new SubConns for new + // addresses instead. UpdateAddresses(SubConn, []resolver.Address) // UpdateState notifies gRPC that the balancer's internal state has @@ -197,8 +233,8 @@ type BuildOptions struct { // implementations which do not communicate with a remote load balancer // server can ignore this field. Authority string - // ChannelzParentID is the parent ClientConn's channelz ID. - ChannelzParentID *channelz.Identifier + // ChannelzParent is the parent ClientConn's channelz channel. + ChannelzParent channelz.Identifier // CustomUserAgent is the custom user agent set on the parent ClientConn. // The balancer should set the same custom user agent if it creates a // ClientConn. @@ -250,7 +286,7 @@ type DoneInfo struct { // trailing metadata. // // The only supported type now is *orca_v3.LoadReport. - ServerLoad interface{} + ServerLoad any } var ( @@ -343,9 +379,13 @@ type Balancer interface { ResolverError(error) // UpdateSubConnState is called by gRPC when the state of a SubConn // changes. + // + // Deprecated: Use NewSubConnOptions.StateListener when creating the + // SubConn instead. UpdateSubConnState(SubConn, SubConnState) - // Close closes the balancer. The balancer is not required to call - // ClientConn.RemoveSubConn for its existing SubConns. + // Close closes the balancer. The balancer is not currently required to + // call SubConn.Shutdown for its existing SubConns; however, this will be + // required in a future release, so it is recommended. Close() } @@ -390,15 +430,14 @@ var ErrBadResolverState = errors.New("bad resolver state") type ProducerBuilder interface { // Build creates a Producer. The first parameter is always a // grpc.ClientConnInterface (a type to allow creating RPCs/streams on the - // associated SubConn), but is declared as interface{} to avoid a - // dependency cycle. Should also return a close function that will be - // called when all references to the Producer have been given up. - Build(grpcClientConnInterface interface{}) (p Producer, close func()) + // associated SubConn), but is declared as `any` to avoid a dependency + // cycle. Should also return a close function that will be called when all + // references to the Producer have been given up. + Build(grpcClientConnInterface any) (p Producer, close func()) } // A Producer is a type shared among potentially many consumers. It is // associated with a SubConn, and an implementation will typically contain // other methods to provide additional functionality, e.g. configuration or // subscription registration. -type Producer interface { -} +type Producer any diff --git a/vendor/google.golang.org/grpc/balancer/base/balancer.go b/vendor/google.golang.org/grpc/balancer/base/balancer.go index 3929c26d3..a7f1eeec8 100644 --- a/vendor/google.golang.org/grpc/balancer/base/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/base/balancer.go @@ -105,7 +105,12 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error { addrsSet.Set(a, nil) if _, ok := b.subConns.Get(a); !ok { // a is a new address (not existing in b.subConns). - sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck}) + var sc balancer.SubConn + opts := balancer.NewSubConnOptions{ + HealthCheckEnabled: b.config.HealthCheck, + StateListener: func(scs balancer.SubConnState) { b.updateSubConnState(sc, scs) }, + } + sc, err := b.cc.NewSubConn([]resolver.Address{a}, opts) if err != nil { logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err) continue @@ -121,10 +126,10 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error { sc := sci.(balancer.SubConn) // a was removed by resolver. if _, ok := addrsSet.Get(a); !ok { - b.cc.RemoveSubConn(sc) + sc.Shutdown() b.subConns.Delete(a) // Keep the state of this sc in b.scStates until sc's state becomes Shutdown. - // The entry will be deleted in UpdateSubConnState. + // The entry will be deleted in updateSubConnState. } } // If resolver state contains no addresses, return an error so ClientConn @@ -177,7 +182,12 @@ func (b *baseBalancer) regeneratePicker() { b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs}) } +// UpdateSubConnState is a nop because a StateListener is always set in NewSubConn. func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { + logger.Errorf("base.baseBalancer: UpdateSubConnState(%v, %+v) called unexpectedly", sc, state) +} + +func (b *baseBalancer) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { s := state.ConnectivityState if logger.V(2) { logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s) @@ -204,8 +214,8 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su case connectivity.Idle: sc.Connect() case connectivity.Shutdown: - // When an address was removed by resolver, b called RemoveSubConn but - // kept the sc's state in scStates. Remove state for this sc here. + // When an address was removed by resolver, b called Shutdown but kept + // the sc's state in scStates. Remove state for this sc here. delete(b.scStates, sc) case connectivity.TransientFailure: // Save error to be reported via picker. @@ -226,7 +236,7 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su } // Close is a nop because base balancer doesn't have internal state to clean up, -// and it doesn't need to call RemoveSubConn for the SubConns. +// and it doesn't need to call Shutdown for the SubConns. func (b *baseBalancer) Close() { } diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go deleted file mode 100644 index 04b9ad411..000000000 --- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go +++ /dev/null @@ -1,459 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "context" - "fmt" - "strings" - "sync" - - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/internal/balancer/gracefulswitch" - "google.golang.org/grpc/internal/channelz" - "google.golang.org/grpc/internal/grpcsync" - "google.golang.org/grpc/resolver" -) - -type ccbMode int - -const ( - ccbModeActive = iota - ccbModeIdle - ccbModeClosed - ccbModeExitingIdle -) - -// ccBalancerWrapper sits between the ClientConn and the Balancer. -// -// ccBalancerWrapper implements methods corresponding to the ones on the -// balancer.Balancer interface. The ClientConn is free to call these methods -// concurrently and the ccBalancerWrapper ensures that calls from the ClientConn -// to the Balancer happen synchronously and in order. -// -// ccBalancerWrapper also implements the balancer.ClientConn interface and is -// passed to the Balancer implementations. It invokes unexported methods on the -// ClientConn to handle these calls from the Balancer. -// -// It uses the gracefulswitch.Balancer internally to ensure that balancer -// switches happen in a graceful manner. -type ccBalancerWrapper struct { - // The following fields are initialized when the wrapper is created and are - // read-only afterwards, and therefore can be accessed without a mutex. - cc *ClientConn - opts balancer.BuildOptions - - // Outgoing (gRPC --> balancer) calls are guaranteed to execute in a - // mutually exclusive manner as they are scheduled in the serializer. Fields - // accessed *only* in these serializer callbacks, can therefore be accessed - // without a mutex. - balancer *gracefulswitch.Balancer - curBalancerName string - - // mu guards access to the below fields. Access to the serializer and its - // cancel function needs to be mutex protected because they are overwritten - // when the wrapper exits idle mode. - mu sync.Mutex - serializer *grpcsync.CallbackSerializer // To serialize all outoing calls. - serializerCancel context.CancelFunc // To close the seralizer at close/enterIdle time. - mode ccbMode // Tracks the current mode of the wrapper. -} - -// newCCBalancerWrapper creates a new balancer wrapper. The underlying balancer -// is not created until the switchTo() method is invoked. -func newCCBalancerWrapper(cc *ClientConn, bopts balancer.BuildOptions) *ccBalancerWrapper { - ctx, cancel := context.WithCancel(context.Background()) - ccb := &ccBalancerWrapper{ - cc: cc, - opts: bopts, - serializer: grpcsync.NewCallbackSerializer(ctx), - serializerCancel: cancel, - } - ccb.balancer = gracefulswitch.NewBalancer(ccb, bopts) - return ccb -} - -// updateClientConnState is invoked by grpc to push a ClientConnState update to -// the underlying balancer. -func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { - ccb.mu.Lock() - errCh := make(chan error, 1) - // Here and everywhere else where Schedule() is called, it is done with the - // lock held. But the lock guards only the scheduling part. The actual - // callback is called asynchronously without the lock being held. - ok := ccb.serializer.Schedule(func(_ context.Context) { - // If the addresses specified in the update contain addresses of type - // "grpclb" and the selected LB policy is not "grpclb", these addresses - // will be filtered out and ccs will be modified with the updated - // address list. - if ccb.curBalancerName != grpclbName { - var addrs []resolver.Address - for _, addr := range ccs.ResolverState.Addresses { - if addr.Type == resolver.GRPCLB { - continue - } - addrs = append(addrs, addr) - } - ccs.ResolverState.Addresses = addrs - } - errCh <- ccb.balancer.UpdateClientConnState(*ccs) - }) - if !ok { - // If we are unable to schedule a function with the serializer, it - // indicates that it has been closed. A serializer is only closed when - // the wrapper is closed or is in idle. - ccb.mu.Unlock() - return fmt.Errorf("grpc: cannot send state update to a closed or idle balancer") - } - ccb.mu.Unlock() - - // We get here only if the above call to Schedule succeeds, in which case it - // is guaranteed that the scheduled function will run. Therefore it is safe - // to block on this channel. - err := <-errCh - if logger.V(2) && err != nil { - logger.Infof("error from balancer.UpdateClientConnState: %v", err) - } - return err -} - -// updateSubConnState is invoked by grpc to push a subConn state update to the -// underlying balancer. -func (ccb *ccBalancerWrapper) updateSubConnState(sc balancer.SubConn, s connectivity.State, err error) { - ccb.mu.Lock() - ccb.serializer.Schedule(func(_ context.Context) { - ccb.balancer.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: s, ConnectionError: err}) - }) - ccb.mu.Unlock() -} - -func (ccb *ccBalancerWrapper) resolverError(err error) { - ccb.mu.Lock() - ccb.serializer.Schedule(func(_ context.Context) { - ccb.balancer.ResolverError(err) - }) - ccb.mu.Unlock() -} - -// switchTo is invoked by grpc to instruct the balancer wrapper to switch to the -// LB policy identified by name. -// -// ClientConn calls newCCBalancerWrapper() at creation time. Upon receipt of the -// first good update from the name resolver, it determines the LB policy to use -// and invokes the switchTo() method. Upon receipt of every subsequent update -// from the name resolver, it invokes this method. -// -// the ccBalancerWrapper keeps track of the current LB policy name, and skips -// the graceful balancer switching process if the name does not change. -func (ccb *ccBalancerWrapper) switchTo(name string) { - ccb.mu.Lock() - ccb.serializer.Schedule(func(_ context.Context) { - // TODO: Other languages use case-sensitive balancer registries. We should - // switch as well. See: https://github.com/grpc/grpc-go/issues/5288. - if strings.EqualFold(ccb.curBalancerName, name) { - return - } - ccb.buildLoadBalancingPolicy(name) - }) - ccb.mu.Unlock() -} - -// buildLoadBalancingPolicy performs the following: -// - retrieve a balancer builder for the given name. Use the default LB -// policy, pick_first, if no LB policy with name is found in the registry. -// - instruct the gracefulswitch balancer to switch to the above builder. This -// will actually build the new balancer. -// - update the `curBalancerName` field -// -// Must be called from a serializer callback. -func (ccb *ccBalancerWrapper) buildLoadBalancingPolicy(name string) { - builder := balancer.Get(name) - if builder == nil { - channelz.Warningf(logger, ccb.cc.channelzID, "Channel switches to new LB policy %q, since the specified LB policy %q was not registered", PickFirstBalancerName, name) - builder = newPickfirstBuilder() - } else { - channelz.Infof(logger, ccb.cc.channelzID, "Channel switches to new LB policy %q", name) - } - - if err := ccb.balancer.SwitchTo(builder); err != nil { - channelz.Errorf(logger, ccb.cc.channelzID, "Channel failed to build new LB policy %q: %v", name, err) - return - } - ccb.curBalancerName = builder.Name() -} - -func (ccb *ccBalancerWrapper) close() { - channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: closing") - ccb.closeBalancer(ccbModeClosed) -} - -// enterIdleMode is invoked by grpc when the channel enters idle mode upon -// expiry of idle_timeout. This call blocks until the balancer is closed. -func (ccb *ccBalancerWrapper) enterIdleMode() { - channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: entering idle mode") - ccb.closeBalancer(ccbModeIdle) -} - -// closeBalancer is invoked when the channel is being closed or when it enters -// idle mode upon expiry of idle_timeout. -func (ccb *ccBalancerWrapper) closeBalancer(m ccbMode) { - ccb.mu.Lock() - if ccb.mode == ccbModeClosed || ccb.mode == ccbModeIdle { - ccb.mu.Unlock() - return - } - - ccb.mode = m - done := ccb.serializer.Done - b := ccb.balancer - ok := ccb.serializer.Schedule(func(_ context.Context) { - // Close the serializer to ensure that no more calls from gRPC are sent - // to the balancer. - ccb.serializerCancel() - // Empty the current balancer name because we don't have a balancer - // anymore and also so that we act on the next call to switchTo by - // creating a new balancer specified by the new resolver. - ccb.curBalancerName = "" - }) - if !ok { - ccb.mu.Unlock() - return - } - ccb.mu.Unlock() - - // Give enqueued callbacks a chance to finish. - <-done - // Spawn a goroutine to close the balancer (since it may block trying to - // cleanup all allocated resources) and return early. - go b.Close() -} - -// exitIdleMode is invoked by grpc when the channel exits idle mode either -// because of an RPC or because of an invocation of the Connect() API. This -// recreates the balancer that was closed previously when entering idle mode. -// -// If the channel is not in idle mode, we know for a fact that we are here as a -// result of the user calling the Connect() method on the ClientConn. In this -// case, we can simply forward the call to the underlying balancer, instructing -// it to reconnect to the backends. -func (ccb *ccBalancerWrapper) exitIdleMode() { - ccb.mu.Lock() - if ccb.mode == ccbModeClosed { - // Request to exit idle is a no-op when wrapper is already closed. - ccb.mu.Unlock() - return - } - - if ccb.mode == ccbModeIdle { - // Recreate the serializer which was closed when we entered idle. - ctx, cancel := context.WithCancel(context.Background()) - ccb.serializer = grpcsync.NewCallbackSerializer(ctx) - ccb.serializerCancel = cancel - } - - // The ClientConn guarantees that mutual exclusion between close() and - // exitIdleMode(), and since we just created a new serializer, we can be - // sure that the below function will be scheduled. - done := make(chan struct{}) - ccb.serializer.Schedule(func(_ context.Context) { - defer close(done) - - ccb.mu.Lock() - defer ccb.mu.Unlock() - - if ccb.mode != ccbModeIdle { - ccb.balancer.ExitIdle() - return - } - - // Gracefulswitch balancer does not support a switchTo operation after - // being closed. Hence we need to create a new one here. - ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts) - ccb.mode = ccbModeActive - channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: exiting idle mode") - - }) - ccb.mu.Unlock() - - <-done -} - -func (ccb *ccBalancerWrapper) isIdleOrClosed() bool { - ccb.mu.Lock() - defer ccb.mu.Unlock() - return ccb.mode == ccbModeIdle || ccb.mode == ccbModeClosed -} - -func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { - if ccb.isIdleOrClosed() { - return nil, fmt.Errorf("grpc: cannot create SubConn when balancer is closed or idle") - } - - if len(addrs) == 0 { - return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") - } - ac, err := ccb.cc.newAddrConn(addrs, opts) - if err != nil { - channelz.Warningf(logger, ccb.cc.channelzID, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err) - return nil, err - } - acbw := &acBalancerWrapper{ac: ac, producers: make(map[balancer.ProducerBuilder]*refCountedProducer)} - ac.acbw = acbw - return acbw, nil -} - -func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { - if ccb.isIdleOrClosed() { - // It it safe to ignore this call when the balancer is closed or in idle - // because the ClientConn takes care of closing the connections. - // - // Not returning early from here when the balancer is closed or in idle - // leads to a deadlock though, because of the following sequence of - // calls when holding cc.mu: - // cc.exitIdleMode --> ccb.enterIdleMode --> gsw.Close --> - // ccb.RemoveAddrConn --> cc.removeAddrConn - return - } - - acbw, ok := sc.(*acBalancerWrapper) - if !ok { - return - } - ccb.cc.removeAddrConn(acbw.ac, errConnDrain) -} - -func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { - if ccb.isIdleOrClosed() { - return - } - - acbw, ok := sc.(*acBalancerWrapper) - if !ok { - return - } - acbw.UpdateAddresses(addrs) -} - -func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { - if ccb.isIdleOrClosed() { - return - } - - // Update picker before updating state. Even though the ordering here does - // not matter, it can lead to multiple calls of Pick in the common start-up - // case where we wait for ready and then perform an RPC. If the picker is - // updated later, we could call the "connecting" picker when the state is - // updated, and then call the "ready" picker after the picker gets updated. - ccb.cc.blockingpicker.updatePicker(s.Picker) - ccb.cc.csMgr.updateState(s.ConnectivityState) -} - -func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) { - if ccb.isIdleOrClosed() { - return - } - - ccb.cc.resolveNow(o) -} - -func (ccb *ccBalancerWrapper) Target() string { - return ccb.cc.target -} - -// acBalancerWrapper is a wrapper on top of ac for balancers. -// It implements balancer.SubConn interface. -type acBalancerWrapper struct { - ac *addrConn // read-only - - mu sync.Mutex - producers map[balancer.ProducerBuilder]*refCountedProducer -} - -func (acbw *acBalancerWrapper) String() string { - return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelzID.Int()) -} - -func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { - acbw.ac.updateAddrs(addrs) -} - -func (acbw *acBalancerWrapper) Connect() { - go acbw.ac.connect() -} - -// NewStream begins a streaming RPC on the addrConn. If the addrConn is not -// ready, blocks until it is or ctx expires. Returns an error when the context -// expires or the addrConn is shut down. -func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { - transport, err := acbw.ac.getTransport(ctx) - if err != nil { - return nil, err - } - return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...) -} - -// Invoke performs a unary RPC. If the addrConn is not ready, returns -// errSubConnNotReady. -func (acbw *acBalancerWrapper) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error { - cs, err := acbw.NewStream(ctx, unaryStreamDesc, method, opts...) - if err != nil { - return err - } - if err := cs.SendMsg(args); err != nil { - return err - } - return cs.RecvMsg(reply) -} - -type refCountedProducer struct { - producer balancer.Producer - refs int // number of current refs to the producer - close func() // underlying producer's close function -} - -func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) (balancer.Producer, func()) { - acbw.mu.Lock() - defer acbw.mu.Unlock() - - // Look up existing producer from this builder. - pData := acbw.producers[pb] - if pData == nil { - // Not found; create a new one and add it to the producers map. - p, close := pb.Build(acbw) - pData = &refCountedProducer{producer: p, close: close} - acbw.producers[pb] = pData - } - // Account for this new reference. - pData.refs++ - - // Return a cleanup function wrapped in a OnceFunc to remove this reference - // and delete the refCountedProducer from the map if the total reference - // count goes to zero. - unref := func() { - acbw.mu.Lock() - pData.refs-- - if pData.refs == 0 { - defer pData.close() // Run outside the acbw mutex - delete(acbw.producers, pb) - } - acbw.mu.Unlock() - } - return pData.producer, grpcsync.OnceFunc(unref) -} diff --git a/vendor/google.golang.org/grpc/balancer_wrapper.go b/vendor/google.golang.org/grpc/balancer_wrapper.go new file mode 100644 index 000000000..af39b8a4c --- /dev/null +++ b/vendor/google.golang.org/grpc/balancer_wrapper.go @@ -0,0 +1,337 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "context" + "fmt" + "sync" + + "google.golang.org/grpc/balancer" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/internal/balancer/gracefulswitch" + "google.golang.org/grpc/internal/channelz" + "google.golang.org/grpc/internal/grpcsync" + "google.golang.org/grpc/resolver" +) + +// ccBalancerWrapper sits between the ClientConn and the Balancer. +// +// ccBalancerWrapper implements methods corresponding to the ones on the +// balancer.Balancer interface. The ClientConn is free to call these methods +// concurrently and the ccBalancerWrapper ensures that calls from the ClientConn +// to the Balancer happen in order by performing them in the serializer, without +// any mutexes held. +// +// ccBalancerWrapper also implements the balancer.ClientConn interface and is +// passed to the Balancer implementations. It invokes unexported methods on the +// ClientConn to handle these calls from the Balancer. +// +// It uses the gracefulswitch.Balancer internally to ensure that balancer +// switches happen in a graceful manner. +type ccBalancerWrapper struct { + // The following fields are initialized when the wrapper is created and are + // read-only afterwards, and therefore can be accessed without a mutex. + cc *ClientConn + opts balancer.BuildOptions + serializer *grpcsync.CallbackSerializer + serializerCancel context.CancelFunc + + // The following fields are only accessed within the serializer or during + // initialization. + curBalancerName string + balancer *gracefulswitch.Balancer + + // The following field is protected by mu. Caller must take cc.mu before + // taking mu. + mu sync.Mutex + closed bool +} + +// newCCBalancerWrapper creates a new balancer wrapper in idle state. The +// underlying balancer is not created until the updateClientConnState() method +// is invoked. +func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper { + ctx, cancel := context.WithCancel(cc.ctx) + ccb := &ccBalancerWrapper{ + cc: cc, + opts: balancer.BuildOptions{ + DialCreds: cc.dopts.copts.TransportCredentials, + CredsBundle: cc.dopts.copts.CredsBundle, + Dialer: cc.dopts.copts.Dialer, + Authority: cc.authority, + CustomUserAgent: cc.dopts.copts.UserAgent, + ChannelzParent: cc.channelz, + Target: cc.parsedTarget, + }, + serializer: grpcsync.NewCallbackSerializer(ctx), + serializerCancel: cancel, + } + ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts) + return ccb +} + +// updateClientConnState is invoked by grpc to push a ClientConnState update to +// the underlying balancer. This is always executed from the serializer, so +// it is safe to call into the balancer here. +func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { + errCh := make(chan error) + ok := ccb.serializer.Schedule(func(ctx context.Context) { + defer close(errCh) + if ctx.Err() != nil || ccb.balancer == nil { + return + } + name := gracefulswitch.ChildName(ccs.BalancerConfig) + if ccb.curBalancerName != name { + ccb.curBalancerName = name + channelz.Infof(logger, ccb.cc.channelz, "Channel switches to new LB policy %q", name) + } + err := ccb.balancer.UpdateClientConnState(*ccs) + if logger.V(2) && err != nil { + logger.Infof("error from balancer.UpdateClientConnState: %v", err) + } + errCh <- err + }) + if !ok { + return nil + } + return <-errCh +} + +// resolverError is invoked by grpc to push a resolver error to the underlying +// balancer. The call to the balancer is executed from the serializer. +func (ccb *ccBalancerWrapper) resolverError(err error) { + ccb.serializer.Schedule(func(ctx context.Context) { + if ctx.Err() != nil || ccb.balancer == nil { + return + } + ccb.balancer.ResolverError(err) + }) +} + +// close initiates async shutdown of the wrapper. cc.mu must be held when +// calling this function. To determine the wrapper has finished shutting down, +// the channel should block on ccb.serializer.Done() without cc.mu held. +func (ccb *ccBalancerWrapper) close() { + ccb.mu.Lock() + ccb.closed = true + ccb.mu.Unlock() + channelz.Info(logger, ccb.cc.channelz, "ccBalancerWrapper: closing") + ccb.serializer.Schedule(func(context.Context) { + if ccb.balancer == nil { + return + } + ccb.balancer.Close() + ccb.balancer = nil + }) + ccb.serializerCancel() +} + +// exitIdle invokes the balancer's exitIdle method in the serializer. +func (ccb *ccBalancerWrapper) exitIdle() { + ccb.serializer.Schedule(func(ctx context.Context) { + if ctx.Err() != nil || ccb.balancer == nil { + return + } + ccb.balancer.ExitIdle() + }) +} + +func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { + ccb.cc.mu.Lock() + defer ccb.cc.mu.Unlock() + + ccb.mu.Lock() + if ccb.closed { + ccb.mu.Unlock() + return nil, fmt.Errorf("balancer is being closed; no new SubConns allowed") + } + ccb.mu.Unlock() + + if len(addrs) == 0 { + return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") + } + ac, err := ccb.cc.newAddrConnLocked(addrs, opts) + if err != nil { + channelz.Warningf(logger, ccb.cc.channelz, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err) + return nil, err + } + acbw := &acBalancerWrapper{ + ccb: ccb, + ac: ac, + producers: make(map[balancer.ProducerBuilder]*refCountedProducer), + stateListener: opts.StateListener, + } + ac.acbw = acbw + return acbw, nil +} + +func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { + // The graceful switch balancer will never call this. + logger.Errorf("ccb RemoveSubConn(%v) called unexpectedly, sc") +} + +func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { + acbw, ok := sc.(*acBalancerWrapper) + if !ok { + return + } + acbw.UpdateAddresses(addrs) +} + +func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { + ccb.cc.mu.Lock() + defer ccb.cc.mu.Unlock() + + ccb.mu.Lock() + if ccb.closed { + ccb.mu.Unlock() + return + } + ccb.mu.Unlock() + // Update picker before updating state. Even though the ordering here does + // not matter, it can lead to multiple calls of Pick in the common start-up + // case where we wait for ready and then perform an RPC. If the picker is + // updated later, we could call the "connecting" picker when the state is + // updated, and then call the "ready" picker after the picker gets updated. + + // Note that there is no need to check if the balancer wrapper was closed, + // as we know the graceful switch LB policy will not call cc if it has been + // closed. + ccb.cc.pickerWrapper.updatePicker(s.Picker) + ccb.cc.csMgr.updateState(s.ConnectivityState) +} + +func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) { + ccb.cc.mu.RLock() + defer ccb.cc.mu.RUnlock() + + ccb.mu.Lock() + if ccb.closed { + ccb.mu.Unlock() + return + } + ccb.mu.Unlock() + ccb.cc.resolveNowLocked(o) +} + +func (ccb *ccBalancerWrapper) Target() string { + return ccb.cc.target +} + +// acBalancerWrapper is a wrapper on top of ac for balancers. +// It implements balancer.SubConn interface. +type acBalancerWrapper struct { + ac *addrConn // read-only + ccb *ccBalancerWrapper // read-only + stateListener func(balancer.SubConnState) + + mu sync.Mutex + producers map[balancer.ProducerBuilder]*refCountedProducer +} + +// updateState is invoked by grpc to push a subConn state update to the +// underlying balancer. +func (acbw *acBalancerWrapper) updateState(s connectivity.State, err error) { + acbw.ccb.serializer.Schedule(func(ctx context.Context) { + if ctx.Err() != nil || acbw.ccb.balancer == nil { + return + } + // Even though it is optional for balancers, gracefulswitch ensures + // opts.StateListener is set, so this cannot ever be nil. + // TODO: delete this comment when UpdateSubConnState is removed. + acbw.stateListener(balancer.SubConnState{ConnectivityState: s, ConnectionError: err}) + }) +} + +func (acbw *acBalancerWrapper) String() string { + return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelz.ID) +} + +func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { + acbw.ac.updateAddrs(addrs) +} + +func (acbw *acBalancerWrapper) Connect() { + go acbw.ac.connect() +} + +func (acbw *acBalancerWrapper) Shutdown() { + acbw.ccb.cc.removeAddrConn(acbw.ac, errConnDrain) +} + +// NewStream begins a streaming RPC on the addrConn. If the addrConn is not +// ready, blocks until it is or ctx expires. Returns an error when the context +// expires or the addrConn is shut down. +func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { + transport, err := acbw.ac.getTransport(ctx) + if err != nil { + return nil, err + } + return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...) +} + +// Invoke performs a unary RPC. If the addrConn is not ready, returns +// errSubConnNotReady. +func (acbw *acBalancerWrapper) Invoke(ctx context.Context, method string, args any, reply any, opts ...CallOption) error { + cs, err := acbw.NewStream(ctx, unaryStreamDesc, method, opts...) + if err != nil { + return err + } + if err := cs.SendMsg(args); err != nil { + return err + } + return cs.RecvMsg(reply) +} + +type refCountedProducer struct { + producer balancer.Producer + refs int // number of current refs to the producer + close func() // underlying producer's close function +} + +func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) (balancer.Producer, func()) { + acbw.mu.Lock() + defer acbw.mu.Unlock() + + // Look up existing producer from this builder. + pData := acbw.producers[pb] + if pData == nil { + // Not found; create a new one and add it to the producers map. + p, close := pb.Build(acbw) + pData = &refCountedProducer{producer: p, close: close} + acbw.producers[pb] = pData + } + // Account for this new reference. + pData.refs++ + + // Return a cleanup function wrapped in a OnceFunc to remove this reference + // and delete the refCountedProducer from the map if the total reference + // count goes to zero. + unref := func() { + acbw.mu.Lock() + pData.refs-- + if pData.refs == 0 { + defer pData.close() // Run outside the acbw mutex + delete(acbw.producers, pb) + } + acbw.mu.Unlock() + } + return pData.producer, grpcsync.OnceFunc(unref) +} diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go index ec2c2fa14..856c75dd4 100644 --- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go +++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go @@ -18,8 +18,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v4.22.0 +// protoc-gen-go v1.32.0 +// protoc v4.25.2 // source: grpc/binlog/v1/binarylog.proto package grpc_binarylog_v1 @@ -430,7 +430,7 @@ type ClientHeader struct { MethodName string `protobuf:"bytes,2,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` // A single process may be used to run multiple virtual // servers with different identities. - // The authority is the name of such a server identitiy. + // The authority is the name of such a server identity. // It is typically a portion of the URI in the form of // or : . Authority string `protobuf:"bytes,3,opt,name=authority,proto3" json:"authority,omitempty"` diff --git a/vendor/google.golang.org/grpc/call.go b/vendor/google.golang.org/grpc/call.go index e6a1dc5d7..788c89c16 100644 --- a/vendor/google.golang.org/grpc/call.go +++ b/vendor/google.golang.org/grpc/call.go @@ -26,12 +26,7 @@ import ( // received. This is typically called by generated code. // // All errors returned by Invoke are compatible with the status package. -func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error { - if err := cc.idlenessMgr.onCallBegin(); err != nil { - return err - } - defer cc.idlenessMgr.onCallEnd() - +func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply any, opts ...CallOption) error { // allow interceptor to see all applicable call options, which means those // configured as defaults from dial option as well as per-call options opts = combine(cc.dopts.callOptions, opts) @@ -61,13 +56,13 @@ func combine(o1 []CallOption, o2 []CallOption) []CallOption { // received. This is typically called by generated code. // // DEPRECATED: Use ClientConn.Invoke instead. -func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) error { +func Invoke(ctx context.Context, method string, args, reply any, cc *ClientConn, opts ...CallOption) error { return cc.Invoke(ctx, method, args, reply, opts...) } var unaryStreamDesc = &StreamDesc{ServerStreams: false, ClientStreams: false} -func invoke(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error { +func invoke(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error { cs, err := newClientStream(ctx, unaryStreamDesc, cc, method, opts...) if err != nil { return err diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index 95a7459b0..c7f260711 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -33,10 +33,11 @@ import ( "google.golang.org/grpc/balancer/base" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/internal/backoff" + "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" + "google.golang.org/grpc/internal/idle" + "google.golang.org/grpc/internal/pretty" iresolver "google.golang.org/grpc/internal/resolver" "google.golang.org/grpc/internal/transport" "google.golang.org/grpc/keepalive" @@ -45,16 +46,14 @@ import ( "google.golang.org/grpc/status" _ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin. - _ "google.golang.org/grpc/internal/resolver/dns" // To register dns resolver. _ "google.golang.org/grpc/internal/resolver/passthrough" // To register passthrough resolver. _ "google.golang.org/grpc/internal/resolver/unix" // To register unix resolver. + _ "google.golang.org/grpc/resolver/dns" // To register dns resolver. ) const ( // minimum time to give a connection to complete minConnectTimeout = 20 * time.Second - // must match grpclbName in grpclb/grpclb.go - grpclbName = "grpclb" ) var ( @@ -68,7 +67,7 @@ var ( errConnDrain = errors.New("grpc: the connection is drained") // errConnClosing indicates that the connection is closing. errConnClosing = errors.New("grpc: the connection is closing") - // errConnIdling indicates the the connection is being closed as the channel + // errConnIdling indicates the connection is being closed as the channel // is moving to an idle mode due to inactivity. errConnIdling = errors.New("grpc: the connection is closing due to channel idleness") // invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default @@ -102,11 +101,6 @@ const ( defaultReadBufSize = 32 * 1024 ) -// Dial creates a client connection to the given target. -func Dial(target string, opts ...DialOption) (*ClientConn, error) { - return DialContext(context.Background(), target, opts...) -} - type defaultConfigSelector struct { sc *ServiceConfig } @@ -118,48 +112,29 @@ func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*ires }, nil } -// DialContext creates a client connection to the given target. By default, it's -// a non-blocking dial (the function won't wait for connections to be -// established, and connecting happens in the background). To make it a blocking -// dial, use WithBlock() dial option. -// -// In the non-blocking case, the ctx does not act against the connection. It -// only controls the setup steps. -// -// In the blocking case, ctx can be used to cancel or expire the pending -// connection. Once this function returns, the cancellation and expiration of -// ctx will be noop. Users should call ClientConn.Close to terminate all the -// pending operations after this function returns. +// NewClient creates a new gRPC "channel" for the target URI provided. No I/O +// is performed. Use of the ClientConn for RPCs will automatically cause it to +// connect. Connect may be used to manually create a connection, but for most +// users this is unnecessary. // // The target name syntax is defined in -// https://github.com/grpc/grpc/blob/master/doc/naming.md. -// e.g. to use dns resolver, a "dns:///" prefix should be applied to the target. -func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { +// https://github.com/grpc/grpc/blob/master/doc/naming.md. e.g. to use dns +// resolver, a "dns:///" prefix should be applied to the target. +// +// The DialOptions returned by WithBlock, WithTimeout, and +// WithReturnConnectionError are ignored by this function. +func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) { cc := &ClientConn{ target: target, - csMgr: &connectivityStateManager{}, conns: make(map[*addrConn]struct{}), dopts: defaultDialOptions(), - czData: new(channelzData), } - // We start the channel off in idle mode, but kick it out of idle at the end - // of this method, instead of waiting for the first RPC. Other gRPC - // implementations do wait for the first RPC to kick the channel out of - // idle. But doing so would be a major behavior change for our users who are - // used to seeing the channel active after Dial. - // - // Taking this approach of kicking it out of idle at the end of this method - // allows us to share the code between channel creation and exiting idle - // mode. This will also make it easy for us to switch to starting the - // channel off in idle, if at all we ever get to do that. - cc.idlenessState = ccIdlenessStateIdle - cc.retryThrottler.Store((*retryThrottler)(nil)) cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) cc.ctx, cc.cancel = context.WithCancel(context.Background()) - cc.exitIdleCond = sync.NewCond(&cc.mu) + // Apply dial options. disableGlobalOpts := false for _, opt := range opts { if _, ok := opt.(*disableGlobalDialOptions); ok { @@ -177,19 +152,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * for _, opt := range opts { opt.apply(&cc.dopts) } - chainUnaryClientInterceptors(cc) chainStreamClientInterceptors(cc) - defer func() { - if err != nil { - cc.Close() - } - }() - - // Register ClientConn with channelz. - cc.channelzRegistration(target) - if err := cc.validateTransportCredentials(); err != nil { return nil, err } @@ -203,10 +168,73 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * } cc.mkp = cc.dopts.copts.KeepaliveParams - if cc.dopts.copts.UserAgent != "" { - cc.dopts.copts.UserAgent += " " + grpcUA - } else { - cc.dopts.copts.UserAgent = grpcUA + // Register ClientConn with channelz. + cc.channelzRegistration(target) + + // TODO: Ideally it should be impossible to error from this function after + // channelz registration. This will require removing some channelz logs + // from the following functions that can error. Errors can be returned to + // the user, and successful logs can be emitted here, after the checks have + // passed and channelz is subsequently registered. + + // Determine the resolver to use. + if err := cc.parseTargetAndFindResolver(); err != nil { + channelz.RemoveEntry(cc.channelz.ID) + return nil, err + } + if err = cc.determineAuthority(); err != nil { + channelz.RemoveEntry(cc.channelz.ID) + return nil, err + } + + cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelz) + cc.pickerWrapper = newPickerWrapper(cc.dopts.copts.StatsHandlers) + + cc.initIdleStateLocked() // Safe to call without the lock, since nothing else has a reference to cc. + cc.idlenessMgr = idle.NewManager((*idler)(cc), cc.dopts.idleTimeout) + return cc, nil +} + +// Dial calls DialContext(context.Background(), target, opts...). +func Dial(target string, opts ...DialOption) (*ClientConn, error) { + return DialContext(context.Background(), target, opts...) +} + +// DialContext calls NewClient and then exits idle mode. If WithBlock(true) is +// used, it calls Connect and WaitForStateChange until either the context +// expires or the state of the ClientConn is Ready. +// +// One subtle difference between NewClient and Dial and DialContext is that the +// former uses "dns" as the default name resolver, while the latter use +// "passthrough" for backward compatibility. This distinction should not matter +// to most users, but could matter to legacy users that specify a custom dialer +// and expect it to receive the target string directly. +func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { + // At the end of this method, we kick the channel out of idle, rather than + // waiting for the first rpc. + opts = append([]DialOption{withDefaultScheme("passthrough")}, opts...) + cc, err := NewClient(target, opts...) + if err != nil { + return nil, err + } + + // We start the channel off in idle mode, but kick it out of idle now, + // instead of waiting for the first RPC. This is the legacy behavior of + // Dial. + defer func() { + if err != nil { + cc.Close() + } + }() + + // This creates the name resolver, load balancer, etc. + if err := cc.idlenessMgr.ExitIdleMode(); err != nil { + return nil, err + } + + // Return now for non-blocking dials. + if !cc.dopts.block { + return cc, nil } if cc.dopts.timeout > 0 { @@ -229,49 +257,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * } }() - if cc.dopts.bs == nil { - cc.dopts.bs = backoff.DefaultExponential - } - - // Determine the resolver to use. - if err := cc.parseTargetAndFindResolver(); err != nil { - return nil, err - } - if err = cc.determineAuthority(); err != nil { - return nil, err - } - - if cc.dopts.scChan != nil { - // Blocking wait for the initial service config. - select { - case sc, ok := <-cc.dopts.scChan: - if ok { - cc.sc = &sc - cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc}) - } - case <-ctx.Done(): - return nil, ctx.Err() - } - } - if cc.dopts.scChan != nil { - go cc.scWatcher() - } - - // This creates the name resolver, load balancer, blocking picker etc. - if err := cc.exitIdleMode(); err != nil { - return nil, err - } - - // Configure idleness support with configured idle timeout or default idle - // timeout duration. Idleness can be explicitly disabled by the user, by - // setting the dial option to 0. - cc.idlenessMgr = newIdlenessManager(cc, cc.dopts.idleTimeout) - - // Return early for non-blocking dials. - if !cc.dopts.block { - return cc, nil - } - // A blocking dial blocks until the clientConn is ready. for { s := cc.GetState() @@ -303,130 +288,95 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * // addTraceEvent is a helper method to add a trace event on the channel. If the // channel is a nested one, the same event is also added on the parent channel. func (cc *ClientConn) addTraceEvent(msg string) { - ted := &channelz.TraceEventDesc{ + ted := &channelz.TraceEvent{ Desc: fmt.Sprintf("Channel %s", msg), Severity: channelz.CtInfo, } - if cc.dopts.channelzParentID != nil { - ted.Parent = &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Nested channel(id:%d) %s", cc.channelzID.Int(), msg), + if cc.dopts.channelzParent != nil { + ted.Parent = &channelz.TraceEvent{ + Desc: fmt.Sprintf("Nested channel(id:%d) %s", cc.channelz.ID, msg), Severity: channelz.CtInfo, } } - channelz.AddTraceEvent(logger, cc.channelzID, 0, ted) + channelz.AddTraceEvent(logger, cc.channelz, 0, ted) +} + +type idler ClientConn + +func (i *idler) EnterIdleMode() { + (*ClientConn)(i).enterIdleMode() +} + +func (i *idler) ExitIdleMode() error { + return (*ClientConn)(i).exitIdleMode() } // exitIdleMode moves the channel out of idle mode by recreating the name -// resolver and load balancer. -func (cc *ClientConn) exitIdleMode() error { +// resolver and load balancer. This should never be called directly; use +// cc.idlenessMgr.ExitIdleMode instead. +func (cc *ClientConn) exitIdleMode() (err error) { cc.mu.Lock() if cc.conns == nil { cc.mu.Unlock() return errConnClosing } - if cc.idlenessState != ccIdlenessStateIdle { - cc.mu.Unlock() - logger.Info("ClientConn asked to exit idle mode when not in idle mode") - return nil - } - - defer func() { - // When Close() and exitIdleMode() race against each other, one of the - // following two can happen: - // - Close() wins the race and runs first. exitIdleMode() runs after, and - // sees that the ClientConn is already closed and hence returns early. - // - exitIdleMode() wins the race and runs first and recreates the balancer - // and releases the lock before recreating the resolver. If Close() runs - // in this window, it will wait for exitIdleMode to complete. - // - // We achieve this synchronization using the below condition variable. - cc.mu.Lock() - cc.idlenessState = ccIdlenessStateActive - cc.exitIdleCond.Signal() - cc.mu.Unlock() - }() - - cc.idlenessState = ccIdlenessStateExitingIdle - exitedIdle := false - if cc.blockingpicker == nil { - cc.blockingpicker = newPickerWrapper() - } else { - cc.blockingpicker.exitIdleMode() - exitedIdle = true - } - - var credsClone credentials.TransportCredentials - if creds := cc.dopts.copts.TransportCredentials; creds != nil { - credsClone = creds.Clone() - } - if cc.balancerWrapper == nil { - cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{ - DialCreds: credsClone, - CredsBundle: cc.dopts.copts.CredsBundle, - Dialer: cc.dopts.copts.Dialer, - Authority: cc.authority, - CustomUserAgent: cc.dopts.copts.UserAgent, - ChannelzParentID: cc.channelzID, - Target: cc.parsedTarget, - }) - } else { - cc.balancerWrapper.exitIdleMode() - } - cc.firstResolveEvent = grpcsync.NewEvent() cc.mu.Unlock() // This needs to be called without cc.mu because this builds a new resolver - // which might update state or report error inline which needs to be handled - // by cc.updateResolverState() which also grabs cc.mu. - if err := cc.initResolverWrapper(credsClone); err != nil { + // which might update state or report error inline, which would then need to + // acquire cc.mu. + if err := cc.resolverWrapper.start(); err != nil { return err } - if exitedIdle { - cc.addTraceEvent("exiting idle mode") - } + cc.addTraceEvent("exiting idle mode") return nil } +// initIdleStateLocked initializes common state to how it should be while idle. +func (cc *ClientConn) initIdleStateLocked() { + cc.resolverWrapper = newCCResolverWrapper(cc) + cc.balancerWrapper = newCCBalancerWrapper(cc) + cc.firstResolveEvent = grpcsync.NewEvent() + // cc.conns == nil is a proxy for the ClientConn being closed. So, instead + // of setting it to nil here, we recreate the map. This also means that we + // don't have to do this when exiting idle mode. + cc.conns = make(map[*addrConn]struct{}) +} + // enterIdleMode puts the channel in idle mode, and as part of it shuts down the -// name resolver, load balancer and any subchannels. -func (cc *ClientConn) enterIdleMode() error { +// name resolver, load balancer, and any subchannels. This should never be +// called directly; use cc.idlenessMgr.EnterIdleMode instead. +func (cc *ClientConn) enterIdleMode() { cc.mu.Lock() + if cc.conns == nil { cc.mu.Unlock() - return ErrClientConnClosing - } - if cc.idlenessState != ccIdlenessStateActive { - logger.Error("ClientConn asked to enter idle mode when not active") - return nil + return } - // cc.conns == nil is a proxy for the ClientConn being closed. So, instead - // of setting it to nil here, we recreate the map. This also means that we - // don't have to do this when exiting idle mode. conns := cc.conns - cc.conns = make(map[*addrConn]struct{}) - // TODO: Currently, we close the resolver wrapper upon entering idle mode - // and create a new one upon exiting idle mode. This means that the - // `cc.resolverWrapper` field would be overwritten everytime we exit idle - // mode. While this means that we need to hold `cc.mu` when accessing - // `cc.resolverWrapper`, it makes the code simpler in the wrapper. We should - // try to do the same for the balancer and picker wrappers too. - cc.resolverWrapper.close() - cc.blockingpicker.enterIdleMode() - cc.balancerWrapper.enterIdleMode() + rWrapper := cc.resolverWrapper + rWrapper.close() + cc.pickerWrapper.reset() + bWrapper := cc.balancerWrapper + bWrapper.close() cc.csMgr.updateState(connectivity.Idle) - cc.idlenessState = ccIdlenessStateIdle + cc.addTraceEvent("entering idle mode") + + cc.initIdleStateLocked() + cc.mu.Unlock() - go func() { - cc.addTraceEvent("entering idle mode") - for ac := range conns { - ac.tearDown(errConnIdling) - } - }() - return nil + // Block until the name resolver and LB policy are closed. + <-rWrapper.serializer.Done() + <-bWrapper.serializer.Done() + + // Close all subchannels after the LB policy is closed. + for ac := range conns { + ac.tearDown(errConnIdling) + } } // validateTransportCredentials performs a series of checks on the configured @@ -465,16 +415,16 @@ func (cc *ClientConn) validateTransportCredentials() error { } // channelzRegistration registers the newly created ClientConn with channelz and -// stores the returned identifier in `cc.channelzID` and `cc.csMgr.channelzID`. -// A channelz trace event is emitted for ClientConn creation. If the newly -// created ClientConn is a nested one, i.e a valid parent ClientConn ID is -// specified via a dial option, the trace event is also added to the parent. +// stores the returned identifier in `cc.channelz`. A channelz trace event is +// emitted for ClientConn creation. If the newly created ClientConn is a nested +// one, i.e a valid parent ClientConn ID is specified via a dial option, the +// trace event is also added to the parent. // // Doesn't grab cc.mu as this method is expected to be called only at Dial time. func (cc *ClientConn) channelzRegistration(target string) { - cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target) + parentChannel, _ := cc.dopts.channelzParent.(*channelz.Channel) + cc.channelz = channelz.RegisterChannel(parentChannel, target) cc.addTraceEvent("created") - cc.csMgr.channelzID = cc.channelzID } // chainUnaryClientInterceptors chains all unary client interceptors into one. @@ -491,7 +441,7 @@ func chainUnaryClientInterceptors(cc *ClientConn) { } else if len(interceptors) == 1 { chainedInt = interceptors[0] } else { - chainedInt = func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error { + chainedInt = func(ctx context.Context, method string, req, reply any, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error { return interceptors[0](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, 0, invoker), opts...) } } @@ -503,7 +453,7 @@ func getChainUnaryInvoker(interceptors []UnaryClientInterceptor, curr int, final if curr == len(interceptors)-1 { return finalInvoker } - return func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error { + return func(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error { return interceptors[curr+1](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, curr+1, finalInvoker), opts...) } } @@ -539,13 +489,27 @@ func getChainStreamer(interceptors []StreamClientInterceptor, curr int, finalStr } } +// newConnectivityStateManager creates an connectivityStateManager with +// the specified channel. +func newConnectivityStateManager(ctx context.Context, channel *channelz.Channel) *connectivityStateManager { + return &connectivityStateManager{ + channelz: channel, + pubSub: grpcsync.NewPubSub(ctx), + } +} + // connectivityStateManager keeps the connectivity.State of ClientConn. // This struct will eventually be exported so the balancers can access it. +// +// TODO: If possible, get rid of the `connectivityStateManager` type, and +// provide this functionality using the `PubSub`, to avoid keeping track of +// the connectivity state at two places. type connectivityStateManager struct { mu sync.Mutex state connectivity.State notifyChan chan struct{} - channelzID *channelz.Identifier + channelz *channelz.Channel + pubSub *grpcsync.PubSub } // updateState updates the connectivity.State of ClientConn. @@ -561,7 +525,10 @@ func (csm *connectivityStateManager) updateState(state connectivity.State) { return } csm.state = state - channelz.Infof(logger, csm.channelzID, "Channel Connectivity change to %v", state) + csm.channelz.ChannelMetrics.State.Store(&state) + csm.pubSub.Publish(state) + + channelz.Infof(logger, csm.channelz, "Channel Connectivity change to %v", state) if csm.notifyChan != nil { // There are other goroutines waiting on this channel. close(csm.notifyChan) @@ -590,7 +557,7 @@ func (csm *connectivityStateManager) getNotifyChan() <-chan struct{} { type ClientConnInterface interface { // Invoke performs a unary RPC and returns after the response is received // into reply. - Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error + Invoke(ctx context.Context, method string, args any, reply any, opts ...CallOption) error // NewStream begins a streaming RPC. NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) } @@ -615,59 +582,40 @@ type ClientConn struct { cancel context.CancelFunc // Cancelled on close. // The following are initialized at dial time, and are read-only after that. - target string // User's dial target. - parsedTarget resolver.Target // See parseTargetAndFindResolver(). - authority string // See determineAuthority(). - dopts dialOptions // Default and user specified dial options. - channelzID *channelz.Identifier // Channelz identifier for the channel. - resolverBuilder resolver.Builder // See parseTargetAndFindResolver(). - balancerWrapper *ccBalancerWrapper // Uses gracefulswitch.balancer underneath. - idlenessMgr idlenessManager + target string // User's dial target. + parsedTarget resolver.Target // See parseTargetAndFindResolver(). + authority string // See determineAuthority(). + dopts dialOptions // Default and user specified dial options. + channelz *channelz.Channel // Channelz object. + resolverBuilder resolver.Builder // See parseTargetAndFindResolver(). + idlenessMgr *idle.Manager // The following provide their own synchronization, and therefore don't // require cc.mu to be held to access them. csMgr *connectivityStateManager - blockingpicker *pickerWrapper + pickerWrapper *pickerWrapper safeConfigSelector iresolver.SafeConfigSelector - czData *channelzData retryThrottler atomic.Value // Updated from service config. - // firstResolveEvent is used to track whether the name resolver sent us at - // least one update. RPCs block on this event. - firstResolveEvent *grpcsync.Event - // mu protects the following fields. // TODO: split mu so the same mutex isn't used for everything. mu sync.RWMutex - resolverWrapper *ccResolverWrapper // Initialized in Dial; cleared in Close. + resolverWrapper *ccResolverWrapper // Always recreated whenever entering idle to simplify Close. + balancerWrapper *ccBalancerWrapper // Always recreated whenever entering idle to simplify Close. sc *ServiceConfig // Latest service config received from the resolver. conns map[*addrConn]struct{} // Set to nil on close. mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway. - idlenessState ccIdlenessState // Tracks idleness state of the channel. - exitIdleCond *sync.Cond // Signalled when channel exits idle. + // firstResolveEvent is used to track whether the name resolver sent us at + // least one update. RPCs block on this event. May be accessed without mu + // if we know we cannot be asked to enter idle mode while accessing it (e.g. + // when the idle manager has already been closed, or if we are already + // entering idle mode). + firstResolveEvent *grpcsync.Event lceMu sync.Mutex // protects lastConnectionError lastConnectionError error } -// ccIdlenessState tracks the idleness state of the channel. -// -// Channels start off in `active` and move to `idle` after a period of -// inactivity. When moving back to `active` upon an incoming RPC, they -// transition through `exiting_idle`. This state is useful for synchronization -// with Close(). -// -// This state tracking is mostly for self-protection. The idlenessManager is -// expected to keep track of the state as well, and is expected not to call into -// the ClientConn unnecessarily. -type ccIdlenessState int8 - -const ( - ccIdlenessStateActive ccIdlenessState = iota - ccIdlenessStateIdle - ccIdlenessStateExitingIdle -) - // WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or // ctx expires. A true value is returned in former case and false in latter. // @@ -707,29 +655,15 @@ func (cc *ClientConn) GetState() connectivity.State { // Notice: This API is EXPERIMENTAL and may be changed or removed in a later // release. func (cc *ClientConn) Connect() { - cc.exitIdleMode() + if err := cc.idlenessMgr.ExitIdleMode(); err != nil { + cc.addTraceEvent(err.Error()) + return + } // If the ClientConn was not in idle mode, we need to call ExitIdle on the // LB policy so that connections can be created. - cc.balancerWrapper.exitIdleMode() -} - -func (cc *ClientConn) scWatcher() { - for { - select { - case sc, ok := <-cc.dopts.scChan: - if !ok { - return - } - cc.mu.Lock() - // TODO: load balance policy runtime change is ignored. - // We may revisit this decision in the future. - cc.sc = &sc - cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc}) - cc.mu.Unlock() - case <-cc.ctx.Done(): - return - } - } + cc.mu.Lock() + cc.balancerWrapper.exitIdle() + cc.mu.Unlock() } // waitForResolvedAddrs blocks until the resolver has provided addresses or the @@ -754,28 +688,38 @@ func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) error { var emptyServiceConfig *ServiceConfig func init() { + balancer.Register(pickfirstBuilder{}) cfg := parseServiceConfig("{}") if cfg.Err != nil { panic(fmt.Sprintf("impossible error parsing empty service config: %v", cfg.Err)) } emptyServiceConfig = cfg.Config.(*ServiceConfig) + + internal.SubscribeToConnectivityStateChanges = func(cc *ClientConn, s grpcsync.Subscriber) func() { + return cc.csMgr.pubSub.Subscribe(s) + } + internal.EnterIdleModeForTesting = func(cc *ClientConn) { + cc.idlenessMgr.EnterIdleModeForTesting() + } + internal.ExitIdleModeForTesting = func(cc *ClientConn) error { + return cc.idlenessMgr.ExitIdleMode() + } } -func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) { +func (cc *ClientConn) maybeApplyDefaultServiceConfig() { if cc.sc != nil { - cc.applyServiceConfigAndBalancer(cc.sc, nil, addrs) + cc.applyServiceConfigAndBalancer(cc.sc, nil) return } if cc.dopts.defaultServiceConfig != nil { - cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, &defaultConfigSelector{cc.dopts.defaultServiceConfig}, addrs) + cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, &defaultConfigSelector{cc.dopts.defaultServiceConfig}) } else { - cc.applyServiceConfigAndBalancer(emptyServiceConfig, &defaultConfigSelector{emptyServiceConfig}, addrs) + cc.applyServiceConfigAndBalancer(emptyServiceConfig, &defaultConfigSelector{emptyServiceConfig}) } } -func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { +func (cc *ClientConn) updateResolverStateAndUnlock(s resolver.State, err error) error { defer cc.firstResolveEvent.Fire() - cc.mu.Lock() // Check if the ClientConn is already closed. Some fields (e.g. // balancerWrapper) are set to nil when closing the ClientConn, and could // cause nil pointer panic if we don't have this check. @@ -788,7 +732,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { // May need to apply the initial service config in case the resolver // doesn't support service configs, or doesn't provide a service config // with the new addresses. - cc.maybeApplyDefaultServiceConfig(nil) + cc.maybeApplyDefaultServiceConfig() cc.balancerWrapper.resolverError(err) @@ -799,10 +743,10 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { var ret error if cc.dopts.disableServiceConfig { - channelz.Infof(logger, cc.channelzID, "ignoring service config from resolver (%v) and applying the default because service config is disabled", s.ServiceConfig) - cc.maybeApplyDefaultServiceConfig(s.Addresses) + channelz.Infof(logger, cc.channelz, "ignoring service config from resolver (%v) and applying the default because service config is disabled", s.ServiceConfig) + cc.maybeApplyDefaultServiceConfig() } else if s.ServiceConfig == nil { - cc.maybeApplyDefaultServiceConfig(s.Addresses) + cc.maybeApplyDefaultServiceConfig() // TODO: do we need to apply a failing LB policy if there is no // default, per the error handling design? } else { @@ -810,18 +754,18 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { configSelector := iresolver.GetConfigSelector(s) if configSelector != nil { if len(s.ServiceConfig.Config.(*ServiceConfig).Methods) != 0 { - channelz.Infof(logger, cc.channelzID, "method configs in service config will be ignored due to presence of config selector") + channelz.Infof(logger, cc.channelz, "method configs in service config will be ignored due to presence of config selector") } } else { configSelector = &defaultConfigSelector{sc} } - cc.applyServiceConfigAndBalancer(sc, configSelector, s.Addresses) + cc.applyServiceConfigAndBalancer(sc, configSelector) } else { ret = balancer.ErrBadResolverState if cc.sc == nil { // Apply the failing LB only if we haven't received valid service config // from the name resolver in the past. - cc.applyFailingLB(s.ServiceConfig) + cc.applyFailingLBLocked(s.ServiceConfig) cc.mu.Unlock() return ret } @@ -830,7 +774,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { var balCfg serviceconfig.LoadBalancingConfig if cc.sc != nil && cc.sc.lbConfig != nil { - balCfg = cc.sc.lbConfig.cfg + balCfg = cc.sc.lbConfig } bw := cc.balancerWrapper cc.mu.Unlock() @@ -843,15 +787,13 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { return ret } -// applyFailingLB is akin to configuring an LB policy on the channel which +// applyFailingLBLocked is akin to configuring an LB policy on the channel which // always fails RPCs. Here, an actual LB policy is not configured, but an always // erroring picker is configured, which returns errors with information about // what was invalid in the received service config. A config selector with no // service config is configured, and the connectivity state of the channel is // set to TransientFailure. -// -// Caller must hold cc.mu. -func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) { +func (cc *ClientConn) applyFailingLBLocked(sc *serviceconfig.ParseResult) { var err error if sc.Err != nil { err = status.Errorf(codes.Unavailable, "error parsing service config: %v", sc.Err) @@ -859,50 +801,54 @@ func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) { err = status.Errorf(codes.Unavailable, "illegal service config type: %T", sc.Config) } cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) - cc.blockingpicker.updatePicker(base.NewErrPicker(err)) + cc.pickerWrapper.updatePicker(base.NewErrPicker(err)) cc.csMgr.updateState(connectivity.TransientFailure) } -func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) { - cc.balancerWrapper.updateSubConnState(sc, s, err) +// Makes a copy of the input addresses slice and clears out the balancer +// attributes field. Addresses are passed during subconn creation and address +// update operations. In both cases, we will clear the balancer attributes by +// calling this function, and therefore we will be able to use the Equal method +// provided by the resolver.Address type for comparison. +func copyAddressesWithoutBalancerAttributes(in []resolver.Address) []resolver.Address { + out := make([]resolver.Address, len(in)) + for i := range in { + out[i] = in[i] + out[i].BalancerAttributes = nil + } + return out } -// newAddrConn creates an addrConn for addrs and adds it to cc.conns. +// newAddrConnLocked creates an addrConn for addrs and adds it to cc.conns. // // Caller needs to make sure len(addrs) > 0. -func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) { +func (cc *ClientConn) newAddrConnLocked(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) { + if cc.conns == nil { + return nil, ErrClientConnClosing + } + ac := &addrConn{ state: connectivity.Idle, cc: cc, - addrs: addrs, + addrs: copyAddressesWithoutBalancerAttributes(addrs), scopts: opts, dopts: cc.dopts, - czData: new(channelzData), + channelz: channelz.RegisterSubChannel(cc.channelz, ""), resetBackoff: make(chan struct{}), stateChan: make(chan struct{}), } ac.ctx, ac.cancel = context.WithCancel(cc.ctx) - // Track ac in cc. This needs to be done before any getTransport(...) is called. - cc.mu.Lock() - defer cc.mu.Unlock() - if cc.conns == nil { - return nil, ErrClientConnClosing - } - var err error - ac.channelzID, err = channelz.RegisterSubChannel(ac, cc.channelzID, "") - if err != nil { - return nil, err - } - channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ + channelz.AddTraceEvent(logger, ac.channelz, 0, &channelz.TraceEvent{ Desc: "Subchannel created", Severity: channelz.CtInfo, - Parent: &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelzID.Int()), + Parent: &channelz.TraceEvent{ + Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelz.ID), Severity: channelz.CtInfo, }, }) + // Track ac in cc. This needs to be done before any getTransport(...) is called. cc.conns[ac] = struct{}{} return ac, nil } @@ -920,38 +866,27 @@ func (cc *ClientConn) removeAddrConn(ac *addrConn, err error) { ac.tearDown(err) } -func (cc *ClientConn) channelzMetric() *channelz.ChannelInternalMetric { - return &channelz.ChannelInternalMetric{ - State: cc.GetState(), - Target: cc.target, - CallsStarted: atomic.LoadInt64(&cc.czData.callsStarted), - CallsSucceeded: atomic.LoadInt64(&cc.czData.callsSucceeded), - CallsFailed: atomic.LoadInt64(&cc.czData.callsFailed), - LastCallStartedTimestamp: time.Unix(0, atomic.LoadInt64(&cc.czData.lastCallStartedTime)), - } -} - // Target returns the target string of the ClientConn. -// -// # Experimental -// -// Notice: This API is EXPERIMENTAL and may be changed or removed in a -// later release. func (cc *ClientConn) Target() string { return cc.target } +// CanonicalTarget returns the canonical target string of the ClientConn. +func (cc *ClientConn) CanonicalTarget() string { + return cc.parsedTarget.String() +} + func (cc *ClientConn) incrCallsStarted() { - atomic.AddInt64(&cc.czData.callsStarted, 1) - atomic.StoreInt64(&cc.czData.lastCallStartedTime, time.Now().UnixNano()) + cc.channelz.ChannelMetrics.CallsStarted.Add(1) + cc.channelz.ChannelMetrics.LastCallStartedTimestamp.Store(time.Now().UnixNano()) } func (cc *ClientConn) incrCallsSucceeded() { - atomic.AddInt64(&cc.czData.callsSucceeded, 1) + cc.channelz.ChannelMetrics.CallsSucceeded.Add(1) } func (cc *ClientConn) incrCallsFailed() { - atomic.AddInt64(&cc.czData.callsFailed, 1) + cc.channelz.ChannelMetrics.CallsFailed.Add(1) } // connect starts creating a transport. @@ -995,8 +930,9 @@ func equalAddresses(a, b []resolver.Address) bool { // connections or connection attempts. func (ac *addrConn) updateAddrs(addrs []resolver.Address) { ac.mu.Lock() - channelz.Infof(logger, ac.channelzID, "addrConn: updateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs) + channelz.Infof(logger, ac.channelz, "addrConn: updateAddrs curAddr: %v, addrs: %v", pretty.ToJSON(ac.curAddr), pretty.ToJSON(addrs)) + addrs = copyAddressesWithoutBalancerAttributes(addrs) if equalAddresses(ac.addrs, addrs) { ac.mu.Unlock() return @@ -1031,8 +967,8 @@ func (ac *addrConn) updateAddrs(addrs []resolver.Address) { ac.cancel() ac.ctx, ac.cancel = context.WithCancel(ac.cc.ctx) - // We have to defer here because GracefulClose => Close => onClose, which - // requires locking ac.mu. + // We have to defer here because GracefulClose => onClose, which requires + // locking ac.mu. if ac.transport != nil { defer ac.transport.GracefulClose() ac.transport = nil @@ -1108,13 +1044,13 @@ func (cc *ClientConn) healthCheckConfig() *healthCheckConfig { } func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, balancer.PickResult, error) { - return cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{ + return cc.pickerWrapper.pick(ctx, failfast, balancer.PickInfo{ Ctx: ctx, FullMethodName: method, }) } -func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector, addrs []resolver.Address) { +func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector) { if sc == nil { // should never reach here. return @@ -1135,37 +1071,16 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSel } else { cc.retryThrottler.Store((*retryThrottler)(nil)) } - - var newBalancerName string - if cc.sc != nil && cc.sc.lbConfig != nil { - newBalancerName = cc.sc.lbConfig.name - } else { - var isGRPCLB bool - for _, a := range addrs { - if a.Type == resolver.GRPCLB { - isGRPCLB = true - break - } - } - if isGRPCLB { - newBalancerName = grpclbName - } else if cc.sc != nil && cc.sc.LB != nil { - newBalancerName = *cc.sc.LB - } else { - newBalancerName = PickFirstBalancerName - } - } - cc.balancerWrapper.switchTo(newBalancerName) } func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) { cc.mu.RLock() - r := cc.resolverWrapper + cc.resolverWrapper.resolveNow(o) cc.mu.RUnlock() - if r == nil { - return - } - go r.resolveNow(o) +} + +func (cc *ClientConn) resolveNowLocked(o resolver.ResolveNowOptions) { + cc.resolverWrapper.resolveNow(o) } // ResetConnectBackoff wakes up all subchannels in transient failure and causes @@ -1192,7 +1107,14 @@ func (cc *ClientConn) ResetConnectBackoff() { // Close tears down the ClientConn and all underlying connections. func (cc *ClientConn) Close() error { - defer cc.cancel() + defer func() { + cc.cancel() + <-cc.csMgr.pubSub.Done() + }() + + // Prevent calls to enter/exit idle immediately, and ensure we are not + // currently entering/exiting idle mode. + cc.idlenessMgr.Close() cc.mu.Lock() if cc.conns == nil { @@ -1200,34 +1122,22 @@ func (cc *ClientConn) Close() error { return ErrClientConnClosing } - for cc.idlenessState == ccIdlenessStateExitingIdle { - cc.exitIdleCond.Wait() - } - conns := cc.conns cc.conns = nil cc.csMgr.updateState(connectivity.Shutdown) - pWrapper := cc.blockingpicker - rWrapper := cc.resolverWrapper - bWrapper := cc.balancerWrapper - idlenessMgr := cc.idlenessMgr + // We can safely unlock and continue to access all fields now as + // cc.conns==nil, preventing any further operations on cc. cc.mu.Unlock() + cc.resolverWrapper.close() // The order of closing matters here since the balancer wrapper assumes the // picker is closed before it is closed. - if pWrapper != nil { - pWrapper.close() - } - if bWrapper != nil { - bWrapper.close() - } - if rWrapper != nil { - rWrapper.close() - } - if idlenessMgr != nil { - idlenessMgr.close() - } + cc.pickerWrapper.close() + cc.balancerWrapper.close() + + <-cc.resolverWrapper.serializer.Done() + <-cc.balancerWrapper.serializer.Done() for ac := range conns { ac.tearDown(ErrClientConnClosing) @@ -1236,7 +1146,7 @@ func (cc *ClientConn) Close() error { // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add // trace reference to the entity being deleted, and thus prevent it from being // deleted right away. - channelz.RemoveEntry(cc.channelzID) + channelz.RemoveEntry(cc.channelz.ID) return nil } @@ -1248,7 +1158,7 @@ type addrConn struct { cc *ClientConn dopts dialOptions - acbw balancer.SubConn + acbw *acBalancerWrapper scopts balancer.NewSubConnOptions // transport is set when there's a viable transport (note: ac state may not be READY as LB channel @@ -1268,8 +1178,7 @@ type addrConn struct { backoffIdx int // Needs to be stateful for resetConnectBackoff. resetBackoff chan struct{} - channelzID *channelz.Identifier - czData *channelzData + channelz *channelz.SubChannel } // Note: this requires a lock on ac.mu. @@ -1281,12 +1190,13 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) close(ac.stateChan) ac.stateChan = make(chan struct{}) ac.state = s + ac.channelz.ChannelMetrics.State.Store(&s) if lastErr == nil { - channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v", s) + channelz.Infof(logger, ac.channelz, "Subchannel Connectivity change to %v", s) } else { - channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v, last error: %s", s, lastErr) + channelz.Infof(logger, ac.channelz, "Subchannel Connectivity change to %v, last error: %s", s, lastErr) } - ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr) + ac.acbw.updateState(s, lastErr) } // adjustParams updates parameters used to create transports upon @@ -1336,12 +1246,14 @@ func (ac *addrConn) resetTransport() { if err := ac.tryAllAddrs(acCtx, addrs, connectDeadline); err != nil { ac.cc.resolveNow(resolver.ResolveNowOptions{}) - // After exhausting all addresses, the addrConn enters - // TRANSIENT_FAILURE. + ac.mu.Lock() if acCtx.Err() != nil { + // addrConn was torn down. + ac.mu.Unlock() return } - ac.mu.Lock() + // After exhausting all addresses, the addrConn enters + // TRANSIENT_FAILURE. ac.updateConnectivityState(connectivity.TransientFailure, err) // Backoff. @@ -1395,7 +1307,7 @@ func (ac *addrConn) tryAllAddrs(ctx context.Context, addrs []resolver.Address, c } ac.mu.Unlock() - channelz.Infof(logger, ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr) + channelz.Infof(logger, ac.channelz, "Subchannel picks a new address %q to connect", addr.Addr) err := ac.createTransport(ctx, addr, copts, connectDeadline) if err == nil { @@ -1448,7 +1360,7 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, connectCtx, cancel := context.WithDeadline(ctx, connectDeadline) defer cancel() - copts.ChannelzParentID = ac.channelzID + copts.ChannelzParent = ac.channelz newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onClose) if err != nil { @@ -1457,7 +1369,7 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, } // newTr is either nil, or closed. hcancel() - channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err) + channelz.Warningf(logger, ac.channelz, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err) return err } @@ -1529,7 +1441,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) { // The health package is not imported to set health check function. // // TODO: add a link to the health check doc in the error message. - channelz.Error(logger, ac.channelzID, "Health check is requested but health check function is not set.") + channelz.Error(logger, ac.channelz, "Health check is requested but health check function is not set.") return } @@ -1537,7 +1449,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) { // Set up the health check helper functions. currentTr := ac.transport - newStream := func(method string) (interface{}, error) { + newStream := func(method string) (any, error) { ac.mu.Lock() if ac.transport != currentTr { ac.mu.Unlock() @@ -1559,9 +1471,9 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) { err := ac.cc.dopts.healthCheckFunc(ctx, newStream, setConnectivityState, healthCheckConfig.ServiceName) if err != nil { if status.Code(err) == codes.Unimplemented { - channelz.Error(logger, ac.channelzID, "Subchannel health check is unimplemented at server side, thus health check is disabled") + channelz.Error(logger, ac.channelz, "Subchannel health check is unimplemented at server side, thus health check is disabled") } else { - channelz.Errorf(logger, ac.channelzID, "Health checking failed: %v", err) + channelz.Errorf(logger, ac.channelz, "Health checking failed: %v", err) } } }() @@ -1625,64 +1537,45 @@ func (ac *addrConn) tearDown(err error) { ac.updateConnectivityState(connectivity.Shutdown, nil) ac.cancel() ac.curAddr = resolver.Address{} - if err == errConnDrain && curTr != nil { - // GracefulClose(...) may be executed multiple times when - // i) receiving multiple GoAway frames from the server; or - // ii) there are concurrent name resolver/Balancer triggered - // address removal and GoAway. - // We have to unlock and re-lock here because GracefulClose => Close => onClose, which requires locking ac.mu. - ac.mu.Unlock() - curTr.GracefulClose() - ac.mu.Lock() - } - channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ + + channelz.AddTraceEvent(logger, ac.channelz, 0, &channelz.TraceEvent{ Desc: "Subchannel deleted", Severity: channelz.CtInfo, - Parent: &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Subchannel(id:%d) deleted", ac.channelzID.Int()), + Parent: &channelz.TraceEvent{ + Desc: fmt.Sprintf("Subchannel(id:%d) deleted", ac.channelz.ID), Severity: channelz.CtInfo, }, }) // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add // trace reference to the entity being deleted, and thus prevent it from // being deleted right away. - channelz.RemoveEntry(ac.channelzID) + channelz.RemoveEntry(ac.channelz.ID) ac.mu.Unlock() -} -func (ac *addrConn) getState() connectivity.State { - ac.mu.Lock() - defer ac.mu.Unlock() - return ac.state -} - -func (ac *addrConn) ChannelzMetric() *channelz.ChannelInternalMetric { - ac.mu.Lock() - addr := ac.curAddr.Addr - ac.mu.Unlock() - return &channelz.ChannelInternalMetric{ - State: ac.getState(), - Target: addr, - CallsStarted: atomic.LoadInt64(&ac.czData.callsStarted), - CallsSucceeded: atomic.LoadInt64(&ac.czData.callsSucceeded), - CallsFailed: atomic.LoadInt64(&ac.czData.callsFailed), - LastCallStartedTimestamp: time.Unix(0, atomic.LoadInt64(&ac.czData.lastCallStartedTime)), + // We have to release the lock before the call to GracefulClose/Close here + // because both of them call onClose(), which requires locking ac.mu. + if curTr != nil { + if err == errConnDrain { + // Close the transport gracefully when the subConn is being shutdown. + // + // GracefulClose() may be executed multiple times if: + // - multiple GoAway frames are received from the server + // - there are concurrent name resolver or balancer triggered + // address removal and GoAway + curTr.GracefulClose() + } else { + // Hard close the transport when the channel is entering idle or is + // being shutdown. In the case where the channel is being shutdown, + // closing of transports is also taken care of by cancelation of cc.ctx. + // But in the case where the channel is entering idle, we need to + // explicitly close the transports here. Instead of distinguishing + // between these two cases, it is simpler to close the transport + // unconditionally here. + curTr.Close(err) + } } } -func (ac *addrConn) incrCallsStarted() { - atomic.AddInt64(&ac.czData.callsStarted, 1) - atomic.StoreInt64(&ac.czData.lastCallStartedTime, time.Now().UnixNano()) -} - -func (ac *addrConn) incrCallsSucceeded() { - atomic.AddInt64(&ac.czData.callsSucceeded, 1) -} - -func (ac *addrConn) incrCallsFailed() { - atomic.AddInt64(&ac.czData.callsFailed, 1) -} - type retryThrottler struct { max float64 thresh float64 @@ -1720,12 +1613,17 @@ func (rt *retryThrottler) successfulRPC() { } } -type channelzChannel struct { - cc *ClientConn +func (ac *addrConn) incrCallsStarted() { + ac.channelz.ChannelMetrics.CallsStarted.Add(1) + ac.channelz.ChannelMetrics.LastCallStartedTimestamp.Store(time.Now().UnixNano()) +} + +func (ac *addrConn) incrCallsSucceeded() { + ac.channelz.ChannelMetrics.CallsSucceeded.Add(1) } -func (c *channelzChannel) ChannelzMetric() *channelz.ChannelInternalMetric { - return c.cc.channelzMetric() +func (ac *addrConn) incrCallsFailed() { + ac.channelz.ChannelMetrics.CallsFailed.Add(1) } // ErrClientConnTimeout indicates that the ClientConn cannot establish the @@ -1767,14 +1665,14 @@ func (cc *ClientConn) connectionError() error { // // Doesn't grab cc.mu as this method is expected to be called only at Dial time. func (cc *ClientConn) parseTargetAndFindResolver() error { - channelz.Infof(logger, cc.channelzID, "original dial target is: %q", cc.target) + channelz.Infof(logger, cc.channelz, "original dial target is: %q", cc.target) var rb resolver.Builder parsedTarget, err := parseTarget(cc.target) if err != nil { - channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", cc.target, err) + channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", cc.target, err) } else { - channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget) + channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", parsedTarget) rb = cc.getResolver(parsedTarget.URL.Scheme) if rb != nil { cc.parsedTarget = parsedTarget @@ -1786,17 +1684,22 @@ func (cc *ClientConn) parseTargetAndFindResolver() error { // We are here because the user's dial target did not contain a scheme or // specified an unregistered scheme. We should fallback to the default // scheme, except when a custom dialer is specified in which case, we should - // always use passthrough scheme. - defScheme := resolver.GetDefaultScheme() - channelz.Infof(logger, cc.channelzID, "fallback to scheme %q", defScheme) + // always use passthrough scheme. For either case, we need to respect any overridden + // global defaults set by the user. + defScheme := cc.dopts.defaultScheme + if internal.UserSetDefaultScheme { + defScheme = resolver.GetDefaultScheme() + } + + channelz.Infof(logger, cc.channelz, "fallback to scheme %q", defScheme) canonicalTarget := defScheme + ":///" + cc.target parsedTarget, err = parseTarget(canonicalTarget) if err != nil { - channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", canonicalTarget, err) + channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", canonicalTarget, err) return err } - channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget) + channelz.Infof(logger, cc.channelz, "parsed dial target is: %+v", parsedTarget) rb = cc.getResolver(parsedTarget.URL.Scheme) if rb == nil { return fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme) @@ -1807,19 +1710,72 @@ func (cc *ClientConn) parseTargetAndFindResolver() error { } // parseTarget uses RFC 3986 semantics to parse the given target into a -// resolver.Target struct containing scheme, authority and url. Query -// params are stripped from the endpoint. +// resolver.Target struct containing url. Query params are stripped from the +// endpoint. func parseTarget(target string) (resolver.Target, error) { u, err := url.Parse(target) if err != nil { return resolver.Target{}, err } - return resolver.Target{ - Scheme: u.Scheme, - Authority: u.Host, - URL: *u, - }, nil + return resolver.Target{URL: *u}, nil +} + +// encodeAuthority escapes the authority string based on valid chars defined in +// https://datatracker.ietf.org/doc/html/rfc3986#section-3.2. +func encodeAuthority(authority string) string { + const upperhex = "0123456789ABCDEF" + + // Return for characters that must be escaped as per + // Valid chars are mentioned here: + // https://datatracker.ietf.org/doc/html/rfc3986#section-3.2 + shouldEscape := func(c byte) bool { + // Alphanum are always allowed. + if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { + return false + } + switch c { + case '-', '_', '.', '~': // Unreserved characters + return false + case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // Subdelim characters + return false + case ':', '[', ']', '@': // Authority related delimeters + return false + } + // Everything else must be escaped. + return true + } + + hexCount := 0 + for i := 0; i < len(authority); i++ { + c := authority[i] + if shouldEscape(c) { + hexCount++ + } + } + + if hexCount == 0 { + return authority + } + + required := len(authority) + 2*hexCount + t := make([]byte, required) + + j := 0 + // This logic is a barebones version of escape in the go net/url library. + for i := 0; i < len(authority); i++ { + switch c := authority[i]; { + case shouldEscape(c): + t[j] = '%' + t[j+1] = upperhex[c>>4] + t[j+2] = upperhex[c&15] + j += 3 + default: + t[j] = authority[i] + j++ + } + } + return string(t) } // Determine channel authority. The order of precedence is as follows: @@ -1855,54 +1811,17 @@ func (cc *ClientConn) determineAuthority() error { } endpoint := cc.parsedTarget.Endpoint() - target := cc.target - switch { - case authorityFromDialOption != "": + if authorityFromDialOption != "" { cc.authority = authorityFromDialOption - case authorityFromCreds != "": + } else if authorityFromCreds != "" { cc.authority = authorityFromCreds - case strings.HasPrefix(target, "unix:") || strings.HasPrefix(target, "unix-abstract:"): - // TODO: remove when the unix resolver implements optional interface to - // return channel authority. - cc.authority = "localhost" - case strings.HasPrefix(endpoint, ":"): + } else if auth, ok := cc.resolverBuilder.(resolver.AuthorityOverrider); ok { + cc.authority = auth.OverrideAuthority(cc.parsedTarget) + } else if strings.HasPrefix(endpoint, ":") { cc.authority = "localhost" + endpoint - default: - // TODO: Define an optional interface on the resolver builder to return - // the channel authority given the user's dial target. For resolvers - // which don't implement this interface, we will use the endpoint from - // "scheme://authority/endpoint" as the default authority. - cc.authority = endpoint - } - channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority) - return nil -} - -// initResolverWrapper creates a ccResolverWrapper, which builds the name -// resolver. This method grabs the lock to assign the newly built resolver -// wrapper to the cc.resolverWrapper field. -func (cc *ClientConn) initResolverWrapper(creds credentials.TransportCredentials) error { - rw, err := newCCResolverWrapper(cc, ccResolverWrapperOpts{ - target: cc.parsedTarget, - builder: cc.resolverBuilder, - bOpts: resolver.BuildOptions{ - DisableServiceConfig: cc.dopts.disableServiceConfig, - DialCreds: creds, - CredsBundle: cc.dopts.copts.CredsBundle, - Dialer: cc.dopts.copts.Dialer, - }, - channelzID: cc.channelzID, - }) - if err != nil { - return fmt.Errorf("failed to build resolver: %v", err) + } else { + cc.authority = encodeAuthority(endpoint) } - // Resolver implementations may report state update or error inline when - // built (or right after), and this is handled in cc.updateResolverState. - // Also, an error from the resolver might lead to a re-resolution request - // from the balancer, which is handled in resolveNow() where - // `cc.resolverWrapper` is accessed. Hence, we need to hold the lock here. - cc.mu.Lock() - cc.resolverWrapper = rw - cc.mu.Unlock() + channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority) return nil } diff --git a/vendor/google.golang.org/grpc/codec.go b/vendor/google.golang.org/grpc/codec.go index 129776547..411e3dfd4 100644 --- a/vendor/google.golang.org/grpc/codec.go +++ b/vendor/google.golang.org/grpc/codec.go @@ -27,8 +27,8 @@ import ( // omits the name/string, which vary between the two and are not needed for // anything besides the registry in the encoding package. type baseCodec interface { - Marshal(v interface{}) ([]byte, error) - Unmarshal(data []byte, v interface{}) error + Marshal(v any) ([]byte, error) + Unmarshal(data []byte, v any) error } var _ baseCodec = Codec(nil) @@ -41,9 +41,9 @@ var _ baseCodec = encoding.Codec(nil) // Deprecated: use encoding.Codec instead. type Codec interface { // Marshal returns the wire format of v. - Marshal(v interface{}) ([]byte, error) + Marshal(v any) ([]byte, error) // Unmarshal parses the wire format into v. - Unmarshal(data []byte, v interface{}) error + Unmarshal(data []byte, v any) error // String returns the name of the Codec implementation. This is unused by // gRPC. String() string diff --git a/vendor/google.golang.org/grpc/codes/codes.go b/vendor/google.golang.org/grpc/codes/codes.go index 11b106182..08476ad1f 100644 --- a/vendor/google.golang.org/grpc/codes/codes.go +++ b/vendor/google.golang.org/grpc/codes/codes.go @@ -25,7 +25,13 @@ import ( "strconv" ) -// A Code is an unsigned 32-bit error code as defined in the gRPC spec. +// A Code is a status code defined according to the [gRPC documentation]. +// +// Only the codes defined as consts in this package are valid codes. Do not use +// other code values. Behavior of other codes is implementation-specific and +// interoperability between implementations is not guaranteed. +// +// [gRPC documentation]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md type Code uint32 const ( diff --git a/vendor/google.golang.org/grpc/credentials/credentials.go b/vendor/google.golang.org/grpc/credentials/credentials.go index 5feac3aa0..f6b55c68b 100644 --- a/vendor/google.golang.org/grpc/credentials/credentials.go +++ b/vendor/google.golang.org/grpc/credentials/credentials.go @@ -28,9 +28,9 @@ import ( "fmt" "net" - "github.com/golang/protobuf/proto" "google.golang.org/grpc/attributes" icredentials "google.golang.org/grpc/internal/credentials" + "google.golang.org/protobuf/protoadapt" ) // PerRPCCredentials defines the common interface for the credentials which need to @@ -287,5 +287,5 @@ type ChannelzSecurityValue interface { type OtherChannelzSecurityValue struct { ChannelzSecurityValue Name string - Value proto.Message + Value protoadapt.MessageV1 } diff --git a/vendor/google.golang.org/grpc/credentials/tls.go b/vendor/google.golang.org/grpc/credentials/tls.go index 877b7cd21..5dafd34ed 100644 --- a/vendor/google.golang.org/grpc/credentials/tls.go +++ b/vendor/google.golang.org/grpc/credentials/tls.go @@ -44,10 +44,25 @@ func (t TLSInfo) AuthType() string { return "tls" } +// cipherSuiteLookup returns the string version of a TLS cipher suite ID. +func cipherSuiteLookup(cipherSuiteID uint16) string { + for _, s := range tls.CipherSuites() { + if s.ID == cipherSuiteID { + return s.Name + } + } + for _, s := range tls.InsecureCipherSuites() { + if s.ID == cipherSuiteID { + return s.Name + } + } + return fmt.Sprintf("unknown ID: %v", cipherSuiteID) +} + // GetSecurityValue returns security info requested by channelz. func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue { v := &TLSChannelzSecurityValue{ - StandardName: cipherSuiteLookup[t.State.CipherSuite], + StandardName: cipherSuiteLookup(t.State.CipherSuite), } // Currently there's no way to get LocalCertificate info from tls package. if len(t.State.PeerCertificates) > 0 { @@ -138,10 +153,39 @@ func (c *tlsCreds) OverrideServerName(serverNameOverride string) error { return nil } +// The following cipher suites are forbidden for use with HTTP/2 by +// https://datatracker.ietf.org/doc/html/rfc7540#appendix-A +var tls12ForbiddenCipherSuites = map[uint16]struct{}{ + tls.TLS_RSA_WITH_AES_128_CBC_SHA: {}, + tls.TLS_RSA_WITH_AES_256_CBC_SHA: {}, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256: {}, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384: {}, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {}, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {}, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {}, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: {}, +} + // NewTLS uses c to construct a TransportCredentials based on TLS. func NewTLS(c *tls.Config) TransportCredentials { tc := &tlsCreds{credinternal.CloneTLSConfig(c)} tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos) + // If the user did not configure a MinVersion and did not configure a + // MaxVersion < 1.2, use MinVersion=1.2, which is required by + // https://datatracker.ietf.org/doc/html/rfc7540#section-9.2 + if tc.config.MinVersion == 0 && (tc.config.MaxVersion == 0 || tc.config.MaxVersion >= tls.VersionTLS12) { + tc.config.MinVersion = tls.VersionTLS12 + } + // If the user did not configure CipherSuites, use all "secure" cipher + // suites reported by the TLS package, but remove some explicitly forbidden + // by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A + if tc.config.CipherSuites == nil { + for _, cs := range tls.CipherSuites() { + if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok { + tc.config.CipherSuites = append(tc.config.CipherSuites, cs.ID) + } + } + } return tc } @@ -205,32 +249,3 @@ type TLSChannelzSecurityValue struct { LocalCertificate []byte RemoteCertificate []byte } - -var cipherSuiteLookup = map[uint16]string{ - tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA", - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", - tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA", - tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA", - tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256", - tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384", - tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA", - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV", - tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", - tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256", - tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384", - tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", -} diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index 15a3d5102..402493224 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -46,6 +46,7 @@ func init() { internal.WithBinaryLogger = withBinaryLogger internal.JoinDialOptions = newJoinDialOption internal.DisableGlobalDialOptions = newDisableGlobalDialOptions + internal.WithRecvBufferPool = withRecvBufferPool } // dialOptions configure a Dial call. dialOptions are set by the DialOption @@ -63,12 +64,11 @@ type dialOptions struct { block bool returnLastError bool timeout time.Duration - scChan <-chan ServiceConfig authority string binaryLogger binarylog.Logger copts transport.ConnectOptions callOptions []CallOption - channelzParentID *channelz.Identifier + channelzParent channelz.Identifier disableServiceConfig bool disableRetry bool disableHealthCheck bool @@ -78,6 +78,8 @@ type dialOptions struct { defaultServiceConfigRawJSON *string resolvers []resolver.Builder idleTimeout time.Duration + recvBufferPool SharedBufferPool + defaultScheme string } // DialOption configures how we set up the connection. @@ -138,10 +140,22 @@ func newJoinDialOption(opts ...DialOption) DialOption { return &joinDialOption{opts: opts} } +// WithSharedWriteBuffer allows reusing per-connection transport write buffer. +// If this option is set to true every connection will release the buffer after +// flushing the data on the wire. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func WithSharedWriteBuffer(val bool) DialOption { + return newFuncDialOption(func(o *dialOptions) { + o.copts.SharedWriteBuffer = val + }) +} + // WithWriteBufferSize determines how much data can be batched before doing a -// write on the wire. The corresponding memory allocation for this buffer will -// be twice the size to keep syscalls low. The default value for this buffer is -// 32KB. +// write on the wire. The default value for this buffer is 32KB. // // Zero or negative values will disable the write buffer such that each write // will be on underlying connection. Note: A Send call may not directly @@ -235,19 +249,6 @@ func WithDecompressor(dc Decompressor) DialOption { }) } -// WithServiceConfig returns a DialOption which has a channel to read the -// service configuration. -// -// Deprecated: service config should be received through name resolver or via -// WithDefaultServiceConfig, as specified at -// https://github.com/grpc/grpc/blob/master/doc/service_config.md. Will be -// removed in a future 1.x release. -func WithServiceConfig(c <-chan ServiceConfig) DialOption { - return newFuncDialOption(func(o *dialOptions) { - o.scChan = c - }) -} - // WithConnectParams configures the ClientConn to use the provided ConnectParams // for creating and maintaining connections to servers. // @@ -398,6 +399,17 @@ func WithTimeout(d time.Duration) DialOption { // connections. If FailOnNonTempDialError() is set to true, and an error is // returned by f, gRPC checks the error's Temporary() method to decide if it // should try to reconnect to the network address. +// +// Note: All supported releases of Go (as of December 2023) override the OS +// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive +// with OS defaults for keepalive time and interval, use a net.Dialer that sets +// the KeepAlive field to a negative value, and sets the SO_KEEPALIVE socket +// option to true from the Control field. For a concrete example of how to do +// this, see internal.NetDialerWithTCPKeepalive(). +// +// For more information, please see [issue 23459] in the Go github repo. +// +// [issue 23459]: https://github.com/golang/go/issues/23459 func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption { return newFuncDialOption(func(o *dialOptions) { o.copts.Dialer = f @@ -472,7 +484,7 @@ func FailOnNonTempDialError(f bool) DialOption { // the RPCs. func WithUserAgent(s string) DialOption { return newFuncDialOption(func(o *dialOptions) { - o.copts.UserAgent = s + o.copts.UserAgent = s + " " + grpcUA }) } @@ -542,9 +554,9 @@ func WithAuthority(a string) DialOption { // // Notice: This API is EXPERIMENTAL and may be changed or removed in a // later release. -func WithChannelzParentID(id *channelz.Identifier) DialOption { +func WithChannelzParentID(c channelz.Identifier) DialOption { return newFuncDialOption(func(o *dialOptions) { - o.channelzParentID = id + o.channelzParent = c }) } @@ -622,12 +634,17 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption { func defaultDialOptions() dialOptions { return dialOptions{ - healthCheckFunc: internal.HealthCheckFunc, copts: transport.ConnectOptions{ - WriteBufferSize: defaultWriteBufSize, ReadBufferSize: defaultReadBufSize, + WriteBufferSize: defaultWriteBufSize, UseProxy: true, + UserAgent: grpcUA, }, + bs: internalbackoff.DefaultExponential, + healthCheckFunc: internal.HealthCheckFunc, + idleTimeout: 30 * time.Minute, + recvBufferPool: nopBufferPool{}, + defaultScheme: "dns", } } @@ -642,6 +659,14 @@ func withMinConnectDeadline(f func() time.Duration) DialOption { }) } +// withDefaultScheme is used to allow Dial to use "passthrough" as the default +// name resolver, while NewClient uses "dns" otherwise. +func withDefaultScheme(s string) DialOption { + return newFuncDialOption(func(o *dialOptions) { + o.defaultScheme = s + }) +} + // WithResolvers allows a list of resolver implementations to be registered // locally with the ClientConn without needing to be globally registered via // resolver.Register. They will be matched against the scheme used for the @@ -664,8 +689,8 @@ func WithResolvers(rs ...resolver.Builder) DialOption { // channel will exit idle mode when the Connect() method is called or when an // RPC is initiated. // -// By default this feature is disabled, which can also be explicitly configured -// by passing zero to this function. +// A default timeout of 30 minutes will be used if this dial option is not set +// at dial time and idleness can be disabled by passing a timeout of zero. // // # Experimental // @@ -676,3 +701,26 @@ func WithIdleTimeout(d time.Duration) DialOption { o.idleTimeout = d }) } + +// WithRecvBufferPool returns a DialOption that configures the ClientConn +// to use the provided shared buffer pool for parsing incoming messages. Depending +// on the application's workload, this could result in reduced memory allocation. +// +// If you are unsure about how to implement a memory pool but want to utilize one, +// begin with grpc.NewSharedBufferPool. +// +// Note: The shared buffer pool feature will not be active if any of the following +// options are used: WithStatsHandler, EnableTracing, or binary logging. In such +// cases, the shared buffer pool will be ignored. +// +// Deprecated: use experimental.WithRecvBufferPool instead. Will be deleted in +// v1.60.0 or later. +func WithRecvBufferPool(bufferPool SharedBufferPool) DialOption { + return withRecvBufferPool(bufferPool) +} + +func withRecvBufferPool(bufferPool SharedBufferPool) DialOption { + return newFuncDialOption(func(o *dialOptions) { + o.recvBufferPool = bufferPool + }) +} diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go index 07a586135..5ebf88d71 100644 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/vendor/google.golang.org/grpc/encoding/encoding.go @@ -38,6 +38,10 @@ const Identity = "identity" // Compressor is used for compressing and decompressing when sending or // receiving messages. +// +// If a Compressor implements `DecompressedSize(compressedBytes []byte) int`, +// gRPC will invoke it to determine the size of the buffer allocated for the +// result of decompression. A return value of -1 indicates unknown size. type Compressor interface { // Compress writes the data written to wc to w after compressing it. If an // error occurs while initializing the compressor, that error is returned @@ -51,15 +55,6 @@ type Compressor interface { // coding header. The result must be static; the result cannot change // between calls. Name() string - // If a Compressor implements - // DecompressedSize(compressedBytes []byte) int, gRPC will call it - // to determine the size of the buffer allocated for the result of decompression. - // Return -1 to indicate unknown size. - // - // Experimental - // - // Notice: This API is EXPERIMENTAL and may be changed or removed in a - // later release. } var registeredCompressor = make(map[string]Compressor) @@ -90,9 +85,9 @@ func GetCompressor(name string) Compressor { // methods can be called from concurrent goroutines. type Codec interface { // Marshal returns the wire format of v. - Marshal(v interface{}) ([]byte, error) + Marshal(v any) ([]byte, error) // Unmarshal parses the wire format into v. - Unmarshal(data []byte, v interface{}) error + Unmarshal(data []byte, v any) error // Name returns the name of the Codec implementation. The returned string // will be used as part of content type in transmission. The result must be // static; the result cannot change between calls. diff --git a/vendor/google.golang.org/grpc/encoding/proto/proto.go b/vendor/google.golang.org/grpc/encoding/proto/proto.go index 3009b35af..66d5cdf03 100644 --- a/vendor/google.golang.org/grpc/encoding/proto/proto.go +++ b/vendor/google.golang.org/grpc/encoding/proto/proto.go @@ -23,8 +23,9 @@ package proto import ( "fmt" - "github.com/golang/protobuf/proto" "google.golang.org/grpc/encoding" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/protoadapt" ) // Name is the name registered for the proto compressor. @@ -37,22 +38,35 @@ func init() { // codec is a Codec implementation with protobuf. It is the default codec for gRPC. type codec struct{} -func (codec) Marshal(v interface{}) ([]byte, error) { - vv, ok := v.(proto.Message) - if !ok { +func (codec) Marshal(v any) ([]byte, error) { + vv := messageV2Of(v) + if vv == nil { return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v) } + return proto.Marshal(vv) } -func (codec) Unmarshal(data []byte, v interface{}) error { - vv, ok := v.(proto.Message) - if !ok { +func (codec) Unmarshal(data []byte, v any) error { + vv := messageV2Of(v) + if vv == nil { return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v) } + return proto.Unmarshal(data, vv) } +func messageV2Of(v any) proto.Message { + switch v := v.(type) { + case protoadapt.MessageV1: + return protoadapt.MessageV2Of(v) + case protoadapt.MessageV2: + return v + } + + return nil +} + func (codec) Name() string { return Name } diff --git a/vendor/google.golang.org/grpc/grpclog/component.go b/vendor/google.golang.org/grpc/grpclog/component.go index 8358dd6e2..ac73c9ced 100644 --- a/vendor/google.golang.org/grpc/grpclog/component.go +++ b/vendor/google.golang.org/grpc/grpclog/component.go @@ -31,71 +31,71 @@ type componentData struct { var cache = map[string]*componentData{} -func (c *componentData) InfoDepth(depth int, args ...interface{}) { - args = append([]interface{}{"[" + string(c.name) + "]"}, args...) +func (c *componentData) InfoDepth(depth int, args ...any) { + args = append([]any{"[" + string(c.name) + "]"}, args...) grpclog.InfoDepth(depth+1, args...) } -func (c *componentData) WarningDepth(depth int, args ...interface{}) { - args = append([]interface{}{"[" + string(c.name) + "]"}, args...) +func (c *componentData) WarningDepth(depth int, args ...any) { + args = append([]any{"[" + string(c.name) + "]"}, args...) grpclog.WarningDepth(depth+1, args...) } -func (c *componentData) ErrorDepth(depth int, args ...interface{}) { - args = append([]interface{}{"[" + string(c.name) + "]"}, args...) +func (c *componentData) ErrorDepth(depth int, args ...any) { + args = append([]any{"[" + string(c.name) + "]"}, args...) grpclog.ErrorDepth(depth+1, args...) } -func (c *componentData) FatalDepth(depth int, args ...interface{}) { - args = append([]interface{}{"[" + string(c.name) + "]"}, args...) +func (c *componentData) FatalDepth(depth int, args ...any) { + args = append([]any{"[" + string(c.name) + "]"}, args...) grpclog.FatalDepth(depth+1, args...) } -func (c *componentData) Info(args ...interface{}) { +func (c *componentData) Info(args ...any) { c.InfoDepth(1, args...) } -func (c *componentData) Warning(args ...interface{}) { +func (c *componentData) Warning(args ...any) { c.WarningDepth(1, args...) } -func (c *componentData) Error(args ...interface{}) { +func (c *componentData) Error(args ...any) { c.ErrorDepth(1, args...) } -func (c *componentData) Fatal(args ...interface{}) { +func (c *componentData) Fatal(args ...any) { c.FatalDepth(1, args...) } -func (c *componentData) Infof(format string, args ...interface{}) { +func (c *componentData) Infof(format string, args ...any) { c.InfoDepth(1, fmt.Sprintf(format, args...)) } -func (c *componentData) Warningf(format string, args ...interface{}) { +func (c *componentData) Warningf(format string, args ...any) { c.WarningDepth(1, fmt.Sprintf(format, args...)) } -func (c *componentData) Errorf(format string, args ...interface{}) { +func (c *componentData) Errorf(format string, args ...any) { c.ErrorDepth(1, fmt.Sprintf(format, args...)) } -func (c *componentData) Fatalf(format string, args ...interface{}) { +func (c *componentData) Fatalf(format string, args ...any) { c.FatalDepth(1, fmt.Sprintf(format, args...)) } -func (c *componentData) Infoln(args ...interface{}) { +func (c *componentData) Infoln(args ...any) { c.InfoDepth(1, args...) } -func (c *componentData) Warningln(args ...interface{}) { +func (c *componentData) Warningln(args ...any) { c.WarningDepth(1, args...) } -func (c *componentData) Errorln(args ...interface{}) { +func (c *componentData) Errorln(args ...any) { c.ErrorDepth(1, args...) } -func (c *componentData) Fatalln(args ...interface{}) { +func (c *componentData) Fatalln(args ...any) { c.FatalDepth(1, args...) } diff --git a/vendor/google.golang.org/grpc/grpclog/grpclog.go b/vendor/google.golang.org/grpc/grpclog/grpclog.go index c8bb2be34..16928c9cb 100644 --- a/vendor/google.golang.org/grpc/grpclog/grpclog.go +++ b/vendor/google.golang.org/grpc/grpclog/grpclog.go @@ -42,53 +42,53 @@ func V(l int) bool { } // Info logs to the INFO log. -func Info(args ...interface{}) { +func Info(args ...any) { grpclog.Logger.Info(args...) } // Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf. -func Infof(format string, args ...interface{}) { +func Infof(format string, args ...any) { grpclog.Logger.Infof(format, args...) } // Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println. -func Infoln(args ...interface{}) { +func Infoln(args ...any) { grpclog.Logger.Infoln(args...) } // Warning logs to the WARNING log. -func Warning(args ...interface{}) { +func Warning(args ...any) { grpclog.Logger.Warning(args...) } // Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf. -func Warningf(format string, args ...interface{}) { +func Warningf(format string, args ...any) { grpclog.Logger.Warningf(format, args...) } // Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println. -func Warningln(args ...interface{}) { +func Warningln(args ...any) { grpclog.Logger.Warningln(args...) } // Error logs to the ERROR log. -func Error(args ...interface{}) { +func Error(args ...any) { grpclog.Logger.Error(args...) } // Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf. -func Errorf(format string, args ...interface{}) { +func Errorf(format string, args ...any) { grpclog.Logger.Errorf(format, args...) } // Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println. -func Errorln(args ...interface{}) { +func Errorln(args ...any) { grpclog.Logger.Errorln(args...) } // Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print. // It calls os.Exit() with exit code 1. -func Fatal(args ...interface{}) { +func Fatal(args ...any) { grpclog.Logger.Fatal(args...) // Make sure fatal logs will exit. os.Exit(1) @@ -96,7 +96,7 @@ func Fatal(args ...interface{}) { // Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf. // It calls os.Exit() with exit code 1. -func Fatalf(format string, args ...interface{}) { +func Fatalf(format string, args ...any) { grpclog.Logger.Fatalf(format, args...) // Make sure fatal logs will exit. os.Exit(1) @@ -104,7 +104,7 @@ func Fatalf(format string, args ...interface{}) { // Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println. // It calle os.Exit()) with exit code 1. -func Fatalln(args ...interface{}) { +func Fatalln(args ...any) { grpclog.Logger.Fatalln(args...) // Make sure fatal logs will exit. os.Exit(1) @@ -113,20 +113,20 @@ func Fatalln(args ...interface{}) { // Print prints to the logger. Arguments are handled in the manner of fmt.Print. // // Deprecated: use Info. -func Print(args ...interface{}) { +func Print(args ...any) { grpclog.Logger.Info(args...) } // Printf prints to the logger. Arguments are handled in the manner of fmt.Printf. // // Deprecated: use Infof. -func Printf(format string, args ...interface{}) { +func Printf(format string, args ...any) { grpclog.Logger.Infof(format, args...) } // Println prints to the logger. Arguments are handled in the manner of fmt.Println. // // Deprecated: use Infoln. -func Println(args ...interface{}) { +func Println(args ...any) { grpclog.Logger.Infoln(args...) } diff --git a/vendor/google.golang.org/grpc/grpclog/logger.go b/vendor/google.golang.org/grpc/grpclog/logger.go index ef06a4822..b1674d826 100644 --- a/vendor/google.golang.org/grpc/grpclog/logger.go +++ b/vendor/google.golang.org/grpc/grpclog/logger.go @@ -24,12 +24,12 @@ import "google.golang.org/grpc/internal/grpclog" // // Deprecated: use LoggerV2. type Logger interface { - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) - Fatalln(args ...interface{}) - Print(args ...interface{}) - Printf(format string, args ...interface{}) - Println(args ...interface{}) + Fatal(args ...any) + Fatalf(format string, args ...any) + Fatalln(args ...any) + Print(args ...any) + Printf(format string, args ...any) + Println(args ...any) } // SetLogger sets the logger that is used in grpc. Call only from @@ -45,39 +45,39 @@ type loggerWrapper struct { Logger } -func (g *loggerWrapper) Info(args ...interface{}) { +func (g *loggerWrapper) Info(args ...any) { g.Logger.Print(args...) } -func (g *loggerWrapper) Infoln(args ...interface{}) { +func (g *loggerWrapper) Infoln(args ...any) { g.Logger.Println(args...) } -func (g *loggerWrapper) Infof(format string, args ...interface{}) { +func (g *loggerWrapper) Infof(format string, args ...any) { g.Logger.Printf(format, args...) } -func (g *loggerWrapper) Warning(args ...interface{}) { +func (g *loggerWrapper) Warning(args ...any) { g.Logger.Print(args...) } -func (g *loggerWrapper) Warningln(args ...interface{}) { +func (g *loggerWrapper) Warningln(args ...any) { g.Logger.Println(args...) } -func (g *loggerWrapper) Warningf(format string, args ...interface{}) { +func (g *loggerWrapper) Warningf(format string, args ...any) { g.Logger.Printf(format, args...) } -func (g *loggerWrapper) Error(args ...interface{}) { +func (g *loggerWrapper) Error(args ...any) { g.Logger.Print(args...) } -func (g *loggerWrapper) Errorln(args ...interface{}) { +func (g *loggerWrapper) Errorln(args ...any) { g.Logger.Println(args...) } -func (g *loggerWrapper) Errorf(format string, args ...interface{}) { +func (g *loggerWrapper) Errorf(format string, args ...any) { g.Logger.Printf(format, args...) } diff --git a/vendor/google.golang.org/grpc/grpclog/loggerv2.go b/vendor/google.golang.org/grpc/grpclog/loggerv2.go index 5de66e40d..ecfd36d71 100644 --- a/vendor/google.golang.org/grpc/grpclog/loggerv2.go +++ b/vendor/google.golang.org/grpc/grpclog/loggerv2.go @@ -33,35 +33,35 @@ import ( // LoggerV2 does underlying logging work for grpclog. type LoggerV2 interface { // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. - Info(args ...interface{}) + Info(args ...any) // Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println. - Infoln(args ...interface{}) + Infoln(args ...any) // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. - Infof(format string, args ...interface{}) + Infof(format string, args ...any) // Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print. - Warning(args ...interface{}) + Warning(args ...any) // Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println. - Warningln(args ...interface{}) + Warningln(args ...any) // Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. - Warningf(format string, args ...interface{}) + Warningf(format string, args ...any) // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. - Error(args ...interface{}) + Error(args ...any) // Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println. - Errorln(args ...interface{}) + Errorln(args ...any) // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. - Errorf(format string, args ...interface{}) + Errorf(format string, args ...any) // Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatal(args ...interface{}) + Fatal(args ...any) // Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatalln(args ...interface{}) + Fatalln(args ...any) // Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatalf(format string, args ...interface{}) + Fatalf(format string, args ...any) // V reports whether verbosity level l is at least the requested verbose level. V(l int) bool } @@ -182,53 +182,53 @@ func (g *loggerT) output(severity int, s string) { g.m[severity].Output(2, string(b)) } -func (g *loggerT) Info(args ...interface{}) { +func (g *loggerT) Info(args ...any) { g.output(infoLog, fmt.Sprint(args...)) } -func (g *loggerT) Infoln(args ...interface{}) { +func (g *loggerT) Infoln(args ...any) { g.output(infoLog, fmt.Sprintln(args...)) } -func (g *loggerT) Infof(format string, args ...interface{}) { +func (g *loggerT) Infof(format string, args ...any) { g.output(infoLog, fmt.Sprintf(format, args...)) } -func (g *loggerT) Warning(args ...interface{}) { +func (g *loggerT) Warning(args ...any) { g.output(warningLog, fmt.Sprint(args...)) } -func (g *loggerT) Warningln(args ...interface{}) { +func (g *loggerT) Warningln(args ...any) { g.output(warningLog, fmt.Sprintln(args...)) } -func (g *loggerT) Warningf(format string, args ...interface{}) { +func (g *loggerT) Warningf(format string, args ...any) { g.output(warningLog, fmt.Sprintf(format, args...)) } -func (g *loggerT) Error(args ...interface{}) { +func (g *loggerT) Error(args ...any) { g.output(errorLog, fmt.Sprint(args...)) } -func (g *loggerT) Errorln(args ...interface{}) { +func (g *loggerT) Errorln(args ...any) { g.output(errorLog, fmt.Sprintln(args...)) } -func (g *loggerT) Errorf(format string, args ...interface{}) { +func (g *loggerT) Errorf(format string, args ...any) { g.output(errorLog, fmt.Sprintf(format, args...)) } -func (g *loggerT) Fatal(args ...interface{}) { +func (g *loggerT) Fatal(args ...any) { g.output(fatalLog, fmt.Sprint(args...)) os.Exit(1) } -func (g *loggerT) Fatalln(args ...interface{}) { +func (g *loggerT) Fatalln(args ...any) { g.output(fatalLog, fmt.Sprintln(args...)) os.Exit(1) } -func (g *loggerT) Fatalf(format string, args ...interface{}) { +func (g *loggerT) Fatalf(format string, args ...any) { g.output(fatalLog, fmt.Sprintf(format, args...)) os.Exit(1) } @@ -248,11 +248,11 @@ func (g *loggerT) V(l int) bool { type DepthLoggerV2 interface { LoggerV2 // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println. - InfoDepth(depth int, args ...interface{}) + InfoDepth(depth int, args ...any) // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println. - WarningDepth(depth int, args ...interface{}) + WarningDepth(depth int, args ...any) // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println. - ErrorDepth(depth int, args ...interface{}) + ErrorDepth(depth int, args ...any) // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println. - FatalDepth(depth int, args ...interface{}) + FatalDepth(depth int, args ...any) } diff --git a/vendor/google.golang.org/grpc/idle.go b/vendor/google.golang.org/grpc/idle.go deleted file mode 100644 index dc3dc72f6..000000000 --- a/vendor/google.golang.org/grpc/idle.go +++ /dev/null @@ -1,287 +0,0 @@ -/* - * - * Copyright 2023 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "math" - "sync" - "sync/atomic" - "time" -) - -// For overriding in unit tests. -var timeAfterFunc = func(d time.Duration, f func()) *time.Timer { - return time.AfterFunc(d, f) -} - -// idlenessEnforcer is the functionality provided by grpc.ClientConn to enter -// and exit from idle mode. -type idlenessEnforcer interface { - exitIdleMode() error - enterIdleMode() error -} - -// idlenessManager defines the functionality required to track RPC activity on a -// channel. -type idlenessManager interface { - onCallBegin() error - onCallEnd() - close() -} - -type noopIdlenessManager struct{} - -func (noopIdlenessManager) onCallBegin() error { return nil } -func (noopIdlenessManager) onCallEnd() {} -func (noopIdlenessManager) close() {} - -// idlenessManagerImpl implements the idlenessManager interface. It uses atomic -// operations to synchronize access to shared state and a mutex to guarantee -// mutual exclusion in a critical section. -type idlenessManagerImpl struct { - // State accessed atomically. - lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. - activeCallsCount int32 // Count of active RPCs; -math.MaxInt32 means channel is idle or is trying to get there. - activeSinceLastTimerCheck int32 // Boolean; True if there was an RPC since the last timer callback. - closed int32 // Boolean; True when the manager is closed. - - // Can be accessed without atomics or mutex since these are set at creation - // time and read-only after that. - enforcer idlenessEnforcer // Functionality provided by grpc.ClientConn. - timeout int64 // Idle timeout duration nanos stored as an int64. - - // idleMu is used to guarantee mutual exclusion in two scenarios: - // - Opposing intentions: - // - a: Idle timeout has fired and handleIdleTimeout() is trying to put - // the channel in idle mode because the channel has been inactive. - // - b: At the same time an RPC is made on the channel, and onCallBegin() - // is trying to prevent the channel from going idle. - // - Competing intentions: - // - The channel is in idle mode and there are multiple RPCs starting at - // the same time, all trying to move the channel out of idle. Only one - // of them should succeed in doing so, while the other RPCs should - // piggyback on the first one and be successfully handled. - idleMu sync.RWMutex - actuallyIdle bool - timer *time.Timer -} - -// newIdlenessManager creates a new idleness manager implementation for the -// given idle timeout. -func newIdlenessManager(enforcer idlenessEnforcer, idleTimeout time.Duration) idlenessManager { - if idleTimeout == 0 { - return noopIdlenessManager{} - } - - i := &idlenessManagerImpl{ - enforcer: enforcer, - timeout: int64(idleTimeout), - } - i.timer = timeAfterFunc(idleTimeout, i.handleIdleTimeout) - return i -} - -// resetIdleTimer resets the idle timer to the given duration. This method -// should only be called from the timer callback. -func (i *idlenessManagerImpl) resetIdleTimer(d time.Duration) { - i.idleMu.Lock() - defer i.idleMu.Unlock() - - if i.timer == nil { - // Only close sets timer to nil. We are done. - return - } - - // It is safe to ignore the return value from Reset() because this method is - // only ever called from the timer callback, which means the timer has - // already fired. - i.timer.Reset(d) -} - -// handleIdleTimeout is the timer callback that is invoked upon expiry of the -// configured idle timeout. The channel is considered inactive if there are no -// ongoing calls and no RPC activity since the last time the timer fired. -func (i *idlenessManagerImpl) handleIdleTimeout() { - if i.isClosed() { - return - } - - if atomic.LoadInt32(&i.activeCallsCount) > 0 { - i.resetIdleTimer(time.Duration(i.timeout)) - return - } - - // There has been activity on the channel since we last got here. Reset the - // timer and return. - if atomic.LoadInt32(&i.activeSinceLastTimerCheck) == 1 { - // Set the timer to fire after a duration of idle timeout, calculated - // from the time the most recent RPC completed. - atomic.StoreInt32(&i.activeSinceLastTimerCheck, 0) - i.resetIdleTimer(time.Duration(atomic.LoadInt64(&i.lastCallEndTime) + i.timeout - time.Now().UnixNano())) - return - } - - // This CAS operation is extremely likely to succeed given that there has - // been no activity since the last time we were here. Setting the - // activeCallsCount to -math.MaxInt32 indicates to onCallBegin() that the - // channel is either in idle mode or is trying to get there. - if !atomic.CompareAndSwapInt32(&i.activeCallsCount, 0, -math.MaxInt32) { - // This CAS operation can fail if an RPC started after we checked for - // activity at the top of this method, or one was ongoing from before - // the last time we were here. In both case, reset the timer and return. - i.resetIdleTimer(time.Duration(i.timeout)) - return - } - - // Now that we've set the active calls count to -math.MaxInt32, it's time to - // actually move to idle mode. - if i.tryEnterIdleMode() { - // Successfully entered idle mode. No timer needed until we exit idle. - return - } - - // Failed to enter idle mode due to a concurrent RPC that kept the channel - // active, or because of an error from the channel. Undo the attempt to - // enter idle, and reset the timer to try again later. - atomic.AddInt32(&i.activeCallsCount, math.MaxInt32) - i.resetIdleTimer(time.Duration(i.timeout)) -} - -// tryEnterIdleMode instructs the channel to enter idle mode. But before -// that, it performs a last minute check to ensure that no new RPC has come in, -// making the channel active. -// -// Return value indicates whether or not the channel moved to idle mode. -// -// Holds idleMu which ensures mutual exclusion with exitIdleMode. -func (i *idlenessManagerImpl) tryEnterIdleMode() bool { - i.idleMu.Lock() - defer i.idleMu.Unlock() - - if atomic.LoadInt32(&i.activeCallsCount) != -math.MaxInt32 { - // We raced and lost to a new RPC. Very rare, but stop entering idle. - return false - } - if atomic.LoadInt32(&i.activeSinceLastTimerCheck) == 1 { - // An very short RPC could have come in (and also finished) after we - // checked for calls count and activity in handleIdleTimeout(), but - // before the CAS operation. So, we need to check for activity again. - return false - } - - // No new RPCs have come in since we last set the active calls count value - // -math.MaxInt32 in the timer callback. And since we have the lock, it is - // safe to enter idle mode now. - if err := i.enforcer.enterIdleMode(); err != nil { - logger.Errorf("Failed to enter idle mode: %v", err) - return false - } - - // Successfully entered idle mode. - i.actuallyIdle = true - return true -} - -// onCallBegin is invoked at the start of every RPC. -func (i *idlenessManagerImpl) onCallBegin() error { - if i.isClosed() { - return nil - } - - if atomic.AddInt32(&i.activeCallsCount, 1) > 0 { - // Channel is not idle now. Set the activity bit and allow the call. - atomic.StoreInt32(&i.activeSinceLastTimerCheck, 1) - return nil - } - - // Channel is either in idle mode or is in the process of moving to idle - // mode. Attempt to exit idle mode to allow this RPC. - if err := i.exitIdleMode(); err != nil { - // Undo the increment to calls count, and return an error causing the - // RPC to fail. - atomic.AddInt32(&i.activeCallsCount, -1) - return err - } - - atomic.StoreInt32(&i.activeSinceLastTimerCheck, 1) - return nil -} - -// exitIdleMode instructs the channel to exit idle mode. -// -// Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. -func (i *idlenessManagerImpl) exitIdleMode() error { - i.idleMu.Lock() - defer i.idleMu.Unlock() - - if !i.actuallyIdle { - // This can happen in two scenarios: - // - handleIdleTimeout() set the calls count to -math.MaxInt32 and called - // tryEnterIdleMode(). But before the latter could grab the lock, an RPC - // came in and onCallBegin() noticed that the calls count is negative. - // - Channel is in idle mode, and multiple new RPCs come in at the same - // time, all of them notice a negative calls count in onCallBegin and get - // here. The first one to get the lock would got the channel to exit idle. - // - // Either way, nothing to do here. - return nil - } - - if err := i.enforcer.exitIdleMode(); err != nil { - return fmt.Errorf("channel failed to exit idle mode: %v", err) - } - - // Undo the idle entry process. This also respects any new RPC attempts. - atomic.AddInt32(&i.activeCallsCount, math.MaxInt32) - i.actuallyIdle = false - - // Start a new timer to fire after the configured idle timeout. - i.timer = timeAfterFunc(time.Duration(i.timeout), i.handleIdleTimeout) - return nil -} - -// onCallEnd is invoked at the end of every RPC. -func (i *idlenessManagerImpl) onCallEnd() { - if i.isClosed() { - return - } - - // Record the time at which the most recent call finished. - atomic.StoreInt64(&i.lastCallEndTime, time.Now().UnixNano()) - - // Decrement the active calls count. This count can temporarily go negative - // when the timer callback is in the process of moving the channel to idle - // mode, but one or more RPCs come in and complete before the timer callback - // can get done with the process of moving to idle mode. - atomic.AddInt32(&i.activeCallsCount, -1) -} - -func (i *idlenessManagerImpl) isClosed() bool { - return atomic.LoadInt32(&i.closed) == 1 -} - -func (i *idlenessManagerImpl) close() { - atomic.StoreInt32(&i.closed, 1) - - i.idleMu.Lock() - i.timer.Stop() - i.timer = nil - i.idleMu.Unlock() -} diff --git a/vendor/google.golang.org/grpc/interceptor.go b/vendor/google.golang.org/grpc/interceptor.go index bb96ef57b..877d78fc3 100644 --- a/vendor/google.golang.org/grpc/interceptor.go +++ b/vendor/google.golang.org/grpc/interceptor.go @@ -23,7 +23,7 @@ import ( ) // UnaryInvoker is called by UnaryClientInterceptor to complete RPCs. -type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error +type UnaryInvoker func(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error // UnaryClientInterceptor intercepts the execution of a unary RPC on the client. // Unary interceptors can be specified as a DialOption, using @@ -40,7 +40,7 @@ type UnaryInvoker func(ctx context.Context, method string, req, reply interface{ // defaults from the ClientConn as well as per-call options. // // The returned error must be compatible with the status package. -type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error +type UnaryClientInterceptor func(ctx context.Context, method string, req, reply any, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error // Streamer is called by StreamClientInterceptor to create a ClientStream. type Streamer func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) @@ -66,7 +66,7 @@ type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *Cli // server side. All per-rpc information may be mutated by the interceptor. type UnaryServerInfo struct { // Server is the service implementation the user provides. This is read-only. - Server interface{} + Server any // FullMethod is the full RPC method string, i.e., /package.service/method. FullMethod string } @@ -78,13 +78,13 @@ type UnaryServerInfo struct { // status package, or be one of the context errors. Otherwise, gRPC will use // codes.Unknown as the status code and err.Error() as the status message of the // RPC. -type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error) +type UnaryHandler func(ctx context.Context, req any) (any, error) // UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info // contains all the information of this RPC the interceptor can operate on. And handler is the wrapper // of the service method implementation. It is the responsibility of the interceptor to invoke handler // to complete the RPC. -type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) +type UnaryServerInterceptor func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (resp any, err error) // StreamServerInfo consists of various information about a streaming RPC on // server side. All per-rpc information may be mutated by the interceptor. @@ -101,4 +101,4 @@ type StreamServerInfo struct { // info contains all the information of this RPC the interceptor can operate on. And handler is the // service method implementation. It is the responsibility of the interceptor to invoke handler to // complete the RPC. -type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error +type StreamServerInterceptor func(srv any, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error diff --git a/vendor/google.golang.org/grpc/internal/backoff/backoff.go b/vendor/google.golang.org/grpc/internal/backoff/backoff.go index 5fc0ee3da..fed1c011a 100644 --- a/vendor/google.golang.org/grpc/internal/backoff/backoff.go +++ b/vendor/google.golang.org/grpc/internal/backoff/backoff.go @@ -23,6 +23,8 @@ package backoff import ( + "context" + "errors" "time" grpcbackoff "google.golang.org/grpc/backoff" @@ -71,3 +73,37 @@ func (bc Exponential) Backoff(retries int) time.Duration { } return time.Duration(backoff) } + +// ErrResetBackoff is the error to be returned by the function executed by RunF, +// to instruct the latter to reset its backoff state. +var ErrResetBackoff = errors.New("reset backoff state") + +// RunF provides a convenient way to run a function f repeatedly until the +// context expires or f returns a non-nil error that is not ErrResetBackoff. +// When f returns ErrResetBackoff, RunF continues to run f, but resets its +// backoff state before doing so. backoff accepts an integer representing the +// number of retries, and returns the amount of time to backoff. +func RunF(ctx context.Context, f func() error, backoff func(int) time.Duration) { + attempt := 0 + timer := time.NewTimer(0) + for ctx.Err() == nil { + select { + case <-timer.C: + case <-ctx.Done(): + timer.Stop() + return + } + + err := f() + if errors.Is(err, ErrResetBackoff) { + timer.Reset(0) + attempt = 0 + continue + } + if err != nil { + return + } + timer.Reset(backoff(attempt)) + attempt++ + } +} diff --git a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/config.go b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/config.go new file mode 100644 index 000000000..6bf7f8739 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/config.go @@ -0,0 +1,83 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package gracefulswitch + +import ( + "encoding/json" + "fmt" + + "google.golang.org/grpc/balancer" + "google.golang.org/grpc/serviceconfig" +) + +type lbConfig struct { + serviceconfig.LoadBalancingConfig + + childBuilder balancer.Builder + childConfig serviceconfig.LoadBalancingConfig +} + +func ChildName(l serviceconfig.LoadBalancingConfig) string { + return l.(*lbConfig).childBuilder.Name() +} + +// ParseConfig parses a child config list and returns a LB config for the +// gracefulswitch Balancer. +// +// cfg is expected to be a json.RawMessage containing a JSON array of LB policy +// names + configs as the format of the "loadBalancingConfig" field in +// ServiceConfig. It returns a type that should be passed to +// UpdateClientConnState in the BalancerConfig field. +func ParseConfig(cfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { + var lbCfg []map[string]json.RawMessage + if err := json.Unmarshal(cfg, &lbCfg); err != nil { + return nil, err + } + for i, e := range lbCfg { + if len(e) != 1 { + return nil, fmt.Errorf("expected a JSON struct with one entry; received entry %v at index %d", e, i) + } + + var name string + var jsonCfg json.RawMessage + for name, jsonCfg = range e { + } + + builder := balancer.Get(name) + if builder == nil { + // Skip unregistered balancer names. + continue + } + + parser, ok := builder.(balancer.ConfigParser) + if !ok { + // This is a valid child with no config. + return &lbConfig{childBuilder: builder}, nil + } + + cfg, err := parser.ParseConfig(jsonCfg) + if err != nil { + return nil, fmt.Errorf("error parsing config for policy %q: %v", name, err) + } + + return &lbConfig{childBuilder: builder, childConfig: cfg}, nil + } + + return nil, fmt.Errorf("no supported policies found in config: %v", string(cfg)) +} diff --git a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go index 08666f62a..45d5e50ea 100644 --- a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go +++ b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go @@ -94,14 +94,23 @@ func (gsb *Balancer) balancerCurrentOrPending(bw *balancerWrapper) bool { // process is not complete when this method returns. This method must be called // synchronously alongside the rest of the balancer.Balancer methods this // Graceful Switch Balancer implements. +// +// Deprecated: use ParseConfig and pass a parsed config to UpdateClientConnState +// to cause the Balancer to automatically change to the new child when necessary. func (gsb *Balancer) SwitchTo(builder balancer.Builder) error { + _, err := gsb.switchTo(builder) + return err +} + +func (gsb *Balancer) switchTo(builder balancer.Builder) (*balancerWrapper, error) { gsb.mu.Lock() if gsb.closed { gsb.mu.Unlock() - return errBalancerClosed + return nil, errBalancerClosed } bw := &balancerWrapper{ - gsb: gsb, + builder: builder, + gsb: gsb, lastState: balancer.State{ ConnectivityState: connectivity.Connecting, Picker: base.NewErrPicker(balancer.ErrNoSubConnAvailable), @@ -129,7 +138,7 @@ func (gsb *Balancer) SwitchTo(builder balancer.Builder) error { gsb.balancerCurrent = nil } gsb.mu.Unlock() - return balancer.ErrBadResolverState + return nil, balancer.ErrBadResolverState } // This write doesn't need to take gsb.mu because this field never gets read @@ -138,7 +147,7 @@ func (gsb *Balancer) SwitchTo(builder balancer.Builder) error { // bw.Balancer field will never be forwarded to until this SwitchTo() // function returns. bw.Balancer = newBalancer - return nil + return bw, nil } // Returns nil if the graceful switch balancer is closed. @@ -152,12 +161,33 @@ func (gsb *Balancer) latestBalancer() *balancerWrapper { } // UpdateClientConnState forwards the update to the latest balancer created. +// +// If the state's BalancerConfig is the config returned by a call to +// gracefulswitch.ParseConfig, then this function will automatically SwitchTo +// the balancer indicated by the config before forwarding its config to it, if +// necessary. func (gsb *Balancer) UpdateClientConnState(state balancer.ClientConnState) error { // The resolver data is only relevant to the most recent LB Policy. balToUpdate := gsb.latestBalancer() + + gsbCfg, ok := state.BalancerConfig.(*lbConfig) + if ok { + // Switch to the child in the config unless it is already active. + if balToUpdate == nil || gsbCfg.childBuilder.Name() != balToUpdate.builder.Name() { + var err error + balToUpdate, err = gsb.switchTo(gsbCfg.childBuilder) + if err != nil { + return fmt.Errorf("could not switch to new child balancer: %w", err) + } + } + // Unwrap the child balancer's config. + state.BalancerConfig = gsbCfg.childConfig + } + if balToUpdate == nil { return errBalancerClosed } + // Perform this call without gsb.mu to prevent deadlocks if the child calls // back into the channel. The latest balancer can never be closed during a // call from the channel, even without gsb.mu held. @@ -169,6 +199,10 @@ func (gsb *Balancer) ResolverError(err error) { // The resolver data is only relevant to the most recent LB Policy. balToUpdate := gsb.latestBalancer() if balToUpdate == nil { + gsb.cc.UpdateState(balancer.State{ + ConnectivityState: connectivity.TransientFailure, + Picker: base.NewErrPicker(err), + }) return } // Perform this call without gsb.mu to prevent deadlocks if the child calls @@ -200,8 +234,8 @@ func (gsb *Balancer) ExitIdle() { } } -// UpdateSubConnState forwards the update to the appropriate child. -func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { +// updateSubConnState forwards the update to the appropriate child. +func (gsb *Balancer) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState, cb func(balancer.SubConnState)) { gsb.currentMu.Lock() defer gsb.currentMu.Unlock() gsb.mu.Lock() @@ -214,13 +248,26 @@ func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubC } else if gsb.balancerPending != nil && gsb.balancerPending.subconns[sc] { balToUpdate = gsb.balancerPending } - gsb.mu.Unlock() if balToUpdate == nil { // SubConn belonged to a stale lb policy that has not yet fully closed, // or the balancer was already closed. + gsb.mu.Unlock() return } - balToUpdate.UpdateSubConnState(sc, state) + if state.ConnectivityState == connectivity.Shutdown { + delete(balToUpdate.subconns, sc) + } + gsb.mu.Unlock() + if cb != nil { + cb(state) + } else { + balToUpdate.UpdateSubConnState(sc, state) + } +} + +// UpdateSubConnState forwards the update to the appropriate child. +func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { + gsb.updateSubConnState(sc, state, nil) } // Close closes any active child balancers. @@ -242,33 +289,23 @@ func (gsb *Balancer) Close() { // // It implements the balancer.ClientConn interface and is passed down in that // capacity to the wrapped balancer. It maintains a set of subConns created by -// the wrapped balancer and calls from the latter to create/update/remove +// the wrapped balancer and calls from the latter to create/update/shutdown // SubConns update this set before being forwarded to the parent ClientConn. // State updates from the wrapped balancer can result in invocation of the // graceful switch logic. type balancerWrapper struct { balancer.Balancer - gsb *Balancer + gsb *Balancer + builder balancer.Builder lastState balancer.State subconns map[balancer.SubConn]bool // subconns created by this balancer } -func (bw *balancerWrapper) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { - if state.ConnectivityState == connectivity.Shutdown { - bw.gsb.mu.Lock() - delete(bw.subconns, sc) - bw.gsb.mu.Unlock() - } - // There is no need to protect this read with a mutex, as the write to the - // Balancer field happens in SwitchTo, which completes before this can be - // called. - bw.Balancer.UpdateSubConnState(sc, state) -} - -// Close closes the underlying LB policy and removes the subconns it created. bw -// must not be referenced via balancerCurrent or balancerPending in gsb when -// called. gsb.mu must not be held. Does not panic with a nil receiver. +// Close closes the underlying LB policy and shuts down the subconns it +// created. bw must not be referenced via balancerCurrent or balancerPending in +// gsb when called. gsb.mu must not be held. Does not panic with a nil +// receiver. func (bw *balancerWrapper) Close() { // before Close is called. if bw == nil { @@ -281,7 +318,7 @@ func (bw *balancerWrapper) Close() { bw.Balancer.Close() bw.gsb.mu.Lock() for sc := range bw.subconns { - bw.gsb.cc.RemoveSubConn(sc) + sc.Shutdown() } bw.gsb.mu.Unlock() } @@ -335,13 +372,16 @@ func (bw *balancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.Ne } bw.gsb.mu.Unlock() + var sc balancer.SubConn + oldListener := opts.StateListener + opts.StateListener = func(state balancer.SubConnState) { bw.gsb.updateSubConnState(sc, state, oldListener) } sc, err := bw.gsb.cc.NewSubConn(addrs, opts) if err != nil { return nil, err } bw.gsb.mu.Lock() if !bw.gsb.balancerCurrentOrPending(bw) { // balancer was closed during this call - bw.gsb.cc.RemoveSubConn(sc) + sc.Shutdown() bw.gsb.mu.Unlock() return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw) } @@ -360,13 +400,9 @@ func (bw *balancerWrapper) ResolveNow(opts resolver.ResolveNowOptions) { } func (bw *balancerWrapper) RemoveSubConn(sc balancer.SubConn) { - bw.gsb.mu.Lock() - if !bw.gsb.balancerCurrentOrPending(bw) { - bw.gsb.mu.Unlock() - return - } - bw.gsb.mu.Unlock() - bw.gsb.cc.RemoveSubConn(sc) + // Note: existing third party balancers may call this, so it must remain + // until RemoveSubConn is fully removed. + sc.Shutdown() } func (bw *balancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { diff --git a/vendor/google.golang.org/grpc/internal/balancerload/load.go b/vendor/google.golang.org/grpc/internal/balancerload/load.go index 3a905d966..94a08d687 100644 --- a/vendor/google.golang.org/grpc/internal/balancerload/load.go +++ b/vendor/google.golang.org/grpc/internal/balancerload/load.go @@ -25,7 +25,7 @@ import ( // Parser converts loads from metadata into a concrete type. type Parser interface { // Parse parses loads from metadata. - Parse(md metadata.MD) interface{} + Parse(md metadata.MD) any } var parser Parser @@ -38,7 +38,7 @@ func SetParser(lr Parser) { } // Parse calls parser.Read(). -func Parse(md metadata.MD) interface{} { +func Parse(md metadata.MD) any { if parser == nil { return nil } diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go index 6c3f63221..e8456a77c 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go @@ -25,11 +25,12 @@ import ( "sync/atomic" "time" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes" binlogpb "google.golang.org/grpc/binarylog/grpc_binarylog_v1" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" ) type callIDGenerator struct { @@ -88,7 +89,7 @@ func NewTruncatingMethodLogger(h, m uint64) *TruncatingMethodLogger { // in TruncatingMethodLogger as possible. func (ml *TruncatingMethodLogger) Build(c LogEntryConfig) *binlogpb.GrpcLogEntry { m := c.toProto() - timestamp, _ := ptypes.TimestampProto(time.Now()) + timestamp := timestamppb.Now() m.Timestamp = timestamp m.CallId = ml.callID m.SequenceIdWithinCall = ml.idWithinCallGen.next() @@ -178,7 +179,7 @@ func (c *ClientHeader) toProto() *binlogpb.GrpcLogEntry { Authority: c.Authority, } if c.Timeout > 0 { - clientHeader.Timeout = ptypes.DurationProto(c.Timeout) + clientHeader.Timeout = durationpb.New(c.Timeout) } ret := &binlogpb.GrpcLogEntry{ Type: binlogpb.GrpcLogEntry_EVENT_TYPE_CLIENT_HEADER, @@ -230,7 +231,7 @@ type ClientMessage struct { OnClientSide bool // Message can be a proto.Message or []byte. Other messages formats are not // supported. - Message interface{} + Message any } func (c *ClientMessage) toProto() *binlogpb.GrpcLogEntry { @@ -270,7 +271,7 @@ type ServerMessage struct { OnClientSide bool // Message can be a proto.Message or []byte. Other messages formats are not // supported. - Message interface{} + Message any } func (c *ServerMessage) toProto() *binlogpb.GrpcLogEntry { diff --git a/vendor/google.golang.org/grpc/internal/binarylog/sink.go b/vendor/google.golang.org/grpc/internal/binarylog/sink.go index 264de387c..9ea598b14 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/sink.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/sink.go @@ -25,8 +25,8 @@ import ( "sync" "time" - "github.com/golang/protobuf/proto" binlogpb "google.golang.org/grpc/binarylog/grpc_binarylog_v1" + "google.golang.org/protobuf/proto" ) var ( diff --git a/vendor/google.golang.org/grpc/internal/buffer/unbounded.go b/vendor/google.golang.org/grpc/internal/buffer/unbounded.go index 81c2f5fd7..11f91668a 100644 --- a/vendor/google.golang.org/grpc/internal/buffer/unbounded.go +++ b/vendor/google.golang.org/grpc/internal/buffer/unbounded.go @@ -18,7 +18,10 @@ // Package buffer provides an implementation of an unbounded buffer. package buffer -import "sync" +import ( + "errors" + "sync" +) // Unbounded is an implementation of an unbounded buffer which does not use // extra goroutines. This is typically used for passing updates from one entity @@ -28,49 +31,50 @@ import "sync" // the underlying mutex used for synchronization. // // Unbounded supports values of any type to be stored in it by using a channel -// of `interface{}`. This means that a call to Put() incurs an extra memory -// allocation, and also that users need a type assertion while reading. For -// performance critical code paths, using Unbounded is strongly discouraged and -// defining a new type specific implementation of this buffer is preferred. See +// of `any`. This means that a call to Put() incurs an extra memory allocation, +// and also that users need a type assertion while reading. For performance +// critical code paths, using Unbounded is strongly discouraged and defining a +// new type specific implementation of this buffer is preferred. See // internal/transport/transport.go for an example of this. type Unbounded struct { - c chan interface{} + c chan any closed bool + closing bool mu sync.Mutex - backlog []interface{} + backlog []any } // NewUnbounded returns a new instance of Unbounded. func NewUnbounded() *Unbounded { - return &Unbounded{c: make(chan interface{}, 1)} + return &Unbounded{c: make(chan any, 1)} } +var errBufferClosed = errors.New("Put called on closed buffer.Unbounded") + // Put adds t to the unbounded buffer. -func (b *Unbounded) Put(t interface{}) { +func (b *Unbounded) Put(t any) error { b.mu.Lock() defer b.mu.Unlock() - if b.closed { - return + if b.closing { + return errBufferClosed } if len(b.backlog) == 0 { select { case b.c <- t: - return + return nil default: } } b.backlog = append(b.backlog, t) + return nil } -// Load sends the earliest buffered data, if any, onto the read channel -// returned by Get(). Users are expected to call this every time they read a +// Load sends the earliest buffered data, if any, onto the read channel returned +// by Get(). Users are expected to call this every time they successfully read a // value from the read channel. func (b *Unbounded) Load() { b.mu.Lock() defer b.mu.Unlock() - if b.closed { - return - } if len(b.backlog) > 0 { select { case b.c <- b.backlog[0]: @@ -78,6 +82,8 @@ func (b *Unbounded) Load() { b.backlog = b.backlog[1:] default: } + } else if b.closing && !b.closed { + close(b.c) } } @@ -88,18 +94,23 @@ func (b *Unbounded) Load() { // send the next buffered value onto the channel if there is any. // // If the unbounded buffer is closed, the read channel returned by this method -// is closed. -func (b *Unbounded) Get() <-chan interface{} { +// is closed after all data is drained. +func (b *Unbounded) Get() <-chan any { return b.c } -// Close closes the unbounded buffer. +// Close closes the unbounded buffer. No subsequent data may be Put(), and the +// channel returned from Get() will be closed after all the data is read and +// Load() is called for the final time. func (b *Unbounded) Close() { b.mu.Lock() defer b.mu.Unlock() - if b.closed { + if b.closing { return } - b.closed = true - close(b.c) + b.closing = true + if len(b.backlog) == 0 { + b.closed = true + close(b.c) + } } diff --git a/vendor/google.golang.org/grpc/internal/channelz/channel.go b/vendor/google.golang.org/grpc/internal/channelz/channel.go new file mode 100644 index 000000000..d7e9e1d54 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/channel.go @@ -0,0 +1,255 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "sync/atomic" + + "google.golang.org/grpc/connectivity" +) + +// Channel represents a channel within channelz, which includes metrics and +// internal channelz data, such as channelz id, child list, etc. +type Channel struct { + Entity + // ID is the channelz id of this channel. + ID int64 + // RefName is the human readable reference string of this channel. + RefName string + + closeCalled bool + nestedChans map[int64]string + subChans map[int64]string + Parent *Channel + trace *ChannelTrace + // traceRefCount is the number of trace events that reference this channel. + // Non-zero traceRefCount means the trace of this channel cannot be deleted. + traceRefCount int32 + + ChannelMetrics ChannelMetrics +} + +// Implemented to make Channel implement the Identifier interface used for +// nesting. +func (c *Channel) channelzIdentifier() {} + +func (c *Channel) String() string { + if c.Parent == nil { + return fmt.Sprintf("Channel #%d", c.ID) + } + return fmt.Sprintf("%s Channel #%d", c.Parent, c.ID) +} + +func (c *Channel) id() int64 { + return c.ID +} + +func (c *Channel) SubChans() map[int64]string { + db.mu.RLock() + defer db.mu.RUnlock() + return copyMap(c.subChans) +} + +func (c *Channel) NestedChans() map[int64]string { + db.mu.RLock() + defer db.mu.RUnlock() + return copyMap(c.nestedChans) +} + +func (c *Channel) Trace() *ChannelTrace { + db.mu.RLock() + defer db.mu.RUnlock() + return c.trace.copy() +} + +type ChannelMetrics struct { + // The current connectivity state of the channel. + State atomic.Pointer[connectivity.State] + // The target this channel originally tried to connect to. May be absent + Target atomic.Pointer[string] + // The number of calls started on the channel. + CallsStarted atomic.Int64 + // The number of calls that have completed with an OK status. + CallsSucceeded atomic.Int64 + // The number of calls that have a completed with a non-OK status. + CallsFailed atomic.Int64 + // The last time a call was started on the channel. + LastCallStartedTimestamp atomic.Int64 +} + +// CopyFrom copies the metrics in o to c. For testing only. +func (c *ChannelMetrics) CopyFrom(o *ChannelMetrics) { + c.State.Store(o.State.Load()) + c.Target.Store(o.Target.Load()) + c.CallsStarted.Store(o.CallsStarted.Load()) + c.CallsSucceeded.Store(o.CallsSucceeded.Load()) + c.CallsFailed.Store(o.CallsFailed.Load()) + c.LastCallStartedTimestamp.Store(o.LastCallStartedTimestamp.Load()) +} + +// Equal returns true iff the metrics of c are the same as the metrics of o. +// For testing only. +func (c *ChannelMetrics) Equal(o any) bool { + oc, ok := o.(*ChannelMetrics) + if !ok { + return false + } + if (c.State.Load() == nil) != (oc.State.Load() == nil) { + return false + } + if c.State.Load() != nil && *c.State.Load() != *oc.State.Load() { + return false + } + if (c.Target.Load() == nil) != (oc.Target.Load() == nil) { + return false + } + if c.Target.Load() != nil && *c.Target.Load() != *oc.Target.Load() { + return false + } + return c.CallsStarted.Load() == oc.CallsStarted.Load() && + c.CallsFailed.Load() == oc.CallsFailed.Load() && + c.CallsSucceeded.Load() == oc.CallsSucceeded.Load() && + c.LastCallStartedTimestamp.Load() == oc.LastCallStartedTimestamp.Load() +} + +func strFromPointer(s *string) string { + if s == nil { + return "" + } + return *s +} + +func (c *ChannelMetrics) String() string { + return fmt.Sprintf("State: %v, Target: %s, CallsStarted: %v, CallsSucceeded: %v, CallsFailed: %v, LastCallStartedTimestamp: %v", + c.State.Load(), strFromPointer(c.Target.Load()), c.CallsStarted.Load(), c.CallsSucceeded.Load(), c.CallsFailed.Load(), c.LastCallStartedTimestamp.Load(), + ) +} + +func NewChannelMetricForTesting(state connectivity.State, target string, started, succeeded, failed, timestamp int64) *ChannelMetrics { + c := &ChannelMetrics{} + c.State.Store(&state) + c.Target.Store(&target) + c.CallsStarted.Store(started) + c.CallsSucceeded.Store(succeeded) + c.CallsFailed.Store(failed) + c.LastCallStartedTimestamp.Store(timestamp) + return c +} + +func (c *Channel) addChild(id int64, e entry) { + switch v := e.(type) { + case *SubChannel: + c.subChans[id] = v.RefName + case *Channel: + c.nestedChans[id] = v.RefName + default: + logger.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e) + } +} + +func (c *Channel) deleteChild(id int64) { + delete(c.subChans, id) + delete(c.nestedChans, id) + c.deleteSelfIfReady() +} + +func (c *Channel) triggerDelete() { + c.closeCalled = true + c.deleteSelfIfReady() +} + +func (c *Channel) getParentID() int64 { + if c.Parent == nil { + return -1 + } + return c.Parent.ID +} + +// deleteSelfFromTree tries to delete the channel from the channelz entry relation tree, which means +// deleting the channel reference from its parent's child list. +// +// In order for a channel to be deleted from the tree, it must meet the criteria that, removal of the +// corresponding grpc object has been invoked, and the channel does not have any children left. +// +// The returned boolean value indicates whether the channel has been successfully deleted from tree. +func (c *Channel) deleteSelfFromTree() (deleted bool) { + if !c.closeCalled || len(c.subChans)+len(c.nestedChans) != 0 { + return false + } + // not top channel + if c.Parent != nil { + c.Parent.deleteChild(c.ID) + } + return true +} + +// deleteSelfFromMap checks whether it is valid to delete the channel from the map, which means +// deleting the channel from channelz's tracking entirely. Users can no longer use id to query the +// channel, and its memory will be garbage collected. +// +// The trace reference count of the channel must be 0 in order to be deleted from the map. This is +// specified in the channel tracing gRFC that as long as some other trace has reference to an entity, +// the trace of the referenced entity must not be deleted. In order to release the resource allocated +// by grpc, the reference to the grpc object is reset to a dummy object. +// +// deleteSelfFromMap must be called after deleteSelfFromTree returns true. +// +// It returns a bool to indicate whether the channel can be safely deleted from map. +func (c *Channel) deleteSelfFromMap() (delete bool) { + return c.getTraceRefCount() == 0 +} + +// deleteSelfIfReady tries to delete the channel itself from the channelz database. +// The delete process includes two steps: +// 1. delete the channel from the entry relation tree, i.e. delete the channel reference from its +// parent's child list. +// 2. delete the channel from the map, i.e. delete the channel entirely from channelz. Lookup by id +// will return entry not found error. +func (c *Channel) deleteSelfIfReady() { + if !c.deleteSelfFromTree() { + return + } + if !c.deleteSelfFromMap() { + return + } + db.deleteEntry(c.ID) + c.trace.clear() +} + +func (c *Channel) getChannelTrace() *ChannelTrace { + return c.trace +} + +func (c *Channel) incrTraceRefCount() { + atomic.AddInt32(&c.traceRefCount, 1) +} + +func (c *Channel) decrTraceRefCount() { + atomic.AddInt32(&c.traceRefCount, -1) +} + +func (c *Channel) getTraceRefCount() int { + i := atomic.LoadInt32(&c.traceRefCount) + return int(i) +} + +func (c *Channel) getRefName() string { + return c.RefName +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/channelmap.go b/vendor/google.golang.org/grpc/internal/channelz/channelmap.go new file mode 100644 index 000000000..dfe18b089 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/channelmap.go @@ -0,0 +1,402 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "sort" + "sync" + "time" +) + +// entry represents a node in the channelz database. +type entry interface { + // addChild adds a child e, whose channelz id is id to child list + addChild(id int64, e entry) + // deleteChild deletes a child with channelz id to be id from child list + deleteChild(id int64) + // triggerDelete tries to delete self from channelz database. However, if + // child list is not empty, then deletion from the database is on hold until + // the last child is deleted from database. + triggerDelete() + // deleteSelfIfReady check whether triggerDelete() has been called before, + // and whether child list is now empty. If both conditions are met, then + // delete self from database. + deleteSelfIfReady() + // getParentID returns parent ID of the entry. 0 value parent ID means no parent. + getParentID() int64 + Entity +} + +// channelMap is the storage data structure for channelz. +// +// Methods of channelMap can be divided in two two categories with respect to +// locking. +// +// 1. Methods acquire the global lock. +// 2. Methods that can only be called when global lock is held. +// +// A second type of method need always to be called inside a first type of method. +type channelMap struct { + mu sync.RWMutex + topLevelChannels map[int64]struct{} + channels map[int64]*Channel + subChannels map[int64]*SubChannel + sockets map[int64]*Socket + servers map[int64]*Server +} + +func newChannelMap() *channelMap { + return &channelMap{ + topLevelChannels: make(map[int64]struct{}), + channels: make(map[int64]*Channel), + subChannels: make(map[int64]*SubChannel), + sockets: make(map[int64]*Socket), + servers: make(map[int64]*Server), + } +} + +func (c *channelMap) addServer(id int64, s *Server) { + c.mu.Lock() + defer c.mu.Unlock() + s.cm = c + c.servers[id] = s +} + +func (c *channelMap) addChannel(id int64, cn *Channel, isTopChannel bool, pid int64) { + c.mu.Lock() + defer c.mu.Unlock() + cn.trace.cm = c + c.channels[id] = cn + if isTopChannel { + c.topLevelChannels[id] = struct{}{} + } else if p := c.channels[pid]; p != nil { + p.addChild(id, cn) + } else { + logger.Infof("channel %d references invalid parent ID %d", id, pid) + } +} + +func (c *channelMap) addSubChannel(id int64, sc *SubChannel, pid int64) { + c.mu.Lock() + defer c.mu.Unlock() + sc.trace.cm = c + c.subChannels[id] = sc + if p := c.channels[pid]; p != nil { + p.addChild(id, sc) + } else { + logger.Infof("subchannel %d references invalid parent ID %d", id, pid) + } +} + +func (c *channelMap) addSocket(s *Socket) { + c.mu.Lock() + defer c.mu.Unlock() + s.cm = c + c.sockets[s.ID] = s + if s.Parent == nil { + logger.Infof("normal socket %d has no parent", s.ID) + } + s.Parent.(entry).addChild(s.ID, s) +} + +// removeEntry triggers the removal of an entry, which may not indeed delete the +// entry, if it has to wait on the deletion of its children and until no other +// entity's channel trace references it. It may lead to a chain of entry +// deletion. For example, deleting the last socket of a gracefully shutting down +// server will lead to the server being also deleted. +func (c *channelMap) removeEntry(id int64) { + c.mu.Lock() + defer c.mu.Unlock() + c.findEntry(id).triggerDelete() +} + +// tracedChannel represents tracing operations which are present on both +// channels and subChannels. +type tracedChannel interface { + getChannelTrace() *ChannelTrace + incrTraceRefCount() + decrTraceRefCount() + getRefName() string +} + +// c.mu must be held by the caller +func (c *channelMap) decrTraceRefCount(id int64) { + e := c.findEntry(id) + if v, ok := e.(tracedChannel); ok { + v.decrTraceRefCount() + e.deleteSelfIfReady() + } +} + +// c.mu must be held by the caller. +func (c *channelMap) findEntry(id int64) entry { + if v, ok := c.channels[id]; ok { + return v + } + if v, ok := c.subChannels[id]; ok { + return v + } + if v, ok := c.servers[id]; ok { + return v + } + if v, ok := c.sockets[id]; ok { + return v + } + return &dummyEntry{idNotFound: id} +} + +// c.mu must be held by the caller +// +// deleteEntry deletes an entry from the channelMap. Before calling this method, +// caller must check this entry is ready to be deleted, i.e removeEntry() has +// been called on it, and no children still exist. +func (c *channelMap) deleteEntry(id int64) entry { + if v, ok := c.sockets[id]; ok { + delete(c.sockets, id) + return v + } + if v, ok := c.subChannels[id]; ok { + delete(c.subChannels, id) + return v + } + if v, ok := c.channels[id]; ok { + delete(c.channels, id) + delete(c.topLevelChannels, id) + return v + } + if v, ok := c.servers[id]; ok { + delete(c.servers, id) + return v + } + return &dummyEntry{idNotFound: id} +} + +func (c *channelMap) traceEvent(id int64, desc *TraceEvent) { + c.mu.Lock() + defer c.mu.Unlock() + child := c.findEntry(id) + childTC, ok := child.(tracedChannel) + if !ok { + return + } + childTC.getChannelTrace().append(&traceEvent{Desc: desc.Desc, Severity: desc.Severity, Timestamp: time.Now()}) + if desc.Parent != nil { + parent := c.findEntry(child.getParentID()) + var chanType RefChannelType + switch child.(type) { + case *Channel: + chanType = RefChannel + case *SubChannel: + chanType = RefSubChannel + } + if parentTC, ok := parent.(tracedChannel); ok { + parentTC.getChannelTrace().append(&traceEvent{ + Desc: desc.Parent.Desc, + Severity: desc.Parent.Severity, + Timestamp: time.Now(), + RefID: id, + RefName: childTC.getRefName(), + RefType: chanType, + }) + childTC.incrTraceRefCount() + } + } +} + +type int64Slice []int64 + +func (s int64Slice) Len() int { return len(s) } +func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] } + +func copyMap(m map[int64]string) map[int64]string { + n := make(map[int64]string) + for k, v := range m { + n[k] = v + } + return n +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func (c *channelMap) getTopChannels(id int64, maxResults int) ([]*Channel, bool) { + if maxResults <= 0 { + maxResults = EntriesPerPage + } + c.mu.RLock() + defer c.mu.RUnlock() + l := int64(len(c.topLevelChannels)) + ids := make([]int64, 0, l) + + for k := range c.topLevelChannels { + ids = append(ids, k) + } + sort.Sort(int64Slice(ids)) + idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) + end := true + var t []*Channel + for _, v := range ids[idx:] { + if len(t) == maxResults { + end = false + break + } + if cn, ok := c.channels[v]; ok { + t = append(t, cn) + } + } + return t, end +} + +func (c *channelMap) getServers(id int64, maxResults int) ([]*Server, bool) { + if maxResults <= 0 { + maxResults = EntriesPerPage + } + c.mu.RLock() + defer c.mu.RUnlock() + ids := make([]int64, 0, len(c.servers)) + for k := range c.servers { + ids = append(ids, k) + } + sort.Sort(int64Slice(ids)) + idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) + end := true + var s []*Server + for _, v := range ids[idx:] { + if len(s) == maxResults { + end = false + break + } + if svr, ok := c.servers[v]; ok { + s = append(s, svr) + } + } + return s, end +} + +func (c *channelMap) getServerSockets(id int64, startID int64, maxResults int) ([]*Socket, bool) { + if maxResults <= 0 { + maxResults = EntriesPerPage + } + c.mu.RLock() + defer c.mu.RUnlock() + svr, ok := c.servers[id] + if !ok { + // server with id doesn't exist. + return nil, true + } + svrskts := svr.sockets + ids := make([]int64, 0, len(svrskts)) + sks := make([]*Socket, 0, min(len(svrskts), maxResults)) + for k := range svrskts { + ids = append(ids, k) + } + sort.Sort(int64Slice(ids)) + idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID }) + end := true + for _, v := range ids[idx:] { + if len(sks) == maxResults { + end = false + break + } + if ns, ok := c.sockets[v]; ok { + sks = append(sks, ns) + } + } + return sks, end +} + +func (c *channelMap) getChannel(id int64) *Channel { + c.mu.RLock() + defer c.mu.RUnlock() + return c.channels[id] +} + +func (c *channelMap) getSubChannel(id int64) *SubChannel { + c.mu.RLock() + defer c.mu.RUnlock() + return c.subChannels[id] +} + +func (c *channelMap) getSocket(id int64) *Socket { + c.mu.RLock() + defer c.mu.RUnlock() + return c.sockets[id] +} + +func (c *channelMap) getServer(id int64) *Server { + c.mu.RLock() + defer c.mu.RUnlock() + return c.servers[id] +} + +type dummyEntry struct { + // dummyEntry is a fake entry to handle entry not found case. + idNotFound int64 + Entity +} + +func (d *dummyEntry) String() string { + return fmt.Sprintf("non-existent entity #%d", d.idNotFound) +} + +func (d *dummyEntry) ID() int64 { return d.idNotFound } + +func (d *dummyEntry) addChild(id int64, e entry) { + // Note: It is possible for a normal program to reach here under race + // condition. For example, there could be a race between ClientConn.Close() + // info being propagated to addrConn and http2Client. ClientConn.Close() + // cancel the context and result in http2Client to error. The error info is + // then caught by transport monitor and before addrConn.tearDown() is called + // in side ClientConn.Close(). Therefore, the addrConn will create a new + // transport. And when registering the new transport in channelz, its parent + // addrConn could have already been torn down and deleted from channelz + // tracking, and thus reach the code here. + logger.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound) +} + +func (d *dummyEntry) deleteChild(id int64) { + // It is possible for a normal program to reach here under race condition. + // Refer to the example described in addChild(). + logger.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound) +} + +func (d *dummyEntry) triggerDelete() { + logger.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound) +} + +func (*dummyEntry) deleteSelfIfReady() { + // code should not reach here. deleteSelfIfReady is always called on an existing entry. +} + +func (*dummyEntry) getParentID() int64 { + return 0 +} + +// Entity is implemented by all channelz types. +type Entity interface { + isEntity() + fmt.Stringer + id() int64 +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/funcs.go b/vendor/google.golang.org/grpc/internal/channelz/funcs.go index 777cbcd79..03e24e150 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/funcs.go +++ b/vendor/google.golang.org/grpc/internal/channelz/funcs.go @@ -16,132 +16,54 @@ * */ -// Package channelz defines APIs for enabling channelz service, entry +// Package channelz defines internal APIs for enabling channelz service, entry // registration/deletion, and accessing channelz data. It also defines channelz // metric struct formats. -// -// All APIs in this package are experimental. package channelz import ( - "context" - "errors" - "fmt" - "sort" - "sync" "sync/atomic" "time" - "google.golang.org/grpc/grpclog" -) - -const ( - defaultMaxTraceEntry int32 = 30 + "google.golang.org/grpc/internal" ) var ( - db dbWrapper - idGen idGenerator - // EntryPerPage defines the number of channelz entries to be shown on a web page. - EntryPerPage = int64(50) - curState int32 - maxTraceEntry = defaultMaxTraceEntry + // IDGen is the global channelz entity ID generator. It should not be used + // outside this package except by tests. + IDGen IDGenerator + + db *channelMap = newChannelMap() + // EntriesPerPage defines the number of channelz entries to be shown on a web page. + EntriesPerPage = 50 + curState int32 ) // TurnOn turns on channelz data collection. func TurnOn() { - if !IsOn() { - db.set(newChannelMap()) - idGen.reset() - atomic.StoreInt32(&curState, 1) + atomic.StoreInt32(&curState, 1) +} + +func init() { + internal.ChannelzTurnOffForTesting = func() { + atomic.StoreInt32(&curState, 0) } } // IsOn returns whether channelz data collection is on. func IsOn() bool { - return atomic.CompareAndSwapInt32(&curState, 1, 1) -} - -// SetMaxTraceEntry sets maximum number of trace entry per entity (i.e. channel/subchannel). -// Setting it to 0 will disable channel tracing. -func SetMaxTraceEntry(i int32) { - atomic.StoreInt32(&maxTraceEntry, i) -} - -// ResetMaxTraceEntryToDefault resets the maximum number of trace entry per entity to default. -func ResetMaxTraceEntryToDefault() { - atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry) -} - -func getMaxTraceEntry() int { - i := atomic.LoadInt32(&maxTraceEntry) - return int(i) -} - -// dbWarpper wraps around a reference to internal channelz data storage, and -// provide synchronized functionality to set and get the reference. -type dbWrapper struct { - mu sync.RWMutex - DB *channelMap -} - -func (d *dbWrapper) set(db *channelMap) { - d.mu.Lock() - d.DB = db - d.mu.Unlock() -} - -func (d *dbWrapper) get() *channelMap { - d.mu.RLock() - defer d.mu.RUnlock() - return d.DB -} - -// NewChannelzStorageForTesting initializes channelz data storage and id -// generator for testing purposes. -// -// Returns a cleanup function to be invoked by the test, which waits for up to -// 10s for all channelz state to be reset by the grpc goroutines when those -// entities get closed. This cleanup function helps with ensuring that tests -// don't mess up each other. -func NewChannelzStorageForTesting() (cleanup func() error) { - db.set(newChannelMap()) - idGen.reset() - - return func() error { - cm := db.get() - if cm == nil { - return nil - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - ticker := time.NewTicker(10 * time.Millisecond) - defer ticker.Stop() - for { - cm.mu.RLock() - topLevelChannels, servers, channels, subChannels, listenSockets, normalSockets := len(cm.topLevelChannels), len(cm.servers), len(cm.channels), len(cm.subChannels), len(cm.listenSockets), len(cm.normalSockets) - cm.mu.RUnlock() - - if err := ctx.Err(); err != nil { - return fmt.Errorf("after 10s the channelz map has not been cleaned up yet, topchannels: %d, servers: %d, channels: %d, subchannels: %d, listen sockets: %d, normal sockets: %d", topLevelChannels, servers, channels, subChannels, listenSockets, normalSockets) - } - if topLevelChannels == 0 && servers == 0 && channels == 0 && subChannels == 0 && listenSockets == 0 && normalSockets == 0 { - return nil - } - <-ticker.C - } - } + return atomic.LoadInt32(&curState) == 1 } // GetTopChannels returns a slice of top channel's ChannelMetric, along with a // boolean indicating whether there's more top channels to be queried for. // -// The arg id specifies that only top channel with id at or above it will be included -// in the result. The returned slice is up to a length of the arg maxResults or -// EntryPerPage if maxResults is zero, and is sorted in ascending id order. -func GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) { - return db.get().GetTopChannels(id, maxResults) +// The arg id specifies that only top channel with id at or above it will be +// included in the result. The returned slice is up to a length of the arg +// maxResults or EntriesPerPage if maxResults is zero, and is sorted in ascending +// id order. +func GetTopChannels(id int64, maxResults int) ([]*Channel, bool) { + return db.getTopChannels(id, maxResults) } // GetServers returns a slice of server's ServerMetric, along with a @@ -149,73 +71,69 @@ func GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) { // // The arg id specifies that only server with id at or above it will be included // in the result. The returned slice is up to a length of the arg maxResults or -// EntryPerPage if maxResults is zero, and is sorted in ascending id order. -func GetServers(id int64, maxResults int64) ([]*ServerMetric, bool) { - return db.get().GetServers(id, maxResults) +// EntriesPerPage if maxResults is zero, and is sorted in ascending id order. +func GetServers(id int64, maxResults int) ([]*Server, bool) { + return db.getServers(id, maxResults) } // GetServerSockets returns a slice of server's (identified by id) normal socket's -// SocketMetric, along with a boolean indicating whether there's more sockets to +// SocketMetrics, along with a boolean indicating whether there's more sockets to // be queried for. // // The arg startID specifies that only sockets with id at or above it will be // included in the result. The returned slice is up to a length of the arg maxResults -// or EntryPerPage if maxResults is zero, and is sorted in ascending id order. -func GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) { - return db.get().GetServerSockets(id, startID, maxResults) +// or EntriesPerPage if maxResults is zero, and is sorted in ascending id order. +func GetServerSockets(id int64, startID int64, maxResults int) ([]*Socket, bool) { + return db.getServerSockets(id, startID, maxResults) } -// GetChannel returns the ChannelMetric for the channel (identified by id). -func GetChannel(id int64) *ChannelMetric { - return db.get().GetChannel(id) +// GetChannel returns the Channel for the channel (identified by id). +func GetChannel(id int64) *Channel { + return db.getChannel(id) } -// GetSubChannel returns the SubChannelMetric for the subchannel (identified by id). -func GetSubChannel(id int64) *SubChannelMetric { - return db.get().GetSubChannel(id) +// GetSubChannel returns the SubChannel for the subchannel (identified by id). +func GetSubChannel(id int64) *SubChannel { + return db.getSubChannel(id) } -// GetSocket returns the SocketInternalMetric for the socket (identified by id). -func GetSocket(id int64) *SocketMetric { - return db.get().GetSocket(id) +// GetSocket returns the Socket for the socket (identified by id). +func GetSocket(id int64) *Socket { + return db.getSocket(id) } // GetServer returns the ServerMetric for the server (identified by id). -func GetServer(id int64) *ServerMetric { - return db.get().GetServer(id) +func GetServer(id int64) *Server { + return db.getServer(id) } // RegisterChannel registers the given channel c in the channelz database with -// ref as its reference name, and adds it to the child list of its parent -// (identified by pid). pid == nil means no parent. +// target as its target and reference name, and adds it to the child list of its +// parent. parent == nil means no parent. // // Returns a unique channelz identifier assigned to this channel. // // If channelz is not turned ON, the channelz database is not mutated. -func RegisterChannel(c Channel, pid *Identifier, ref string) *Identifier { - id := idGen.genID() - var parent int64 - isTopChannel := true - if pid != nil { - isTopChannel = false - parent = pid.Int() - } +func RegisterChannel(parent *Channel, target string) *Channel { + id := IDGen.genID() if !IsOn() { - return newIdentifer(RefChannel, id, pid) + return &Channel{ID: id} } - cn := &channel{ - refName: ref, - c: c, - subChans: make(map[int64]string), + isTopChannel := parent == nil + + cn := &Channel{ + ID: id, + RefName: target, nestedChans: make(map[int64]string), - id: id, - pid: parent, - trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, + subChans: make(map[int64]string), + Parent: parent, + trace: &ChannelTrace{CreationTime: time.Now(), Events: make([]*traceEvent, 0, getMaxTraceEntry())}, } - db.get().addChannel(id, cn, isTopChannel, parent) - return newIdentifer(RefChannel, id, pid) + cn.ChannelMetrics.Target.Store(&target) + db.addChannel(id, cn, isTopChannel, cn.getParentID()) + return cn } // RegisterSubChannel registers the given subChannel c in the channelz database @@ -225,565 +143,88 @@ func RegisterChannel(c Channel, pid *Identifier, ref string) *Identifier { // Returns a unique channelz identifier assigned to this subChannel. // // If channelz is not turned ON, the channelz database is not mutated. -func RegisterSubChannel(c Channel, pid *Identifier, ref string) (*Identifier, error) { - if pid == nil { - return nil, errors.New("a SubChannel's parent id cannot be nil") +func RegisterSubChannel(parent *Channel, ref string) *SubChannel { + id := IDGen.genID() + sc := &SubChannel{ + ID: id, + RefName: ref, + parent: parent, } - id := idGen.genID() + if !IsOn() { - return newIdentifer(RefSubChannel, id, pid), nil + return sc } - sc := &subChannel{ - refName: ref, - c: c, - sockets: make(map[int64]string), - id: id, - pid: pid.Int(), - trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, - } - db.get().addSubChannel(id, sc, pid.Int()) - return newIdentifer(RefSubChannel, id, pid), nil + sc.sockets = make(map[int64]string) + sc.trace = &ChannelTrace{CreationTime: time.Now(), Events: make([]*traceEvent, 0, getMaxTraceEntry())} + db.addSubChannel(id, sc, parent.ID) + return sc } // RegisterServer registers the given server s in channelz database. It returns // the unique channelz tracking id assigned to this server. // // If channelz is not turned ON, the channelz database is not mutated. -func RegisterServer(s Server, ref string) *Identifier { - id := idGen.genID() +func RegisterServer(ref string) *Server { + id := IDGen.genID() if !IsOn() { - return newIdentifer(RefServer, id, nil) + return &Server{ID: id} } - svr := &server{ - refName: ref, - s: s, + svr := &Server{ + RefName: ref, sockets: make(map[int64]string), listenSockets: make(map[int64]string), - id: id, + ID: id, } - db.get().addServer(id, svr) - return newIdentifer(RefServer, id, nil) + db.addServer(id, svr) + return svr } -// RegisterListenSocket registers the given listen socket s in channelz database -// with ref as its reference name, and add it to the child list of its parent -// (identified by pid). It returns the unique channelz tracking id assigned to -// this listen socket. -// -// If channelz is not turned ON, the channelz database is not mutated. -func RegisterListenSocket(s Socket, pid *Identifier, ref string) (*Identifier, error) { - if pid == nil { - return nil, errors.New("a ListenSocket's parent id cannot be 0") - } - id := idGen.genID() - if !IsOn() { - return newIdentifer(RefListenSocket, id, pid), nil - } - - ls := &listenSocket{refName: ref, s: s, id: id, pid: pid.Int()} - db.get().addListenSocket(id, ls, pid.Int()) - return newIdentifer(RefListenSocket, id, pid), nil -} - -// RegisterNormalSocket registers the given normal socket s in channelz database +// RegisterSocket registers the given normal socket s in channelz database // with ref as its reference name, and adds it to the child list of its parent -// (identified by pid). It returns the unique channelz tracking id assigned to -// this normal socket. +// (identified by skt.Parent, which must be set). It returns the unique channelz +// tracking id assigned to this normal socket. // // If channelz is not turned ON, the channelz database is not mutated. -func RegisterNormalSocket(s Socket, pid *Identifier, ref string) (*Identifier, error) { - if pid == nil { - return nil, errors.New("a NormalSocket's parent id cannot be 0") - } - id := idGen.genID() - if !IsOn() { - return newIdentifer(RefNormalSocket, id, pid), nil +func RegisterSocket(skt *Socket) *Socket { + skt.ID = IDGen.genID() + if IsOn() { + db.addSocket(skt) } - - ns := &normalSocket{refName: ref, s: s, id: id, pid: pid.Int()} - db.get().addNormalSocket(id, ns, pid.Int()) - return newIdentifer(RefNormalSocket, id, pid), nil + return skt } // RemoveEntry removes an entry with unique channelz tracking id to be id from // channelz database. // // If channelz is not turned ON, this function is a no-op. -func RemoveEntry(id *Identifier) { +func RemoveEntry(id int64) { if !IsOn() { return } - db.get().removeEntry(id.Int()) + db.removeEntry(id) } -// TraceEventDesc is what the caller of AddTraceEvent should provide to describe -// the event to be added to the channel trace. -// -// The Parent field is optional. It is used for an event that will be recorded -// in the entity's parent trace. -type TraceEventDesc struct { - Desc string - Severity Severity - Parent *TraceEventDesc -} - -// AddTraceEvent adds trace related to the entity with specified id, using the -// provided TraceEventDesc. -// -// If channelz is not turned ON, this will simply log the event descriptions. -func AddTraceEvent(l grpclog.DepthLoggerV2, id *Identifier, depth int, desc *TraceEventDesc) { - // Log only the trace description associated with the bottom most entity. - switch desc.Severity { - case CtUnknown, CtInfo: - l.InfoDepth(depth+1, withParens(id)+desc.Desc) - case CtWarning: - l.WarningDepth(depth+1, withParens(id)+desc.Desc) - case CtError: - l.ErrorDepth(depth+1, withParens(id)+desc.Desc) - } - - if getMaxTraceEntry() == 0 { - return - } - if IsOn() { - db.get().traceEvent(id.Int(), desc) - } -} - -// channelMap is the storage data structure for channelz. -// Methods of channelMap can be divided in two two categories with respect to locking. -// 1. Methods acquire the global lock. -// 2. Methods that can only be called when global lock is held. -// A second type of method need always to be called inside a first type of method. -type channelMap struct { - mu sync.RWMutex - topLevelChannels map[int64]struct{} - servers map[int64]*server - channels map[int64]*channel - subChannels map[int64]*subChannel - listenSockets map[int64]*listenSocket - normalSockets map[int64]*normalSocket -} - -func newChannelMap() *channelMap { - return &channelMap{ - topLevelChannels: make(map[int64]struct{}), - channels: make(map[int64]*channel), - listenSockets: make(map[int64]*listenSocket), - normalSockets: make(map[int64]*normalSocket), - servers: make(map[int64]*server), - subChannels: make(map[int64]*subChannel), - } -} - -func (c *channelMap) addServer(id int64, s *server) { - c.mu.Lock() - s.cm = c - c.servers[id] = s - c.mu.Unlock() -} - -func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64) { - c.mu.Lock() - cn.cm = c - cn.trace.cm = c - c.channels[id] = cn - if isTopChannel { - c.topLevelChannels[id] = struct{}{} - } else { - c.findEntry(pid).addChild(id, cn) - } - c.mu.Unlock() -} - -func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64) { - c.mu.Lock() - sc.cm = c - sc.trace.cm = c - c.subChannels[id] = sc - c.findEntry(pid).addChild(id, sc) - c.mu.Unlock() -} - -func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64) { - c.mu.Lock() - ls.cm = c - c.listenSockets[id] = ls - c.findEntry(pid).addChild(id, ls) - c.mu.Unlock() -} - -func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64) { - c.mu.Lock() - ns.cm = c - c.normalSockets[id] = ns - c.findEntry(pid).addChild(id, ns) - c.mu.Unlock() -} - -// removeEntry triggers the removal of an entry, which may not indeed delete the entry, if it has to -// wait on the deletion of its children and until no other entity's channel trace references it. -// It may lead to a chain of entry deletion. For example, deleting the last socket of a gracefully -// shutting down server will lead to the server being also deleted. -func (c *channelMap) removeEntry(id int64) { - c.mu.Lock() - c.findEntry(id).triggerDelete() - c.mu.Unlock() -} - -// c.mu must be held by the caller -func (c *channelMap) decrTraceRefCount(id int64) { - e := c.findEntry(id) - if v, ok := e.(tracedChannel); ok { - v.decrTraceRefCount() - e.deleteSelfIfReady() - } -} - -// c.mu must be held by the caller. -func (c *channelMap) findEntry(id int64) entry { - var v entry - var ok bool - if v, ok = c.channels[id]; ok { - return v - } - if v, ok = c.subChannels[id]; ok { - return v - } - if v, ok = c.servers[id]; ok { - return v - } - if v, ok = c.listenSockets[id]; ok { - return v - } - if v, ok = c.normalSockets[id]; ok { - return v - } - return &dummyEntry{idNotFound: id} -} - -// c.mu must be held by the caller -// deleteEntry simply deletes an entry from the channelMap. Before calling this -// method, caller must check this entry is ready to be deleted, i.e removeEntry() -// has been called on it, and no children still exist. -// Conditionals are ordered by the expected frequency of deletion of each entity -// type, in order to optimize performance. -func (c *channelMap) deleteEntry(id int64) { - var ok bool - if _, ok = c.normalSockets[id]; ok { - delete(c.normalSockets, id) - return - } - if _, ok = c.subChannels[id]; ok { - delete(c.subChannels, id) - return - } - if _, ok = c.channels[id]; ok { - delete(c.channels, id) - delete(c.topLevelChannels, id) - return - } - if _, ok = c.listenSockets[id]; ok { - delete(c.listenSockets, id) - return - } - if _, ok = c.servers[id]; ok { - delete(c.servers, id) - return - } -} - -func (c *channelMap) traceEvent(id int64, desc *TraceEventDesc) { - c.mu.Lock() - child := c.findEntry(id) - childTC, ok := child.(tracedChannel) - if !ok { - c.mu.Unlock() - return - } - childTC.getChannelTrace().append(&TraceEvent{Desc: desc.Desc, Severity: desc.Severity, Timestamp: time.Now()}) - if desc.Parent != nil { - parent := c.findEntry(child.getParentID()) - var chanType RefChannelType - switch child.(type) { - case *channel: - chanType = RefChannel - case *subChannel: - chanType = RefSubChannel - } - if parentTC, ok := parent.(tracedChannel); ok { - parentTC.getChannelTrace().append(&TraceEvent{ - Desc: desc.Parent.Desc, - Severity: desc.Parent.Severity, - Timestamp: time.Now(), - RefID: id, - RefName: childTC.getRefName(), - RefType: chanType, - }) - childTC.incrTraceRefCount() - } - } - c.mu.Unlock() -} - -type int64Slice []int64 - -func (s int64Slice) Len() int { return len(s) } -func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] } - -func copyMap(m map[int64]string) map[int64]string { - n := make(map[int64]string) - for k, v := range m { - n[k] = v - } - return n -} - -func min(a, b int64) int64 { - if a < b { - return a - } - return b -} - -func (c *channelMap) GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) { - if maxResults <= 0 { - maxResults = EntryPerPage - } - c.mu.RLock() - l := int64(len(c.topLevelChannels)) - ids := make([]int64, 0, l) - cns := make([]*channel, 0, min(l, maxResults)) - - for k := range c.topLevelChannels { - ids = append(ids, k) - } - sort.Sort(int64Slice(ids)) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) - count := int64(0) - var end bool - var t []*ChannelMetric - for i, v := range ids[idx:] { - if count == maxResults { - break - } - if cn, ok := c.channels[v]; ok { - cns = append(cns, cn) - t = append(t, &ChannelMetric{ - NestedChans: copyMap(cn.nestedChans), - SubChans: copyMap(cn.subChans), - }) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - - for i, cn := range cns { - t[i].ChannelData = cn.c.ChannelzMetric() - t[i].ID = cn.id - t[i].RefName = cn.refName - t[i].Trace = cn.trace.dumpData() - } - return t, end -} - -func (c *channelMap) GetServers(id, maxResults int64) ([]*ServerMetric, bool) { - if maxResults <= 0 { - maxResults = EntryPerPage - } - c.mu.RLock() - l := int64(len(c.servers)) - ids := make([]int64, 0, l) - ss := make([]*server, 0, min(l, maxResults)) - for k := range c.servers { - ids = append(ids, k) - } - sort.Sort(int64Slice(ids)) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) - count := int64(0) - var end bool - var s []*ServerMetric - for i, v := range ids[idx:] { - if count == maxResults { - break - } - if svr, ok := c.servers[v]; ok { - ss = append(ss, svr) - s = append(s, &ServerMetric{ - ListenSockets: copyMap(svr.listenSockets), - }) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - - for i, svr := range ss { - s[i].ServerData = svr.s.ChannelzMetric() - s[i].ID = svr.id - s[i].RefName = svr.refName - } - return s, end -} - -func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) { - if maxResults <= 0 { - maxResults = EntryPerPage - } - var svr *server - var ok bool - c.mu.RLock() - if svr, ok = c.servers[id]; !ok { - // server with id doesn't exist. - c.mu.RUnlock() - return nil, true - } - svrskts := svr.sockets - l := int64(len(svrskts)) - ids := make([]int64, 0, l) - sks := make([]*normalSocket, 0, min(l, maxResults)) - for k := range svrskts { - ids = append(ids, k) - } - sort.Sort(int64Slice(ids)) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID }) - count := int64(0) - var end bool - for i, v := range ids[idx:] { - if count == maxResults { - break - } - if ns, ok := c.normalSockets[v]; ok { - sks = append(sks, ns) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - s := make([]*SocketMetric, 0, len(sks)) - for _, ns := range sks { - sm := &SocketMetric{} - sm.SocketData = ns.s.ChannelzMetric() - sm.ID = ns.id - sm.RefName = ns.refName - s = append(s, sm) - } - return s, end -} - -func (c *channelMap) GetChannel(id int64) *ChannelMetric { - cm := &ChannelMetric{} - var cn *channel - var ok bool - c.mu.RLock() - if cn, ok = c.channels[id]; !ok { - // channel with id doesn't exist. - c.mu.RUnlock() - return nil - } - cm.NestedChans = copyMap(cn.nestedChans) - cm.SubChans = copyMap(cn.subChans) - // cn.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of cn.c when - // holding the lock to prevent potential data race. - chanCopy := cn.c - c.mu.RUnlock() - cm.ChannelData = chanCopy.ChannelzMetric() - cm.ID = cn.id - cm.RefName = cn.refName - cm.Trace = cn.trace.dumpData() - return cm -} - -func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric { - cm := &SubChannelMetric{} - var sc *subChannel - var ok bool - c.mu.RLock() - if sc, ok = c.subChannels[id]; !ok { - // subchannel with id doesn't exist. - c.mu.RUnlock() - return nil - } - cm.Sockets = copyMap(sc.sockets) - // sc.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of sc.c when - // holding the lock to prevent potential data race. - chanCopy := sc.c - c.mu.RUnlock() - cm.ChannelData = chanCopy.ChannelzMetric() - cm.ID = sc.id - cm.RefName = sc.refName - cm.Trace = sc.trace.dumpData() - return cm -} - -func (c *channelMap) GetSocket(id int64) *SocketMetric { - sm := &SocketMetric{} - c.mu.RLock() - if ls, ok := c.listenSockets[id]; ok { - c.mu.RUnlock() - sm.SocketData = ls.s.ChannelzMetric() - sm.ID = ls.id - sm.RefName = ls.refName - return sm - } - if ns, ok := c.normalSockets[id]; ok { - c.mu.RUnlock() - sm.SocketData = ns.s.ChannelzMetric() - sm.ID = ns.id - sm.RefName = ns.refName - return sm - } - c.mu.RUnlock() - return nil -} - -func (c *channelMap) GetServer(id int64) *ServerMetric { - sm := &ServerMetric{} - var svr *server - var ok bool - c.mu.RLock() - if svr, ok = c.servers[id]; !ok { - c.mu.RUnlock() - return nil - } - sm.ListenSockets = copyMap(svr.listenSockets) - c.mu.RUnlock() - sm.ID = svr.id - sm.RefName = svr.refName - sm.ServerData = svr.s.ChannelzMetric() - return sm -} - -type idGenerator struct { +// IDGenerator is an incrementing atomic that tracks IDs for channelz entities. +type IDGenerator struct { id int64 } -func (i *idGenerator) reset() { +// Reset resets the generated ID back to zero. Should only be used at +// initialization or by tests sensitive to the ID number. +func (i *IDGenerator) Reset() { atomic.StoreInt64(&i.id, 0) } -func (i *idGenerator) genID() int64 { +func (i *IDGenerator) genID() int64 { return atomic.AddInt64(&i.id, 1) } + +// Identifier is an opaque channelz identifier used to expose channelz symbols +// outside of grpc. Currently only implemented by Channel since no other +// types require exposure outside grpc. +type Identifier interface { + Entity + channelzIdentifier() +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/id.go b/vendor/google.golang.org/grpc/internal/channelz/id.go deleted file mode 100644 index c9a27acd3..000000000 --- a/vendor/google.golang.org/grpc/internal/channelz/id.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * Copyright 2022 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package channelz - -import "fmt" - -// Identifier is an opaque identifier which uniquely identifies an entity in the -// channelz database. -type Identifier struct { - typ RefChannelType - id int64 - str string - pid *Identifier -} - -// Type returns the entity type corresponding to id. -func (id *Identifier) Type() RefChannelType { - return id.typ -} - -// Int returns the integer identifier corresponding to id. -func (id *Identifier) Int() int64 { - return id.id -} - -// String returns a string representation of the entity corresponding to id. -// -// This includes some information about the parent as well. Examples: -// Top-level channel: [Channel #channel-number] -// Nested channel: [Channel #parent-channel-number Channel #channel-number] -// Sub channel: [Channel #parent-channel SubChannel #subchannel-number] -func (id *Identifier) String() string { - return id.str -} - -// Equal returns true if other is the same as id. -func (id *Identifier) Equal(other *Identifier) bool { - if (id != nil) != (other != nil) { - return false - } - if id == nil && other == nil { - return true - } - return id.typ == other.typ && id.id == other.id && id.pid == other.pid -} - -// NewIdentifierForTesting returns a new opaque identifier to be used only for -// testing purposes. -func NewIdentifierForTesting(typ RefChannelType, id int64, pid *Identifier) *Identifier { - return newIdentifer(typ, id, pid) -} - -func newIdentifer(typ RefChannelType, id int64, pid *Identifier) *Identifier { - str := fmt.Sprintf("%s #%d", typ, id) - if pid != nil { - str = fmt.Sprintf("%s %s", pid, str) - } - return &Identifier{typ: typ, id: id, str: str, pid: pid} -} diff --git a/vendor/google.golang.org/grpc/internal/channelz/logging.go b/vendor/google.golang.org/grpc/internal/channelz/logging.go index 8e13a3d2c..ee4d72125 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/logging.go +++ b/vendor/google.golang.org/grpc/internal/channelz/logging.go @@ -26,53 +26,49 @@ import ( var logger = grpclog.Component("channelz") -func withParens(id *Identifier) string { - return "[" + id.String() + "] " -} - // Info logs and adds a trace event if channelz is on. -func Info(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Info(l grpclog.DepthLoggerV2, e Entity, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprint(args...), Severity: CtInfo, }) } // Infof logs and adds a trace event if channelz is on. -func Infof(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Infof(l grpclog.DepthLoggerV2, e Entity, format string, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprintf(format, args...), Severity: CtInfo, }) } // Warning logs and adds a trace event if channelz is on. -func Warning(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Warning(l grpclog.DepthLoggerV2, e Entity, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprint(args...), Severity: CtWarning, }) } // Warningf logs and adds a trace event if channelz is on. -func Warningf(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Warningf(l grpclog.DepthLoggerV2, e Entity, format string, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprintf(format, args...), Severity: CtWarning, }) } // Error logs and adds a trace event if channelz is on. -func Error(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Error(l grpclog.DepthLoggerV2, e Entity, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprint(args...), Severity: CtError, }) } // Errorf logs and adds a trace event if channelz is on. -func Errorf(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { - AddTraceEvent(l, id, 1, &TraceEventDesc{ +func Errorf(l grpclog.DepthLoggerV2, e Entity, format string, args ...any) { + AddTraceEvent(l, e, 1, &TraceEvent{ Desc: fmt.Sprintf(format, args...), Severity: CtError, }) diff --git a/vendor/google.golang.org/grpc/internal/channelz/server.go b/vendor/google.golang.org/grpc/internal/channelz/server.go new file mode 100644 index 000000000..cdfc49d6e --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/server.go @@ -0,0 +1,119 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "sync/atomic" +) + +// Server is the channelz representation of a server. +type Server struct { + Entity + ID int64 + RefName string + + ServerMetrics ServerMetrics + + closeCalled bool + sockets map[int64]string + listenSockets map[int64]string + cm *channelMap +} + +// ServerMetrics defines a struct containing metrics for servers. +type ServerMetrics struct { + // The number of incoming calls started on the server. + CallsStarted atomic.Int64 + // The number of incoming calls that have completed with an OK status. + CallsSucceeded atomic.Int64 + // The number of incoming calls that have a completed with a non-OK status. + CallsFailed atomic.Int64 + // The last time a call was started on the server. + LastCallStartedTimestamp atomic.Int64 +} + +// NewServerMetricsForTesting returns an initialized ServerMetrics. +func NewServerMetricsForTesting(started, succeeded, failed, timestamp int64) *ServerMetrics { + sm := &ServerMetrics{} + sm.CallsStarted.Store(started) + sm.CallsSucceeded.Store(succeeded) + sm.CallsFailed.Store(failed) + sm.LastCallStartedTimestamp.Store(timestamp) + return sm +} + +func (sm *ServerMetrics) CopyFrom(o *ServerMetrics) { + sm.CallsStarted.Store(o.CallsStarted.Load()) + sm.CallsSucceeded.Store(o.CallsSucceeded.Load()) + sm.CallsFailed.Store(o.CallsFailed.Load()) + sm.LastCallStartedTimestamp.Store(o.LastCallStartedTimestamp.Load()) +} + +// ListenSockets returns the listening sockets for s. +func (s *Server) ListenSockets() map[int64]string { + db.mu.RLock() + defer db.mu.RUnlock() + return copyMap(s.listenSockets) +} + +// String returns a printable description of s. +func (s *Server) String() string { + return fmt.Sprintf("Server #%d", s.ID) +} + +func (s *Server) id() int64 { + return s.ID +} + +func (s *Server) addChild(id int64, e entry) { + switch v := e.(type) { + case *Socket: + switch v.SocketType { + case SocketTypeNormal: + s.sockets[id] = v.RefName + case SocketTypeListen: + s.listenSockets[id] = v.RefName + } + default: + logger.Errorf("cannot add a child (id = %d) of type %T to a server", id, e) + } +} + +func (s *Server) deleteChild(id int64) { + delete(s.sockets, id) + delete(s.listenSockets, id) + s.deleteSelfIfReady() +} + +func (s *Server) triggerDelete() { + s.closeCalled = true + s.deleteSelfIfReady() +} + +func (s *Server) deleteSelfIfReady() { + if !s.closeCalled || len(s.sockets)+len(s.listenSockets) != 0 { + return + } + s.cm.deleteEntry(s.ID) +} + +func (s *Server) getParentID() int64 { + return 0 +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/socket.go b/vendor/google.golang.org/grpc/internal/channelz/socket.go new file mode 100644 index 000000000..fa64834b2 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/socket.go @@ -0,0 +1,130 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "net" + "sync/atomic" + + "google.golang.org/grpc/credentials" +) + +// SocketMetrics defines the struct that the implementor of Socket interface +// should return from ChannelzMetric(). +type SocketMetrics struct { + // The number of streams that have been started. + StreamsStarted atomic.Int64 + // The number of streams that have ended successfully: + // On client side, receiving frame with eos bit set. + // On server side, sending frame with eos bit set. + StreamsSucceeded atomic.Int64 + // The number of streams that have ended unsuccessfully: + // On client side, termination without receiving frame with eos bit set. + // On server side, termination without sending frame with eos bit set. + StreamsFailed atomic.Int64 + // The number of messages successfully sent on this socket. + MessagesSent atomic.Int64 + MessagesReceived atomic.Int64 + // The number of keep alives sent. This is typically implemented with HTTP/2 + // ping messages. + KeepAlivesSent atomic.Int64 + // The last time a stream was created by this endpoint. Usually unset for + // servers. + LastLocalStreamCreatedTimestamp atomic.Int64 + // The last time a stream was created by the remote endpoint. Usually unset + // for clients. + LastRemoteStreamCreatedTimestamp atomic.Int64 + // The last time a message was sent by this endpoint. + LastMessageSentTimestamp atomic.Int64 + // The last time a message was received by this endpoint. + LastMessageReceivedTimestamp atomic.Int64 +} + +// EphemeralSocketMetrics are metrics that change rapidly and are tracked +// outside of channelz. +type EphemeralSocketMetrics struct { + // The amount of window, granted to the local endpoint by the remote endpoint. + // This may be slightly out of date due to network latency. This does NOT + // include stream level or TCP level flow control info. + LocalFlowControlWindow int64 + // The amount of window, granted to the remote endpoint by the local endpoint. + // This may be slightly out of date due to network latency. This does NOT + // include stream level or TCP level flow control info. + RemoteFlowControlWindow int64 +} + +type SocketType string + +const ( + SocketTypeNormal = "NormalSocket" + SocketTypeListen = "ListenSocket" +) + +type Socket struct { + Entity + SocketType SocketType + ID int64 + Parent Entity + cm *channelMap + SocketMetrics SocketMetrics + EphemeralMetrics func() *EphemeralSocketMetrics + + RefName string + // The locally bound address. Immutable. + LocalAddr net.Addr + // The remote bound address. May be absent. Immutable. + RemoteAddr net.Addr + // Optional, represents the name of the remote endpoint, if different than + // the original target name. Immutable. + RemoteName string + // Immutable. + SocketOptions *SocketOptionData + // Immutable. + Security credentials.ChannelzSecurityValue +} + +func (ls *Socket) String() string { + return fmt.Sprintf("%s %s #%d", ls.Parent, ls.SocketType, ls.ID) +} + +func (ls *Socket) id() int64 { + return ls.ID +} + +func (ls *Socket) addChild(id int64, e entry) { + logger.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e) +} + +func (ls *Socket) deleteChild(id int64) { + logger.Errorf("cannot delete a child (id = %d) from a listen socket", id) +} + +func (ls *Socket) triggerDelete() { + ls.cm.deleteEntry(ls.ID) + ls.Parent.(entry).deleteChild(ls.ID) +} + +func (ls *Socket) deleteSelfIfReady() { + logger.Errorf("cannot call deleteSelfIfReady on a listen socket") +} + +func (ls *Socket) getParentID() int64 { + return ls.Parent.id() +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/subchannel.go b/vendor/google.golang.org/grpc/internal/channelz/subchannel.go new file mode 100644 index 000000000..3b88e4cba --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/subchannel.go @@ -0,0 +1,151 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "sync/atomic" +) + +// SubChannel is the channelz representation of a subchannel. +type SubChannel struct { + Entity + // ID is the channelz id of this subchannel. + ID int64 + // RefName is the human readable reference string of this subchannel. + RefName string + closeCalled bool + sockets map[int64]string + parent *Channel + trace *ChannelTrace + traceRefCount int32 + + ChannelMetrics ChannelMetrics +} + +func (sc *SubChannel) String() string { + return fmt.Sprintf("%s SubChannel #%d", sc.parent, sc.ID) +} + +func (sc *SubChannel) id() int64 { + return sc.ID +} + +func (sc *SubChannel) Sockets() map[int64]string { + db.mu.RLock() + defer db.mu.RUnlock() + return copyMap(sc.sockets) +} + +func (sc *SubChannel) Trace() *ChannelTrace { + db.mu.RLock() + defer db.mu.RUnlock() + return sc.trace.copy() +} + +func (sc *SubChannel) addChild(id int64, e entry) { + if v, ok := e.(*Socket); ok && v.SocketType == SocketTypeNormal { + sc.sockets[id] = v.RefName + } else { + logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e) + } +} + +func (sc *SubChannel) deleteChild(id int64) { + delete(sc.sockets, id) + sc.deleteSelfIfReady() +} + +func (sc *SubChannel) triggerDelete() { + sc.closeCalled = true + sc.deleteSelfIfReady() +} + +func (sc *SubChannel) getParentID() int64 { + return sc.parent.ID +} + +// deleteSelfFromTree tries to delete the subchannel from the channelz entry relation tree, which +// means deleting the subchannel reference from its parent's child list. +// +// In order for a subchannel to be deleted from the tree, it must meet the criteria that, removal of +// the corresponding grpc object has been invoked, and the subchannel does not have any children left. +// +// The returned boolean value indicates whether the channel has been successfully deleted from tree. +func (sc *SubChannel) deleteSelfFromTree() (deleted bool) { + if !sc.closeCalled || len(sc.sockets) != 0 { + return false + } + sc.parent.deleteChild(sc.ID) + return true +} + +// deleteSelfFromMap checks whether it is valid to delete the subchannel from the map, which means +// deleting the subchannel from channelz's tracking entirely. Users can no longer use id to query +// the subchannel, and its memory will be garbage collected. +// +// The trace reference count of the subchannel must be 0 in order to be deleted from the map. This is +// specified in the channel tracing gRFC that as long as some other trace has reference to an entity, +// the trace of the referenced entity must not be deleted. In order to release the resource allocated +// by grpc, the reference to the grpc object is reset to a dummy object. +// +// deleteSelfFromMap must be called after deleteSelfFromTree returns true. +// +// It returns a bool to indicate whether the channel can be safely deleted from map. +func (sc *SubChannel) deleteSelfFromMap() (delete bool) { + return sc.getTraceRefCount() == 0 +} + +// deleteSelfIfReady tries to delete the subchannel itself from the channelz database. +// The delete process includes two steps: +// 1. delete the subchannel from the entry relation tree, i.e. delete the subchannel reference from +// its parent's child list. +// 2. delete the subchannel from the map, i.e. delete the subchannel entirely from channelz. Lookup +// by id will return entry not found error. +func (sc *SubChannel) deleteSelfIfReady() { + if !sc.deleteSelfFromTree() { + return + } + if !sc.deleteSelfFromMap() { + return + } + db.deleteEntry(sc.ID) + sc.trace.clear() +} + +func (sc *SubChannel) getChannelTrace() *ChannelTrace { + return sc.trace +} + +func (sc *SubChannel) incrTraceRefCount() { + atomic.AddInt32(&sc.traceRefCount, 1) +} + +func (sc *SubChannel) decrTraceRefCount() { + atomic.AddInt32(&sc.traceRefCount, -1) +} + +func (sc *SubChannel) getTraceRefCount() int { + i := atomic.LoadInt32(&sc.traceRefCount) + return int(i) +} + +func (sc *SubChannel) getRefName() string { + return sc.RefName +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/types_linux.go b/vendor/google.golang.org/grpc/internal/channelz/syscall_linux.go similarity index 83% rename from vendor/google.golang.org/grpc/internal/channelz/types_linux.go rename to vendor/google.golang.org/grpc/internal/channelz/syscall_linux.go index 1b1c4cce3..5ac73ff83 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/types_linux.go +++ b/vendor/google.golang.org/grpc/internal/channelz/syscall_linux.go @@ -49,3 +49,17 @@ func (s *SocketOptionData) Getsockopt(fd uintptr) { s.TCPInfo = v } } + +// GetSocketOption gets the socket option info of the conn. +func GetSocketOption(socket any) *SocketOptionData { + c, ok := socket.(syscall.Conn) + if !ok { + return nil + } + data := &SocketOptionData{} + if rawConn, err := c.SyscallConn(); err == nil { + rawConn.Control(data.Getsockopt) + return data + } + return nil +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go b/vendor/google.golang.org/grpc/internal/channelz/syscall_nonlinux.go similarity index 90% rename from vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go rename to vendor/google.golang.org/grpc/internal/channelz/syscall_nonlinux.go index 8b06eed1a..d1ed8df6a 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go +++ b/vendor/google.golang.org/grpc/internal/channelz/syscall_nonlinux.go @@ -1,5 +1,4 @@ //go:build !linux -// +build !linux /* * @@ -41,3 +40,8 @@ func (s *SocketOptionData) Getsockopt(fd uintptr) { logger.Warning("Channelz: socket options are not supported on non-linux environments") }) } + +// GetSocketOption gets the socket option info of the conn. +func GetSocketOption(c any) *SocketOptionData { + return nil +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/trace.go b/vendor/google.golang.org/grpc/internal/channelz/trace.go new file mode 100644 index 000000000..36b867403 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/trace.go @@ -0,0 +1,204 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package channelz + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "google.golang.org/grpc/grpclog" +) + +const ( + defaultMaxTraceEntry int32 = 30 +) + +var maxTraceEntry = defaultMaxTraceEntry + +// SetMaxTraceEntry sets maximum number of trace entries per entity (i.e. +// channel/subchannel). Setting it to 0 will disable channel tracing. +func SetMaxTraceEntry(i int32) { + atomic.StoreInt32(&maxTraceEntry, i) +} + +// ResetMaxTraceEntryToDefault resets the maximum number of trace entries per +// entity to default. +func ResetMaxTraceEntryToDefault() { + atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry) +} + +func getMaxTraceEntry() int { + i := atomic.LoadInt32(&maxTraceEntry) + return int(i) +} + +// traceEvent is an internal representation of a single trace event +type traceEvent struct { + // Desc is a simple description of the trace event. + Desc string + // Severity states the severity of this trace event. + Severity Severity + // Timestamp is the event time. + Timestamp time.Time + // RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is + // involved in this event. + // e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside []) + RefID int64 + // RefName is the reference name for the entity that gets referenced in the event. + RefName string + // RefType indicates the referenced entity type, i.e Channel or SubChannel. + RefType RefChannelType +} + +// TraceEvent is what the caller of AddTraceEvent should provide to describe the +// event to be added to the channel trace. +// +// The Parent field is optional. It is used for an event that will be recorded +// in the entity's parent trace. +type TraceEvent struct { + Desc string + Severity Severity + Parent *TraceEvent +} + +type ChannelTrace struct { + cm *channelMap + clearCalled bool + CreationTime time.Time + EventNum int64 + mu sync.Mutex + Events []*traceEvent +} + +func (c *ChannelTrace) copy() *ChannelTrace { + return &ChannelTrace{ + CreationTime: c.CreationTime, + EventNum: c.EventNum, + Events: append(([]*traceEvent)(nil), c.Events...), + } +} + +func (c *ChannelTrace) append(e *traceEvent) { + c.mu.Lock() + if len(c.Events) == getMaxTraceEntry() { + del := c.Events[0] + c.Events = c.Events[1:] + if del.RefID != 0 { + // start recursive cleanup in a goroutine to not block the call originated from grpc. + go func() { + // need to acquire c.cm.mu lock to call the unlocked attemptCleanup func. + c.cm.mu.Lock() + c.cm.decrTraceRefCount(del.RefID) + c.cm.mu.Unlock() + }() + } + } + e.Timestamp = time.Now() + c.Events = append(c.Events, e) + c.EventNum++ + c.mu.Unlock() +} + +func (c *ChannelTrace) clear() { + if c.clearCalled { + return + } + c.clearCalled = true + c.mu.Lock() + for _, e := range c.Events { + if e.RefID != 0 { + // caller should have already held the c.cm.mu lock. + c.cm.decrTraceRefCount(e.RefID) + } + } + c.mu.Unlock() +} + +// Severity is the severity level of a trace event. +// The canonical enumeration of all valid values is here: +// https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126. +type Severity int + +const ( + // CtUnknown indicates unknown severity of a trace event. + CtUnknown Severity = iota + // CtInfo indicates info level severity of a trace event. + CtInfo + // CtWarning indicates warning level severity of a trace event. + CtWarning + // CtError indicates error level severity of a trace event. + CtError +) + +// RefChannelType is the type of the entity being referenced in a trace event. +type RefChannelType int + +const ( + // RefUnknown indicates an unknown entity type, the zero value for this type. + RefUnknown RefChannelType = iota + // RefChannel indicates the referenced entity is a Channel. + RefChannel + // RefSubChannel indicates the referenced entity is a SubChannel. + RefSubChannel + // RefServer indicates the referenced entity is a Server. + RefServer + // RefListenSocket indicates the referenced entity is a ListenSocket. + RefListenSocket + // RefNormalSocket indicates the referenced entity is a NormalSocket. + RefNormalSocket +) + +var refChannelTypeToString = map[RefChannelType]string{ + RefUnknown: "Unknown", + RefChannel: "Channel", + RefSubChannel: "SubChannel", + RefServer: "Server", + RefListenSocket: "ListenSocket", + RefNormalSocket: "NormalSocket", +} + +func (r RefChannelType) String() string { + return refChannelTypeToString[r] +} + +// AddTraceEvent adds trace related to the entity with specified id, using the +// provided TraceEventDesc. +// +// If channelz is not turned ON, this will simply log the event descriptions. +func AddTraceEvent(l grpclog.DepthLoggerV2, e Entity, depth int, desc *TraceEvent) { + // Log only the trace description associated with the bottom most entity. + d := fmt.Sprintf("[%s]%s", e, desc.Desc) + switch desc.Severity { + case CtUnknown, CtInfo: + l.InfoDepth(depth+1, d) + case CtWarning: + l.WarningDepth(depth+1, d) + case CtError: + l.ErrorDepth(depth+1, d) + } + + if getMaxTraceEntry() == 0 { + return + } + if IsOn() { + db.traceEvent(e.id(), desc) + } +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/types.go b/vendor/google.golang.org/grpc/internal/channelz/types.go deleted file mode 100644 index 7b2f350e2..000000000 --- a/vendor/google.golang.org/grpc/internal/channelz/types.go +++ /dev/null @@ -1,722 +0,0 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package channelz - -import ( - "net" - "sync" - "sync/atomic" - "time" - - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials" -) - -// entry represents a node in the channelz database. -type entry interface { - // addChild adds a child e, whose channelz id is id to child list - addChild(id int64, e entry) - // deleteChild deletes a child with channelz id to be id from child list - deleteChild(id int64) - // triggerDelete tries to delete self from channelz database. However, if child - // list is not empty, then deletion from the database is on hold until the last - // child is deleted from database. - triggerDelete() - // deleteSelfIfReady check whether triggerDelete() has been called before, and whether child - // list is now empty. If both conditions are met, then delete self from database. - deleteSelfIfReady() - // getParentID returns parent ID of the entry. 0 value parent ID means no parent. - getParentID() int64 -} - -// dummyEntry is a fake entry to handle entry not found case. -type dummyEntry struct { - idNotFound int64 -} - -func (d *dummyEntry) addChild(id int64, e entry) { - // Note: It is possible for a normal program to reach here under race condition. - // For example, there could be a race between ClientConn.Close() info being propagated - // to addrConn and http2Client. ClientConn.Close() cancel the context and result - // in http2Client to error. The error info is then caught by transport monitor - // and before addrConn.tearDown() is called in side ClientConn.Close(). Therefore, - // the addrConn will create a new transport. And when registering the new transport in - // channelz, its parent addrConn could have already been torn down and deleted - // from channelz tracking, and thus reach the code here. - logger.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound) -} - -func (d *dummyEntry) deleteChild(id int64) { - // It is possible for a normal program to reach here under race condition. - // Refer to the example described in addChild(). - logger.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound) -} - -func (d *dummyEntry) triggerDelete() { - logger.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound) -} - -func (*dummyEntry) deleteSelfIfReady() { - // code should not reach here. deleteSelfIfReady is always called on an existing entry. -} - -func (*dummyEntry) getParentID() int64 { - return 0 -} - -// ChannelMetric defines the info channelz provides for a specific Channel, which -// includes ChannelInternalMetric and channelz-specific data, such as channelz id, -// child list, etc. -type ChannelMetric struct { - // ID is the channelz id of this channel. - ID int64 - // RefName is the human readable reference string of this channel. - RefName string - // ChannelData contains channel internal metric reported by the channel through - // ChannelzMetric(). - ChannelData *ChannelInternalMetric - // NestedChans tracks the nested channel type children of this channel in the format of - // a map from nested channel channelz id to corresponding reference string. - NestedChans map[int64]string - // SubChans tracks the subchannel type children of this channel in the format of a - // map from subchannel channelz id to corresponding reference string. - SubChans map[int64]string - // Sockets tracks the socket type children of this channel in the format of a map - // from socket channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow channel having sockets directly, - // therefore, this is field is unused. - Sockets map[int64]string - // Trace contains the most recent traced events. - Trace *ChannelTrace -} - -// SubChannelMetric defines the info channelz provides for a specific SubChannel, -// which includes ChannelInternalMetric and channelz-specific data, such as -// channelz id, child list, etc. -type SubChannelMetric struct { - // ID is the channelz id of this subchannel. - ID int64 - // RefName is the human readable reference string of this subchannel. - RefName string - // ChannelData contains subchannel internal metric reported by the subchannel - // through ChannelzMetric(). - ChannelData *ChannelInternalMetric - // NestedChans tracks the nested channel type children of this subchannel in the format of - // a map from nested channel channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow subchannel to have nested channels - // as children, therefore, this field is unused. - NestedChans map[int64]string - // SubChans tracks the subchannel type children of this subchannel in the format of a - // map from subchannel channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow subchannel to have subchannels - // as children, therefore, this field is unused. - SubChans map[int64]string - // Sockets tracks the socket type children of this subchannel in the format of a map - // from socket channelz id to corresponding reference string. - Sockets map[int64]string - // Trace contains the most recent traced events. - Trace *ChannelTrace -} - -// ChannelInternalMetric defines the struct that the implementor of Channel interface -// should return from ChannelzMetric(). -type ChannelInternalMetric struct { - // current connectivity state of the channel. - State connectivity.State - // The target this channel originally tried to connect to. May be absent - Target string - // The number of calls started on the channel. - CallsStarted int64 - // The number of calls that have completed with an OK status. - CallsSucceeded int64 - // The number of calls that have a completed with a non-OK status. - CallsFailed int64 - // The last time a call was started on the channel. - LastCallStartedTimestamp time.Time -} - -// ChannelTrace stores traced events on a channel/subchannel and related info. -type ChannelTrace struct { - // EventNum is the number of events that ever got traced (i.e. including those that have been deleted) - EventNum int64 - // CreationTime is the creation time of the trace. - CreationTime time.Time - // Events stores the most recent trace events (up to $maxTraceEntry, newer event will overwrite the - // oldest one) - Events []*TraceEvent -} - -// TraceEvent represent a single trace event -type TraceEvent struct { - // Desc is a simple description of the trace event. - Desc string - // Severity states the severity of this trace event. - Severity Severity - // Timestamp is the event time. - Timestamp time.Time - // RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is - // involved in this event. - // e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside []) - RefID int64 - // RefName is the reference name for the entity that gets referenced in the event. - RefName string - // RefType indicates the referenced entity type, i.e Channel or SubChannel. - RefType RefChannelType -} - -// Channel is the interface that should be satisfied in order to be tracked by -// channelz as Channel or SubChannel. -type Channel interface { - ChannelzMetric() *ChannelInternalMetric -} - -type dummyChannel struct{} - -func (d *dummyChannel) ChannelzMetric() *ChannelInternalMetric { - return &ChannelInternalMetric{} -} - -type channel struct { - refName string - c Channel - closeCalled bool - nestedChans map[int64]string - subChans map[int64]string - id int64 - pid int64 - cm *channelMap - trace *channelTrace - // traceRefCount is the number of trace events that reference this channel. - // Non-zero traceRefCount means the trace of this channel cannot be deleted. - traceRefCount int32 -} - -func (c *channel) addChild(id int64, e entry) { - switch v := e.(type) { - case *subChannel: - c.subChans[id] = v.refName - case *channel: - c.nestedChans[id] = v.refName - default: - logger.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e) - } -} - -func (c *channel) deleteChild(id int64) { - delete(c.subChans, id) - delete(c.nestedChans, id) - c.deleteSelfIfReady() -} - -func (c *channel) triggerDelete() { - c.closeCalled = true - c.deleteSelfIfReady() -} - -func (c *channel) getParentID() int64 { - return c.pid -} - -// deleteSelfFromTree tries to delete the channel from the channelz entry relation tree, which means -// deleting the channel reference from its parent's child list. -// -// In order for a channel to be deleted from the tree, it must meet the criteria that, removal of the -// corresponding grpc object has been invoked, and the channel does not have any children left. -// -// The returned boolean value indicates whether the channel has been successfully deleted from tree. -func (c *channel) deleteSelfFromTree() (deleted bool) { - if !c.closeCalled || len(c.subChans)+len(c.nestedChans) != 0 { - return false - } - // not top channel - if c.pid != 0 { - c.cm.findEntry(c.pid).deleteChild(c.id) - } - return true -} - -// deleteSelfFromMap checks whether it is valid to delete the channel from the map, which means -// deleting the channel from channelz's tracking entirely. Users can no longer use id to query the -// channel, and its memory will be garbage collected. -// -// The trace reference count of the channel must be 0 in order to be deleted from the map. This is -// specified in the channel tracing gRFC that as long as some other trace has reference to an entity, -// the trace of the referenced entity must not be deleted. In order to release the resource allocated -// by grpc, the reference to the grpc object is reset to a dummy object. -// -// deleteSelfFromMap must be called after deleteSelfFromTree returns true. -// -// It returns a bool to indicate whether the channel can be safely deleted from map. -func (c *channel) deleteSelfFromMap() (delete bool) { - if c.getTraceRefCount() != 0 { - c.c = &dummyChannel{} - return false - } - return true -} - -// deleteSelfIfReady tries to delete the channel itself from the channelz database. -// The delete process includes two steps: -// 1. delete the channel from the entry relation tree, i.e. delete the channel reference from its -// parent's child list. -// 2. delete the channel from the map, i.e. delete the channel entirely from channelz. Lookup by id -// will return entry not found error. -func (c *channel) deleteSelfIfReady() { - if !c.deleteSelfFromTree() { - return - } - if !c.deleteSelfFromMap() { - return - } - c.cm.deleteEntry(c.id) - c.trace.clear() -} - -func (c *channel) getChannelTrace() *channelTrace { - return c.trace -} - -func (c *channel) incrTraceRefCount() { - atomic.AddInt32(&c.traceRefCount, 1) -} - -func (c *channel) decrTraceRefCount() { - atomic.AddInt32(&c.traceRefCount, -1) -} - -func (c *channel) getTraceRefCount() int { - i := atomic.LoadInt32(&c.traceRefCount) - return int(i) -} - -func (c *channel) getRefName() string { - return c.refName -} - -type subChannel struct { - refName string - c Channel - closeCalled bool - sockets map[int64]string - id int64 - pid int64 - cm *channelMap - trace *channelTrace - traceRefCount int32 -} - -func (sc *subChannel) addChild(id int64, e entry) { - if v, ok := e.(*normalSocket); ok { - sc.sockets[id] = v.refName - } else { - logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e) - } -} - -func (sc *subChannel) deleteChild(id int64) { - delete(sc.sockets, id) - sc.deleteSelfIfReady() -} - -func (sc *subChannel) triggerDelete() { - sc.closeCalled = true - sc.deleteSelfIfReady() -} - -func (sc *subChannel) getParentID() int64 { - return sc.pid -} - -// deleteSelfFromTree tries to delete the subchannel from the channelz entry relation tree, which -// means deleting the subchannel reference from its parent's child list. -// -// In order for a subchannel to be deleted from the tree, it must meet the criteria that, removal of -// the corresponding grpc object has been invoked, and the subchannel does not have any children left. -// -// The returned boolean value indicates whether the channel has been successfully deleted from tree. -func (sc *subChannel) deleteSelfFromTree() (deleted bool) { - if !sc.closeCalled || len(sc.sockets) != 0 { - return false - } - sc.cm.findEntry(sc.pid).deleteChild(sc.id) - return true -} - -// deleteSelfFromMap checks whether it is valid to delete the subchannel from the map, which means -// deleting the subchannel from channelz's tracking entirely. Users can no longer use id to query -// the subchannel, and its memory will be garbage collected. -// -// The trace reference count of the subchannel must be 0 in order to be deleted from the map. This is -// specified in the channel tracing gRFC that as long as some other trace has reference to an entity, -// the trace of the referenced entity must not be deleted. In order to release the resource allocated -// by grpc, the reference to the grpc object is reset to a dummy object. -// -// deleteSelfFromMap must be called after deleteSelfFromTree returns true. -// -// It returns a bool to indicate whether the channel can be safely deleted from map. -func (sc *subChannel) deleteSelfFromMap() (delete bool) { - if sc.getTraceRefCount() != 0 { - // free the grpc struct (i.e. addrConn) - sc.c = &dummyChannel{} - return false - } - return true -} - -// deleteSelfIfReady tries to delete the subchannel itself from the channelz database. -// The delete process includes two steps: -// 1. delete the subchannel from the entry relation tree, i.e. delete the subchannel reference from -// its parent's child list. -// 2. delete the subchannel from the map, i.e. delete the subchannel entirely from channelz. Lookup -// by id will return entry not found error. -func (sc *subChannel) deleteSelfIfReady() { - if !sc.deleteSelfFromTree() { - return - } - if !sc.deleteSelfFromMap() { - return - } - sc.cm.deleteEntry(sc.id) - sc.trace.clear() -} - -func (sc *subChannel) getChannelTrace() *channelTrace { - return sc.trace -} - -func (sc *subChannel) incrTraceRefCount() { - atomic.AddInt32(&sc.traceRefCount, 1) -} - -func (sc *subChannel) decrTraceRefCount() { - atomic.AddInt32(&sc.traceRefCount, -1) -} - -func (sc *subChannel) getTraceRefCount() int { - i := atomic.LoadInt32(&sc.traceRefCount) - return int(i) -} - -func (sc *subChannel) getRefName() string { - return sc.refName -} - -// SocketMetric defines the info channelz provides for a specific Socket, which -// includes SocketInternalMetric and channelz-specific data, such as channelz id, etc. -type SocketMetric struct { - // ID is the channelz id of this socket. - ID int64 - // RefName is the human readable reference string of this socket. - RefName string - // SocketData contains socket internal metric reported by the socket through - // ChannelzMetric(). - SocketData *SocketInternalMetric -} - -// SocketInternalMetric defines the struct that the implementor of Socket interface -// should return from ChannelzMetric(). -type SocketInternalMetric struct { - // The number of streams that have been started. - StreamsStarted int64 - // The number of streams that have ended successfully: - // On client side, receiving frame with eos bit set. - // On server side, sending frame with eos bit set. - StreamsSucceeded int64 - // The number of streams that have ended unsuccessfully: - // On client side, termination without receiving frame with eos bit set. - // On server side, termination without sending frame with eos bit set. - StreamsFailed int64 - // The number of messages successfully sent on this socket. - MessagesSent int64 - MessagesReceived int64 - // The number of keep alives sent. This is typically implemented with HTTP/2 - // ping messages. - KeepAlivesSent int64 - // The last time a stream was created by this endpoint. Usually unset for - // servers. - LastLocalStreamCreatedTimestamp time.Time - // The last time a stream was created by the remote endpoint. Usually unset - // for clients. - LastRemoteStreamCreatedTimestamp time.Time - // The last time a message was sent by this endpoint. - LastMessageSentTimestamp time.Time - // The last time a message was received by this endpoint. - LastMessageReceivedTimestamp time.Time - // The amount of window, granted to the local endpoint by the remote endpoint. - // This may be slightly out of date due to network latency. This does NOT - // include stream level or TCP level flow control info. - LocalFlowControlWindow int64 - // The amount of window, granted to the remote endpoint by the local endpoint. - // This may be slightly out of date due to network latency. This does NOT - // include stream level or TCP level flow control info. - RemoteFlowControlWindow int64 - // The locally bound address. - LocalAddr net.Addr - // The remote bound address. May be absent. - RemoteAddr net.Addr - // Optional, represents the name of the remote endpoint, if different than - // the original target name. - RemoteName string - SocketOptions *SocketOptionData - Security credentials.ChannelzSecurityValue -} - -// Socket is the interface that should be satisfied in order to be tracked by -// channelz as Socket. -type Socket interface { - ChannelzMetric() *SocketInternalMetric -} - -type listenSocket struct { - refName string - s Socket - id int64 - pid int64 - cm *channelMap -} - -func (ls *listenSocket) addChild(id int64, e entry) { - logger.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e) -} - -func (ls *listenSocket) deleteChild(id int64) { - logger.Errorf("cannot delete a child (id = %d) from a listen socket", id) -} - -func (ls *listenSocket) triggerDelete() { - ls.cm.deleteEntry(ls.id) - ls.cm.findEntry(ls.pid).deleteChild(ls.id) -} - -func (ls *listenSocket) deleteSelfIfReady() { - logger.Errorf("cannot call deleteSelfIfReady on a listen socket") -} - -func (ls *listenSocket) getParentID() int64 { - return ls.pid -} - -type normalSocket struct { - refName string - s Socket - id int64 - pid int64 - cm *channelMap -} - -func (ns *normalSocket) addChild(id int64, e entry) { - logger.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e) -} - -func (ns *normalSocket) deleteChild(id int64) { - logger.Errorf("cannot delete a child (id = %d) from a normal socket", id) -} - -func (ns *normalSocket) triggerDelete() { - ns.cm.deleteEntry(ns.id) - ns.cm.findEntry(ns.pid).deleteChild(ns.id) -} - -func (ns *normalSocket) deleteSelfIfReady() { - logger.Errorf("cannot call deleteSelfIfReady on a normal socket") -} - -func (ns *normalSocket) getParentID() int64 { - return ns.pid -} - -// ServerMetric defines the info channelz provides for a specific Server, which -// includes ServerInternalMetric and channelz-specific data, such as channelz id, -// child list, etc. -type ServerMetric struct { - // ID is the channelz id of this server. - ID int64 - // RefName is the human readable reference string of this server. - RefName string - // ServerData contains server internal metric reported by the server through - // ChannelzMetric(). - ServerData *ServerInternalMetric - // ListenSockets tracks the listener socket type children of this server in the - // format of a map from socket channelz id to corresponding reference string. - ListenSockets map[int64]string -} - -// ServerInternalMetric defines the struct that the implementor of Server interface -// should return from ChannelzMetric(). -type ServerInternalMetric struct { - // The number of incoming calls started on the server. - CallsStarted int64 - // The number of incoming calls that have completed with an OK status. - CallsSucceeded int64 - // The number of incoming calls that have a completed with a non-OK status. - CallsFailed int64 - // The last time a call was started on the server. - LastCallStartedTimestamp time.Time -} - -// Server is the interface to be satisfied in order to be tracked by channelz as -// Server. -type Server interface { - ChannelzMetric() *ServerInternalMetric -} - -type server struct { - refName string - s Server - closeCalled bool - sockets map[int64]string - listenSockets map[int64]string - id int64 - cm *channelMap -} - -func (s *server) addChild(id int64, e entry) { - switch v := e.(type) { - case *normalSocket: - s.sockets[id] = v.refName - case *listenSocket: - s.listenSockets[id] = v.refName - default: - logger.Errorf("cannot add a child (id = %d) of type %T to a server", id, e) - } -} - -func (s *server) deleteChild(id int64) { - delete(s.sockets, id) - delete(s.listenSockets, id) - s.deleteSelfIfReady() -} - -func (s *server) triggerDelete() { - s.closeCalled = true - s.deleteSelfIfReady() -} - -func (s *server) deleteSelfIfReady() { - if !s.closeCalled || len(s.sockets)+len(s.listenSockets) != 0 { - return - } - s.cm.deleteEntry(s.id) -} - -func (s *server) getParentID() int64 { - return 0 -} - -type tracedChannel interface { - getChannelTrace() *channelTrace - incrTraceRefCount() - decrTraceRefCount() - getRefName() string -} - -type channelTrace struct { - cm *channelMap - createdTime time.Time - eventCount int64 - mu sync.Mutex - events []*TraceEvent -} - -func (c *channelTrace) append(e *TraceEvent) { - c.mu.Lock() - if len(c.events) == getMaxTraceEntry() { - del := c.events[0] - c.events = c.events[1:] - if del.RefID != 0 { - // start recursive cleanup in a goroutine to not block the call originated from grpc. - go func() { - // need to acquire c.cm.mu lock to call the unlocked attemptCleanup func. - c.cm.mu.Lock() - c.cm.decrTraceRefCount(del.RefID) - c.cm.mu.Unlock() - }() - } - } - e.Timestamp = time.Now() - c.events = append(c.events, e) - c.eventCount++ - c.mu.Unlock() -} - -func (c *channelTrace) clear() { - c.mu.Lock() - for _, e := range c.events { - if e.RefID != 0 { - // caller should have already held the c.cm.mu lock. - c.cm.decrTraceRefCount(e.RefID) - } - } - c.mu.Unlock() -} - -// Severity is the severity level of a trace event. -// The canonical enumeration of all valid values is here: -// https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126. -type Severity int - -const ( - // CtUnknown indicates unknown severity of a trace event. - CtUnknown Severity = iota - // CtInfo indicates info level severity of a trace event. - CtInfo - // CtWarning indicates warning level severity of a trace event. - CtWarning - // CtError indicates error level severity of a trace event. - CtError -) - -// RefChannelType is the type of the entity being referenced in a trace event. -type RefChannelType int - -const ( - // RefUnknown indicates an unknown entity type, the zero value for this type. - RefUnknown RefChannelType = iota - // RefChannel indicates the referenced entity is a Channel. - RefChannel - // RefSubChannel indicates the referenced entity is a SubChannel. - RefSubChannel - // RefServer indicates the referenced entity is a Server. - RefServer - // RefListenSocket indicates the referenced entity is a ListenSocket. - RefListenSocket - // RefNormalSocket indicates the referenced entity is a NormalSocket. - RefNormalSocket -) - -var refChannelTypeToString = map[RefChannelType]string{ - RefUnknown: "Unknown", - RefChannel: "Channel", - RefSubChannel: "SubChannel", - RefServer: "Server", - RefListenSocket: "ListenSocket", - RefNormalSocket: "NormalSocket", -} - -func (r RefChannelType) String() string { - return refChannelTypeToString[r] -} - -func (c *channelTrace) dumpData() *ChannelTrace { - c.mu.Lock() - ct := &ChannelTrace{EventNum: c.eventCount, CreationTime: c.createdTime} - ct.Events = c.events[:len(c.events)] - c.mu.Unlock() - return ct -} diff --git a/vendor/google.golang.org/grpc/internal/credentials/credentials.go b/vendor/google.golang.org/grpc/internal/credentials/credentials.go index 32c9b5903..9deee7f65 100644 --- a/vendor/google.golang.org/grpc/internal/credentials/credentials.go +++ b/vendor/google.golang.org/grpc/internal/credentials/credentials.go @@ -25,12 +25,12 @@ import ( type requestInfoKey struct{} // NewRequestInfoContext creates a context with ri. -func NewRequestInfoContext(ctx context.Context, ri interface{}) context.Context { +func NewRequestInfoContext(ctx context.Context, ri any) context.Context { return context.WithValue(ctx, requestInfoKey{}, ri) } // RequestInfoFromContext extracts the RequestInfo from ctx. -func RequestInfoFromContext(ctx context.Context) interface{} { +func RequestInfoFromContext(ctx context.Context) any { return ctx.Value(requestInfoKey{}) } @@ -39,11 +39,11 @@ func RequestInfoFromContext(ctx context.Context) interface{} { type clientHandshakeInfoKey struct{} // ClientHandshakeInfoFromContext extracts the ClientHandshakeInfo from ctx. -func ClientHandshakeInfoFromContext(ctx context.Context) interface{} { +func ClientHandshakeInfoFromContext(ctx context.Context) any { return ctx.Value(clientHandshakeInfoKey{}) } // NewClientHandshakeInfoContext creates a context with chi. -func NewClientHandshakeInfoContext(ctx context.Context, chi interface{}) context.Context { +func NewClientHandshakeInfoContext(ctx context.Context, chi any) context.Context { return context.WithValue(ctx, clientHandshakeInfoKey{}, chi) } diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 80fd5c7d2..685a3cb41 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -36,10 +36,13 @@ var ( // "GRPC_RING_HASH_CAP". This does not override the default bounds // checking which NACKs configs specifying ring sizes > 8*1024*1024 (~8M). RingHashCap = uint64FromEnv("GRPC_RING_HASH_CAP", 4096, 1, 8*1024*1024) - // PickFirstLBConfig is set if we should support configuration of the - // pick_first LB policy, which can be enabled by setting the environment - // variable "GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG" to "true". - PickFirstLBConfig = boolFromEnv("GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG", false) + // LeastRequestLB is set if we should support the least_request_experimental + // LB policy, which can be enabled by setting the environment variable + // "GRPC_EXPERIMENTAL_ENABLE_LEAST_REQUEST" to "true". + LeastRequestLB = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_LEAST_REQUEST", false) + // ALTSMaxConcurrentHandshakes is the maximum number of concurrent ALTS + // handshakes that can be performed. + ALTSMaxConcurrentHandshakes = uint64FromEnv("GRPC_ALTS_MAX_CONCURRENT_HANDSHAKES", 100, 1, 100) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index 02b4b6a1c..29f234acb 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -50,46 +50,7 @@ var ( // // When both bootstrap FileName and FileContent are set, FileName is used. XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv) - // XDSRingHash indicates whether ring hash support is enabled, which can be - // disabled by setting the environment variable - // "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false". - XDSRingHash = boolFromEnv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", true) - // XDSClientSideSecurity is used to control processing of security - // configuration on the client-side. - // - // Note that there is no env var protection for the server-side because we - // have a brand new API on the server-side and users explicitly need to use - // the new API to get security integration on the server. - XDSClientSideSecurity = boolFromEnv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", true) - // XDSAggregateAndDNS indicates whether processing of aggregated cluster and - // DNS cluster is enabled, which can be disabled by setting the environment - // variable "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" - // to "false". - XDSAggregateAndDNS = boolFromEnv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER", true) - - // XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled, - // which can be disabled by setting the environment variable - // "GRPC_XDS_EXPERIMENTAL_RBAC" to "false". - XDSRBAC = boolFromEnv("GRPC_XDS_EXPERIMENTAL_RBAC", true) - // XDSOutlierDetection indicates whether outlier detection support is - // enabled, which can be disabled by setting the environment variable - // "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" to "false". - XDSOutlierDetection = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION", true) - // XDSFederation indicates whether federation support is enabled, which can - // be enabled by setting the environment variable - // "GRPC_EXPERIMENTAL_XDS_FEDERATION" to "true". - XDSFederation = boolFromEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", true) - - // XDSRLS indicates whether processing of Cluster Specifier plugins and - // support for the RLS CLuster Specifier is enabled, which can be disabled by - // setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to - // "false". - XDSRLS = boolFromEnv("GRPC_EXPERIMENTAL_XDS_RLS_LB", true) // C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing. C2PResolverTestOnlyTrafficDirectorURI = os.Getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI") - // XDSCustomLBPolicy indicates whether Custom LB Policies are enabled, which - // can be disabled by setting the environment variable - // "GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG" to "false". - XDSCustomLBPolicy = boolFromEnv("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG", true) ) diff --git a/vendor/google.golang.org/grpc/internal/experimental.go b/vendor/google.golang.org/grpc/internal/experimental.go new file mode 100644 index 000000000..7f7044e17 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/experimental.go @@ -0,0 +1,28 @@ +/* + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package internal + +var ( + // WithRecvBufferPool is implemented by the grpc package and returns a dial + // option to configure a shared buffer pool for a grpc.ClientConn. + WithRecvBufferPool any // func (grpc.SharedBufferPool) grpc.DialOption + + // RecvBufferPool is implemented by the grpc package and returns a server + // option to configure a shared buffer pool for a grpc.Server. + RecvBufferPool any // func (grpc.SharedBufferPool) grpc.ServerOption +) diff --git a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go index b68e26a36..bfc45102a 100644 --- a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go +++ b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go @@ -30,7 +30,7 @@ var Logger LoggerV2 var DepthLogger DepthLoggerV2 // InfoDepth logs to the INFO log at the specified depth. -func InfoDepth(depth int, args ...interface{}) { +func InfoDepth(depth int, args ...any) { if DepthLogger != nil { DepthLogger.InfoDepth(depth, args...) } else { @@ -39,7 +39,7 @@ func InfoDepth(depth int, args ...interface{}) { } // WarningDepth logs to the WARNING log at the specified depth. -func WarningDepth(depth int, args ...interface{}) { +func WarningDepth(depth int, args ...any) { if DepthLogger != nil { DepthLogger.WarningDepth(depth, args...) } else { @@ -48,7 +48,7 @@ func WarningDepth(depth int, args ...interface{}) { } // ErrorDepth logs to the ERROR log at the specified depth. -func ErrorDepth(depth int, args ...interface{}) { +func ErrorDepth(depth int, args ...any) { if DepthLogger != nil { DepthLogger.ErrorDepth(depth, args...) } else { @@ -57,7 +57,7 @@ func ErrorDepth(depth int, args ...interface{}) { } // FatalDepth logs to the FATAL log at the specified depth. -func FatalDepth(depth int, args ...interface{}) { +func FatalDepth(depth int, args ...any) { if DepthLogger != nil { DepthLogger.FatalDepth(depth, args...) } else { @@ -71,35 +71,35 @@ func FatalDepth(depth int, args ...interface{}) { // is defined here to avoid a circular dependency. type LoggerV2 interface { // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. - Info(args ...interface{}) + Info(args ...any) // Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println. - Infoln(args ...interface{}) + Infoln(args ...any) // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. - Infof(format string, args ...interface{}) + Infof(format string, args ...any) // Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print. - Warning(args ...interface{}) + Warning(args ...any) // Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println. - Warningln(args ...interface{}) + Warningln(args ...any) // Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. - Warningf(format string, args ...interface{}) + Warningf(format string, args ...any) // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. - Error(args ...interface{}) + Error(args ...any) // Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println. - Errorln(args ...interface{}) + Errorln(args ...any) // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. - Errorf(format string, args ...interface{}) + Errorf(format string, args ...any) // Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatal(args ...interface{}) + Fatal(args ...any) // Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatalln(args ...interface{}) + Fatalln(args ...any) // Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. // gRPC ensures that all Fatal logs will exit with os.Exit(1). // Implementations may also call os.Exit() with a non-zero exit code. - Fatalf(format string, args ...interface{}) + Fatalf(format string, args ...any) // V reports whether verbosity level l is at least the requested verbose level. V(l int) bool } @@ -116,11 +116,11 @@ type LoggerV2 interface { // later release. type DepthLoggerV2 interface { // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println. - InfoDepth(depth int, args ...interface{}) + InfoDepth(depth int, args ...any) // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println. - WarningDepth(depth int, args ...interface{}) + WarningDepth(depth int, args ...any) // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println. - ErrorDepth(depth int, args ...interface{}) + ErrorDepth(depth int, args ...any) // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println. - FatalDepth(depth int, args ...interface{}) + FatalDepth(depth int, args ...any) } diff --git a/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go b/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go index 02224b42c..faa998de7 100644 --- a/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go +++ b/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go @@ -31,7 +31,7 @@ type PrefixLogger struct { } // Infof does info logging. -func (pl *PrefixLogger) Infof(format string, args ...interface{}) { +func (pl *PrefixLogger) Infof(format string, args ...any) { if pl != nil { // Handle nil, so the tests can pass in a nil logger. format = pl.prefix + format @@ -42,7 +42,7 @@ func (pl *PrefixLogger) Infof(format string, args ...interface{}) { } // Warningf does warning logging. -func (pl *PrefixLogger) Warningf(format string, args ...interface{}) { +func (pl *PrefixLogger) Warningf(format string, args ...any) { if pl != nil { format = pl.prefix + format pl.logger.WarningDepth(1, fmt.Sprintf(format, args...)) @@ -52,7 +52,7 @@ func (pl *PrefixLogger) Warningf(format string, args ...interface{}) { } // Errorf does error logging. -func (pl *PrefixLogger) Errorf(format string, args ...interface{}) { +func (pl *PrefixLogger) Errorf(format string, args ...any) { if pl != nil { format = pl.prefix + format pl.logger.ErrorDepth(1, fmt.Sprintf(format, args...)) @@ -62,7 +62,7 @@ func (pl *PrefixLogger) Errorf(format string, args ...interface{}) { } // Debugf does info logging at verbose level 2. -func (pl *PrefixLogger) Debugf(format string, args ...interface{}) { +func (pl *PrefixLogger) Debugf(format string, args ...any) { // TODO(6044): Refactor interfaces LoggerV2 and DepthLogger, and maybe // rewrite PrefixLogger a little to ensure that we don't use the global // `Logger` here, and instead use the `logger` field. diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go index d08e3e907..0126d6b51 100644 --- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go +++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go @@ -1,3 +1,8 @@ +//go:build !go1.21 + +// TODO: when this file is deleted (after Go 1.20 support is dropped), delete +// all of grpcrand and call the rand package directly. + /* * * Copyright 2018 gRPC authors. @@ -80,6 +85,13 @@ func Uint32() uint32 { return r.Uint32() } +// ExpFloat64 implements rand.ExpFloat64 on the grpcrand global source. +func ExpFloat64() float64 { + mu.Lock() + defer mu.Unlock() + return r.ExpFloat64() +} + // Shuffle implements rand.Shuffle on the grpcrand global source. var Shuffle = func(n int, f func(int, int)) { mu.Lock() diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go new file mode 100644 index 000000000..c37299af1 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go @@ -0,0 +1,73 @@ +//go:build go1.21 + +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package grpcrand implements math/rand functions in a concurrent-safe way +// with a global random source, independent of math/rand's global source. +package grpcrand + +import "math/rand" + +// This implementation will be used for Go version 1.21 or newer. +// For older versions, the original implementation with mutex will be used. + +// Int implements rand.Int on the grpcrand global source. +func Int() int { + return rand.Int() +} + +// Int63n implements rand.Int63n on the grpcrand global source. +func Int63n(n int64) int64 { + return rand.Int63n(n) +} + +// Intn implements rand.Intn on the grpcrand global source. +func Intn(n int) int { + return rand.Intn(n) +} + +// Int31n implements rand.Int31n on the grpcrand global source. +func Int31n(n int32) int32 { + return rand.Int31n(n) +} + +// Float64 implements rand.Float64 on the grpcrand global source. +func Float64() float64 { + return rand.Float64() +} + +// Uint64 implements rand.Uint64 on the grpcrand global source. +func Uint64() uint64 { + return rand.Uint64() +} + +// Uint32 implements rand.Uint32 on the grpcrand global source. +func Uint32() uint32 { + return rand.Uint32() +} + +// ExpFloat64 implements rand.ExpFloat64 on the grpcrand global source. +func ExpFloat64() float64 { + return rand.ExpFloat64() +} + +// Shuffle implements rand.Shuffle on the grpcrand global source. +var Shuffle = func(n int, f func(int, int)) { + rand.Shuffle(n, f) +} diff --git a/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go index 37b8d4117..f7f40a16a 100644 --- a/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go +++ b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go @@ -20,7 +20,6 @@ package grpcsync import ( "context" - "sync" "google.golang.org/grpc/internal/buffer" ) @@ -32,14 +31,12 @@ import ( // // This type is safe for concurrent access. type CallbackSerializer struct { - // Done is closed once the serializer is shut down completely, i.e all + // done is closed once the serializer is shut down completely, i.e all // scheduled callbacks are executed and the serializer has deallocated all // its resources. - Done chan struct{} + done chan struct{} callbacks *buffer.Unbounded - closedMu sync.Mutex - closed bool } // NewCallbackSerializer returns a new CallbackSerializer instance. The provided @@ -48,12 +45,12 @@ type CallbackSerializer struct { // callbacks will be added once this context is canceled, and any pending un-run // callbacks will be executed before the serializer is shut down. func NewCallbackSerializer(ctx context.Context) *CallbackSerializer { - t := &CallbackSerializer{ - Done: make(chan struct{}), + cs := &CallbackSerializer{ + done: make(chan struct{}), callbacks: buffer.NewUnbounded(), } - go t.run(ctx) - return t + go cs.run(ctx) + return cs } // Schedule adds a callback to be scheduled after existing callbacks are run. @@ -64,56 +61,40 @@ func NewCallbackSerializer(ctx context.Context) *CallbackSerializer { // Return value indicates if the callback was successfully added to the list of // callbacks to be executed by the serializer. It is not possible to add // callbacks once the context passed to NewCallbackSerializer is cancelled. -func (t *CallbackSerializer) Schedule(f func(ctx context.Context)) bool { - t.closedMu.Lock() - defer t.closedMu.Unlock() - - if t.closed { - return false - } - t.callbacks.Put(f) - return true +func (cs *CallbackSerializer) Schedule(f func(ctx context.Context)) bool { + return cs.callbacks.Put(f) == nil } -func (t *CallbackSerializer) run(ctx context.Context) { - var backlog []func(context.Context) +func (cs *CallbackSerializer) run(ctx context.Context) { + defer close(cs.done) - defer close(t.Done) + // TODO: when Go 1.21 is the oldest supported version, this loop and Close + // can be replaced with: + // + // context.AfterFunc(ctx, cs.callbacks.Close) for ctx.Err() == nil { select { case <-ctx.Done(): // Do nothing here. Next iteration of the for loop will not happen, // since ctx.Err() would be non-nil. - case callback, ok := <-t.callbacks.Get(): - if !ok { - return - } - t.callbacks.Load() - callback.(func(ctx context.Context))(ctx) + case cb := <-cs.callbacks.Get(): + cs.callbacks.Load() + cb.(func(context.Context))(ctx) } } - // Fetch pending callbacks if any, and execute them before returning from - // this method and closing t.Done. - t.closedMu.Lock() - t.closed = true - backlog = t.fetchPendingCallbacks() - t.callbacks.Close() - t.closedMu.Unlock() - for _, b := range backlog { - b(ctx) + // Close the buffer to prevent new callbacks from being added. + cs.callbacks.Close() + + // Run all pending callbacks. + for cb := range cs.callbacks.Get() { + cs.callbacks.Load() + cb.(func(context.Context))(ctx) } } -func (t *CallbackSerializer) fetchPendingCallbacks() []func(context.Context) { - var backlog []func(context.Context) - for { - select { - case b := <-t.callbacks.Get(): - backlog = append(backlog, b.(func(context.Context))) - t.callbacks.Load() - default: - return backlog - } - } +// Done returns a channel that is closed after the context passed to +// NewCallbackSerializer is canceled and all callbacks have been executed. +func (cs *CallbackSerializer) Done() <-chan struct{} { + return cs.done } diff --git a/vendor/google.golang.org/grpc/internal/grpcsync/pubsub.go b/vendor/google.golang.org/grpc/internal/grpcsync/pubsub.go new file mode 100644 index 000000000..aef8cec1a --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/grpcsync/pubsub.go @@ -0,0 +1,121 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpcsync + +import ( + "context" + "sync" +) + +// Subscriber represents an entity that is subscribed to messages published on +// a PubSub. It wraps the callback to be invoked by the PubSub when a new +// message is published. +type Subscriber interface { + // OnMessage is invoked when a new message is published. Implementations + // must not block in this method. + OnMessage(msg any) +} + +// PubSub is a simple one-to-many publish-subscribe system that supports +// messages of arbitrary type. It guarantees that messages are delivered in +// the same order in which they were published. +// +// Publisher invokes the Publish() method to publish new messages, while +// subscribers interested in receiving these messages register a callback +// via the Subscribe() method. +// +// Once a PubSub is stopped, no more messages can be published, but any pending +// published messages will be delivered to the subscribers. Done may be used +// to determine when all published messages have been delivered. +type PubSub struct { + cs *CallbackSerializer + + // Access to the below fields are guarded by this mutex. + mu sync.Mutex + msg any + subscribers map[Subscriber]bool +} + +// NewPubSub returns a new PubSub instance. Users should cancel the +// provided context to shutdown the PubSub. +func NewPubSub(ctx context.Context) *PubSub { + return &PubSub{ + cs: NewCallbackSerializer(ctx), + subscribers: map[Subscriber]bool{}, + } +} + +// Subscribe registers the provided Subscriber to the PubSub. +// +// If the PubSub contains a previously published message, the Subscriber's +// OnMessage() callback will be invoked asynchronously with the existing +// message to begin with, and subsequently for every newly published message. +// +// The caller is responsible for invoking the returned cancel function to +// unsubscribe itself from the PubSub. +func (ps *PubSub) Subscribe(sub Subscriber) (cancel func()) { + ps.mu.Lock() + defer ps.mu.Unlock() + + ps.subscribers[sub] = true + + if ps.msg != nil { + msg := ps.msg + ps.cs.Schedule(func(context.Context) { + ps.mu.Lock() + defer ps.mu.Unlock() + if !ps.subscribers[sub] { + return + } + sub.OnMessage(msg) + }) + } + + return func() { + ps.mu.Lock() + defer ps.mu.Unlock() + delete(ps.subscribers, sub) + } +} + +// Publish publishes the provided message to the PubSub, and invokes +// callbacks registered by subscribers asynchronously. +func (ps *PubSub) Publish(msg any) { + ps.mu.Lock() + defer ps.mu.Unlock() + + ps.msg = msg + for sub := range ps.subscribers { + s := sub + ps.cs.Schedule(func(context.Context) { + ps.mu.Lock() + defer ps.mu.Unlock() + if !ps.subscribers[s] { + return + } + s.OnMessage(msg) + }) + } +} + +// Done returns a channel that is closed after the context passed to NewPubSub +// is canceled and all updates have been sent to subscribers. +func (ps *PubSub) Done() <-chan struct{} { + return ps.cs.Done() +} diff --git a/vendor/google.golang.org/grpc/internal/idle/idle.go b/vendor/google.golang.org/grpc/internal/idle/idle.go new file mode 100644 index 000000000..fe49cb74c --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/idle/idle.go @@ -0,0 +1,278 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package idle contains a component for managing idleness (entering and exiting) +// based on RPC activity. +package idle + +import ( + "fmt" + "math" + "sync" + "sync/atomic" + "time" +) + +// For overriding in unit tests. +var timeAfterFunc = func(d time.Duration, f func()) *time.Timer { + return time.AfterFunc(d, f) +} + +// Enforcer is the functionality provided by grpc.ClientConn to enter +// and exit from idle mode. +type Enforcer interface { + ExitIdleMode() error + EnterIdleMode() +} + +// Manager implements idleness detection and calls the configured Enforcer to +// enter/exit idle mode when appropriate. Must be created by NewManager. +type Manager struct { + // State accessed atomically. + lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. + activeCallsCount int32 // Count of active RPCs; -math.MaxInt32 means channel is idle or is trying to get there. + activeSinceLastTimerCheck int32 // Boolean; True if there was an RPC since the last timer callback. + closed int32 // Boolean; True when the manager is closed. + + // Can be accessed without atomics or mutex since these are set at creation + // time and read-only after that. + enforcer Enforcer // Functionality provided by grpc.ClientConn. + timeout time.Duration + + // idleMu is used to guarantee mutual exclusion in two scenarios: + // - Opposing intentions: + // - a: Idle timeout has fired and handleIdleTimeout() is trying to put + // the channel in idle mode because the channel has been inactive. + // - b: At the same time an RPC is made on the channel, and OnCallBegin() + // is trying to prevent the channel from going idle. + // - Competing intentions: + // - The channel is in idle mode and there are multiple RPCs starting at + // the same time, all trying to move the channel out of idle. Only one + // of them should succeed in doing so, while the other RPCs should + // piggyback on the first one and be successfully handled. + idleMu sync.RWMutex + actuallyIdle bool + timer *time.Timer +} + +// NewManager creates a new idleness manager implementation for the +// given idle timeout. It begins in idle mode. +func NewManager(enforcer Enforcer, timeout time.Duration) *Manager { + return &Manager{ + enforcer: enforcer, + timeout: timeout, + actuallyIdle: true, + activeCallsCount: -math.MaxInt32, + } +} + +// resetIdleTimerLocked resets the idle timer to the given duration. Called +// when exiting idle mode or when the timer fires and we need to reset it. +func (m *Manager) resetIdleTimerLocked(d time.Duration) { + if m.isClosed() || m.timeout == 0 || m.actuallyIdle { + return + } + + // It is safe to ignore the return value from Reset() because this method is + // only ever called from the timer callback or when exiting idle mode. + if m.timer != nil { + m.timer.Stop() + } + m.timer = timeAfterFunc(d, m.handleIdleTimeout) +} + +func (m *Manager) resetIdleTimer(d time.Duration) { + m.idleMu.Lock() + defer m.idleMu.Unlock() + m.resetIdleTimerLocked(d) +} + +// handleIdleTimeout is the timer callback that is invoked upon expiry of the +// configured idle timeout. The channel is considered inactive if there are no +// ongoing calls and no RPC activity since the last time the timer fired. +func (m *Manager) handleIdleTimeout() { + if m.isClosed() { + return + } + + if atomic.LoadInt32(&m.activeCallsCount) > 0 { + m.resetIdleTimer(m.timeout) + return + } + + // There has been activity on the channel since we last got here. Reset the + // timer and return. + if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { + // Set the timer to fire after a duration of idle timeout, calculated + // from the time the most recent RPC completed. + atomic.StoreInt32(&m.activeSinceLastTimerCheck, 0) + m.resetIdleTimer(time.Duration(atomic.LoadInt64(&m.lastCallEndTime)-time.Now().UnixNano()) + m.timeout) + return + } + + // Now that we've checked that there has been no activity, attempt to enter + // idle mode, which is very likely to succeed. + if m.tryEnterIdleMode() { + // Successfully entered idle mode. No timer needed until we exit idle. + return + } + + // Failed to enter idle mode due to a concurrent RPC that kept the channel + // active, or because of an error from the channel. Undo the attempt to + // enter idle, and reset the timer to try again later. + m.resetIdleTimer(m.timeout) +} + +// tryEnterIdleMode instructs the channel to enter idle mode. But before +// that, it performs a last minute check to ensure that no new RPC has come in, +// making the channel active. +// +// Return value indicates whether or not the channel moved to idle mode. +// +// Holds idleMu which ensures mutual exclusion with exitIdleMode. +func (m *Manager) tryEnterIdleMode() bool { + // Setting the activeCallsCount to -math.MaxInt32 indicates to OnCallBegin() + // that the channel is either in idle mode or is trying to get there. + if !atomic.CompareAndSwapInt32(&m.activeCallsCount, 0, -math.MaxInt32) { + // This CAS operation can fail if an RPC started after we checked for + // activity in the timer handler, or one was ongoing from before the + // last time the timer fired, or if a test is attempting to enter idle + // mode without checking. In all cases, abort going into idle mode. + return false + } + // N.B. if we fail to enter idle mode after this, we must re-add + // math.MaxInt32 to m.activeCallsCount. + + m.idleMu.Lock() + defer m.idleMu.Unlock() + + if atomic.LoadInt32(&m.activeCallsCount) != -math.MaxInt32 { + // We raced and lost to a new RPC. Very rare, but stop entering idle. + atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) + return false + } + if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { + // A very short RPC could have come in (and also finished) after we + // checked for calls count and activity in handleIdleTimeout(), but + // before the CAS operation. So, we need to check for activity again. + atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) + return false + } + + // No new RPCs have come in since we set the active calls count value to + // -math.MaxInt32. And since we have the lock, it is safe to enter idle mode + // unconditionally now. + m.enforcer.EnterIdleMode() + m.actuallyIdle = true + return true +} + +func (m *Manager) EnterIdleModeForTesting() { + m.tryEnterIdleMode() +} + +// OnCallBegin is invoked at the start of every RPC. +func (m *Manager) OnCallBegin() error { + if m.isClosed() { + return nil + } + + if atomic.AddInt32(&m.activeCallsCount, 1) > 0 { + // Channel is not idle now. Set the activity bit and allow the call. + atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) + return nil + } + + // Channel is either in idle mode or is in the process of moving to idle + // mode. Attempt to exit idle mode to allow this RPC. + if err := m.ExitIdleMode(); err != nil { + // Undo the increment to calls count, and return an error causing the + // RPC to fail. + atomic.AddInt32(&m.activeCallsCount, -1) + return err + } + + atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) + return nil +} + +// ExitIdleMode instructs m to call the enforcer's ExitIdleMode and update m's +// internal state. +func (m *Manager) ExitIdleMode() error { + // Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. + m.idleMu.Lock() + defer m.idleMu.Unlock() + + if m.isClosed() || !m.actuallyIdle { + // This can happen in three scenarios: + // - handleIdleTimeout() set the calls count to -math.MaxInt32 and called + // tryEnterIdleMode(). But before the latter could grab the lock, an RPC + // came in and OnCallBegin() noticed that the calls count is negative. + // - Channel is in idle mode, and multiple new RPCs come in at the same + // time, all of them notice a negative calls count in OnCallBegin and get + // here. The first one to get the lock would got the channel to exit idle. + // - Channel is not in idle mode, and the user calls Connect which calls + // m.ExitIdleMode. + // + // In any case, there is nothing to do here. + return nil + } + + if err := m.enforcer.ExitIdleMode(); err != nil { + return fmt.Errorf("failed to exit idle mode: %w", err) + } + + // Undo the idle entry process. This also respects any new RPC attempts. + atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) + m.actuallyIdle = false + + // Start a new timer to fire after the configured idle timeout. + m.resetIdleTimerLocked(m.timeout) + return nil +} + +// OnCallEnd is invoked at the end of every RPC. +func (m *Manager) OnCallEnd() { + if m.isClosed() { + return + } + + // Record the time at which the most recent call finished. + atomic.StoreInt64(&m.lastCallEndTime, time.Now().UnixNano()) + + // Decrement the active calls count. This count can temporarily go negative + // when the timer callback is in the process of moving the channel to idle + // mode, but one or more RPCs come in and complete before the timer callback + // can get done with the process of moving to idle mode. + atomic.AddInt32(&m.activeCallsCount, -1) +} + +func (m *Manager) isClosed() bool { + return atomic.LoadInt32(&m.closed) == 1 +} + +func (m *Manager) Close() { + atomic.StoreInt32(&m.closed, 1) + + m.idleMu.Lock() + if m.timer != nil { + m.timer.Stop() + m.timer = nil + } + m.idleMu.Unlock() +} diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 42ff39c84..48d24bdb4 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -30,7 +30,7 @@ import ( var ( // WithHealthCheckFunc is set by dialoptions.go - WithHealthCheckFunc interface{} // func (HealthChecker) DialOption + WithHealthCheckFunc any // func (HealthChecker) DialOption // HealthCheckFunc is used to provide client-side LB channel health checking HealthCheckFunc HealthChecker // BalancerUnregister is exported by package balancer to unregister a balancer. @@ -38,8 +38,12 @@ var ( // KeepaliveMinPingTime is the minimum ping interval. This must be 10s by // default, but tests may wish to set it lower for convenience. KeepaliveMinPingTime = 10 * time.Second + // KeepaliveMinServerPingTime is the minimum ping interval for servers. + // This must be 1s by default, but tests may wish to set it lower for + // convenience. + KeepaliveMinServerPingTime = time.Second // ParseServiceConfig parses a JSON representation of the service config. - ParseServiceConfig interface{} // func(string) *serviceconfig.ParseResult + ParseServiceConfig any // func(string) *serviceconfig.ParseResult // EqualServiceConfigForTesting is for testing service config generation and // parsing. Both a and b should be returned by ParseServiceConfig. // This function compares the config without rawJSON stripped, in case the @@ -49,33 +53,33 @@ var ( // given name. This is set by package certprovider for use from xDS // bootstrap code while parsing certificate provider configs in the // bootstrap file. - GetCertificateProviderBuilder interface{} // func(string) certprovider.Builder + GetCertificateProviderBuilder any // func(string) certprovider.Builder // GetXDSHandshakeInfoForTesting returns a pointer to the xds.HandshakeInfo // stored in the passed in attributes. This is set by // credentials/xds/xds.go. - GetXDSHandshakeInfoForTesting interface{} // func (*attributes.Attributes) *xds.HandshakeInfo + GetXDSHandshakeInfoForTesting any // func (*attributes.Attributes) *unsafe.Pointer // GetServerCredentials returns the transport credentials configured on a // gRPC server. An xDS-enabled server needs to know what type of credentials // is configured on the underlying gRPC server. This is set by server.go. - GetServerCredentials interface{} // func (*grpc.Server) credentials.TransportCredentials + GetServerCredentials any // func (*grpc.Server) credentials.TransportCredentials // CanonicalString returns the canonical string of the code defined here: // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md. // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - CanonicalString interface{} // func (codes.Code) string - // DrainServerTransports initiates a graceful close of existing connections - // on a gRPC server accepted on the provided listener address. An - // xDS-enabled server invokes this method on a grpc.Server when a particular - // listener moves to "not-serving" mode. - DrainServerTransports interface{} // func(*grpc.Server, string) + CanonicalString any // func (codes.Code) string + // IsRegisteredMethod returns whether the passed in method is registered as + // a method on the server. + IsRegisteredMethod any // func(*grpc.Server, string) bool + // ServerFromContext returns the server from the context. + ServerFromContext any // func(context.Context) *grpc.Server // AddGlobalServerOptions adds an array of ServerOption that will be // effective globally for newly created servers. The priority will be: 1. // user-provided; 2. this method; 3. default values. // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - AddGlobalServerOptions interface{} // func(opt ...ServerOption) + AddGlobalServerOptions any // func(opt ...ServerOption) // ClearGlobalServerOptions clears the array of extra ServerOption. This // method is useful in testing and benchmarking. // @@ -88,14 +92,14 @@ var ( // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - AddGlobalDialOptions interface{} // func(opt ...DialOption) + AddGlobalDialOptions any // func(opt ...DialOption) // DisableGlobalDialOptions returns a DialOption that prevents the // ClientConn from applying the global DialOptions (set via // AddGlobalDialOptions). // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - DisableGlobalDialOptions interface{} // func() grpc.DialOption + DisableGlobalDialOptions any // func() grpc.DialOption // ClearGlobalDialOptions clears the array of extra DialOption. This // method is useful in testing and benchmarking. // @@ -104,23 +108,26 @@ var ( ClearGlobalDialOptions func() // JoinDialOptions combines the dial options passed as arguments into a // single dial option. - JoinDialOptions interface{} // func(...grpc.DialOption) grpc.DialOption + JoinDialOptions any // func(...grpc.DialOption) grpc.DialOption // JoinServerOptions combines the server options passed as arguments into a // single server option. - JoinServerOptions interface{} // func(...grpc.ServerOption) grpc.ServerOption + JoinServerOptions any // func(...grpc.ServerOption) grpc.ServerOption // WithBinaryLogger returns a DialOption that specifies the binary logger // for a ClientConn. // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - WithBinaryLogger interface{} // func(binarylog.Logger) grpc.DialOption + WithBinaryLogger any // func(binarylog.Logger) grpc.DialOption // BinaryLogger returns a ServerOption that can set the binary logger for a // server. // // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. - BinaryLogger interface{} // func(binarylog.Logger) grpc.ServerOption + BinaryLogger any // func(binarylog.Logger) grpc.ServerOption + + // SubscribeToConnectivityStateChanges adds a grpcsync.Subscriber to a provided grpc.ClientConn + SubscribeToConnectivityStateChanges any // func(*grpc.ClientConn, grpcsync.Subscriber) // NewXDSResolverWithConfigForTesting creates a new xds resolver builder using // the provided xds bootstrap config instead of the global configuration from @@ -131,7 +138,7 @@ var ( // // This function should ONLY be used for testing and may not work with some // other features, including the CSDS service. - NewXDSResolverWithConfigForTesting interface{} // func([]byte) (resolver.Builder, error) + NewXDSResolverWithConfigForTesting any // func([]byte) (resolver.Builder, error) // RegisterRLSClusterSpecifierPluginForTesting registers the RLS Cluster // Specifier Plugin for testing purposes, regardless of the XDSRLS environment @@ -163,7 +170,36 @@ var ( UnregisterRBACHTTPFilterForTesting func() // ORCAAllowAnyMinReportingInterval is for examples/orca use ONLY. - ORCAAllowAnyMinReportingInterval interface{} // func(so *orca.ServiceOptions) + ORCAAllowAnyMinReportingInterval any // func(so *orca.ServiceOptions) + + // GRPCResolverSchemeExtraMetadata determines when gRPC will add extra + // metadata to RPCs. + GRPCResolverSchemeExtraMetadata string = "xds" + + // EnterIdleModeForTesting gets the ClientConn to enter IDLE mode. + EnterIdleModeForTesting any // func(*grpc.ClientConn) + + // ExitIdleModeForTesting gets the ClientConn to exit IDLE mode. + ExitIdleModeForTesting any // func(*grpc.ClientConn) error + + ChannelzTurnOffForTesting func() + + // TriggerXDSResourceNameNotFoundForTesting triggers the resource-not-found + // error for a given resource type and name. This is usually triggered when + // the associated watch timer fires. For testing purposes, having this + // function makes events more predictable than relying on timer events. + TriggerXDSResourceNameNotFoundForTesting any // func(func(xdsresource.Type, string), string, string) error + + // TriggerXDSResourceNameNotFoundClient invokes the testing xDS Client + // singleton to invoke resource not found for a resource type name and + // resource name. + TriggerXDSResourceNameNotFoundClient any // func(string, string) error + + // FromOutgoingContextRaw returns the un-merged, intermediary contents of metadata.rawMD. + FromOutgoingContextRaw any // func(context.Context) (metadata.MD, [][]string, bool) + + // UserSetDefaultScheme is set to true if the user has overridden the default resolver scheme. + UserSetDefaultScheme bool = false ) // HealthChecker defines the signature of the client-side LB channel health checking function. @@ -174,7 +210,7 @@ var ( // // The health checking protocol is defined at: // https://github.com/grpc/grpc/blob/master/doc/health-checking.md -type HealthChecker func(ctx context.Context, newStream func(string) (interface{}, error), setConnectivityState func(connectivity.State, error), serviceName string) error +type HealthChecker func(ctx context.Context, newStream func(string) (any, error), setConnectivityState func(connectivity.State, error), serviceName string) error const ( // CredsBundleModeFallback switches GoogleDefaultCreds to fallback mode. diff --git a/vendor/google.golang.org/grpc/internal/metadata/metadata.go b/vendor/google.golang.org/grpc/internal/metadata/metadata.go index c82e608e0..900bfb716 100644 --- a/vendor/google.golang.org/grpc/internal/metadata/metadata.go +++ b/vendor/google.golang.org/grpc/internal/metadata/metadata.go @@ -35,7 +35,7 @@ const mdKey = mdKeyType("grpc.internal.address.metadata") type mdValue metadata.MD -func (m mdValue) Equal(o interface{}) bool { +func (m mdValue) Equal(o any) bool { om, ok := o.(mdValue) if !ok { return false diff --git a/vendor/google.golang.org/grpc/internal/pretty/pretty.go b/vendor/google.golang.org/grpc/internal/pretty/pretty.go index 0177af4b5..dbee7a60d 100644 --- a/vendor/google.golang.org/grpc/internal/pretty/pretty.go +++ b/vendor/google.golang.org/grpc/internal/pretty/pretty.go @@ -24,10 +24,8 @@ import ( "encoding/json" "fmt" - "github.com/golang/protobuf/jsonpb" - protov1 "github.com/golang/protobuf/proto" "google.golang.org/protobuf/encoding/protojson" - protov2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/protoadapt" ) const jsonIndent = " " @@ -35,22 +33,15 @@ const jsonIndent = " " // ToJSON marshals the input into a json string. // // If marshal fails, it falls back to fmt.Sprintf("%+v"). -func ToJSON(e interface{}) string { - switch ee := e.(type) { - case protov1.Message: - mm := jsonpb.Marshaler{Indent: jsonIndent} - ret, err := mm.MarshalToString(ee) - if err != nil { - // This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2 - // messages are not imported, and this will fail because the message - // is not found. - return fmt.Sprintf("%+v", ee) - } - return ret - case protov2.Message: +func ToJSON(e any) string { + if ee, ok := e.(protoadapt.MessageV1); ok { + e = protoadapt.MessageV2Of(ee) + } + + if ee, ok := e.(protoadapt.MessageV2); ok { mm := protojson.MarshalOptions{ - Multiline: true, Indent: jsonIndent, + Multiline: true, } ret, err := mm.Marshal(ee) if err != nil { @@ -60,13 +51,13 @@ func ToJSON(e interface{}) string { return fmt.Sprintf("%+v", ee) } return string(ret) - default: - ret, err := json.MarshalIndent(ee, "", jsonIndent) - if err != nil { - return fmt.Sprintf("%+v", ee) - } - return string(ret) } + + ret, err := json.MarshalIndent(e, "", jsonIndent) + if err != nil { + return fmt.Sprintf("%+v", e) + } + return string(ret) } // FormatJSON formats the input json bytes with indentation. diff --git a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go index c7a18a948..f0603871c 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go +++ b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go @@ -92,7 +92,7 @@ type ClientStream interface { // calling RecvMsg on the same stream at the same time, but it is not safe // to call SendMsg on the same stream in different goroutines. It is also // not safe to call CloseSend concurrently with SendMsg. - SendMsg(m interface{}) error + SendMsg(m any) error // RecvMsg blocks until it receives a message into m or the stream is // done. It returns io.EOF when the stream completes successfully. On // any other error, the stream is aborted and the error contains the RPC @@ -101,7 +101,7 @@ type ClientStream interface { // It is safe to have a goroutine calling SendMsg and another goroutine // calling RecvMsg on the same stream at the same time, but it is not // safe to call RecvMsg on the same stream in different goroutines. - RecvMsg(m interface{}) error + RecvMsg(m any) error } // ClientInterceptor is an interceptor for gRPC client streams. diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go index 09a667f33..abab35e25 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go +++ b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go @@ -23,7 +23,6 @@ package dns import ( "context" "encoding/json" - "errors" "fmt" "net" "os" @@ -37,6 +36,7 @@ import ( "google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpcrand" + "google.golang.org/grpc/internal/resolver/dns/internal" "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" ) @@ -45,55 +45,46 @@ import ( // addresses from SRV records. Must not be changed after init time. var EnableSRVLookups = false -var logger = grpclog.Component("dns") +// ResolvingTimeout specifies the maximum duration for a DNS resolution request. +// If the timeout expires before a response is received, the request will be canceled. +// +// It is recommended to set this value at application startup. Avoid modifying this variable +// after initialization as it's not thread-safe for concurrent modification. +var ResolvingTimeout = 30 * time.Second -// Globals to stub out in tests. TODO: Perhaps these two can be combined into a -// single variable for testing the resolver? -var ( - newTimer = time.NewTimer - newTimerDNSResRate = time.NewTimer -) +var logger = grpclog.Component("dns") func init() { resolver.Register(NewBuilder()) + internal.TimeAfterFunc = time.After + internal.NewNetResolver = newNetResolver + internal.AddressDialer = addressDialer } const ( defaultPort = "443" defaultDNSSvrPort = "53" golang = "GO" - // txtPrefix is the prefix string to be prepended to the host name for txt record lookup. + // txtPrefix is the prefix string to be prepended to the host name for txt + // record lookup. txtPrefix = "_grpc_config." // In DNS, service config is encoded in a TXT record via the mechanism // described in RFC-1464 using the attribute name grpc_config. txtAttribute = "grpc_config=" ) -var ( - errMissingAddr = errors.New("dns resolver: missing address") - - // Addresses ending with a colon that is supposed to be the separator - // between host and port is not allowed. E.g. "::" is a valid address as - // it is an IPv6 address (host only) and "[::]:" is invalid as it ends with - // a colon as the host and port separator - errEndsWithColon = errors.New("dns resolver: missing port after port-separator colon") -) - -var ( - defaultResolver netResolver = net.DefaultResolver - // To prevent excessive re-resolution, we enforce a rate limit on DNS - // resolution requests. - minDNSResRate = 30 * time.Second -) - -var customAuthorityDialler = func(authority string) func(ctx context.Context, network, address string) (net.Conn, error) { - return func(ctx context.Context, network, address string) (net.Conn, error) { +var addressDialer = func(address string) func(context.Context, string, string) (net.Conn, error) { + return func(ctx context.Context, network, _ string) (net.Conn, error) { var dialer net.Dialer - return dialer.DialContext(ctx, network, authority) + return dialer.DialContext(ctx, network, address) } } -var customAuthorityResolver = func(authority string) (netResolver, error) { +var newNetResolver = func(authority string) (internal.NetResolver, error) { + if authority == "" { + return net.DefaultResolver, nil + } + host, port, err := parseTarget(authority, defaultDNSSvrPort) if err != nil { return nil, err @@ -103,7 +94,7 @@ var customAuthorityResolver = func(authority string) (netResolver, error) { return &net.Resolver{ PreferGo: true, - Dial: customAuthorityDialler(authorityWithPort), + Dial: internal.AddressDialer(authorityWithPort), }, nil } @@ -114,7 +105,8 @@ func NewBuilder() resolver.Builder { type dnsBuilder struct{} -// Build creates and starts a DNS resolver that watches the name resolution of the target. +// Build creates and starts a DNS resolver that watches the name resolution of +// the target. func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { host, port, err := parseTarget(target.Endpoint(), defaultPort) if err != nil { @@ -140,13 +132,9 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts disableServiceConfig: opts.DisableServiceConfig, } - if target.URL.Host == "" { - d.resolver = defaultResolver - } else { - d.resolver, err = customAuthorityResolver(target.URL.Host) - if err != nil { - return nil, err - } + d.resolver, err = internal.NewNetResolver(target.URL.Host) + if err != nil { + return nil, err } d.wg.Add(1) @@ -159,12 +147,6 @@ func (b *dnsBuilder) Scheme() string { return "dns" } -type netResolver interface { - LookupHost(ctx context.Context, host string) (addrs []string, err error) - LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) - LookupTXT(ctx context.Context, name string) (txts []string, err error) -} - // deadResolver is a resolver that does nothing. type deadResolver struct{} @@ -176,23 +158,26 @@ func (deadResolver) Close() {} type dnsResolver struct { host string port string - resolver netResolver + resolver internal.NetResolver ctx context.Context cancel context.CancelFunc cc resolver.ClientConn - // rn channel is used by ResolveNow() to force an immediate resolution of the target. + // rn channel is used by ResolveNow() to force an immediate resolution of the + // target. rn chan struct{} - // wg is used to enforce Close() to return after the watcher() goroutine has finished. - // Otherwise, data race will be possible. [Race Example] in dns_resolver_test we - // replace the real lookup functions with mocked ones to facilitate testing. - // If Close() doesn't wait for watcher() goroutine finishes, race detector sometimes - // will warns lookup (READ the lookup function pointers) inside watcher() goroutine - // has data race with replaceNetFunc (WRITE the lookup function pointers). + // wg is used to enforce Close() to return after the watcher() goroutine has + // finished. Otherwise, data race will be possible. [Race Example] in + // dns_resolver_test we replace the real lookup functions with mocked ones to + // facilitate testing. If Close() doesn't wait for watcher() goroutine + // finishes, race detector sometimes will warns lookup (READ the lookup + // function pointers) inside watcher() goroutine has data race with + // replaceNetFunc (WRITE the lookup function pointers). wg sync.WaitGroup disableServiceConfig bool } -// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches. +// ResolveNow invoke an immediate resolution of the target that this +// dnsResolver watches. func (d *dnsResolver) ResolveNow(resolver.ResolveNowOptions) { select { case d.rn <- struct{}{}: @@ -218,44 +203,43 @@ func (d *dnsResolver) watcher() { err = d.cc.UpdateState(*state) } - var timer *time.Timer + var waitTime time.Duration if err == nil { - // Success resolving, wait for the next ResolveNow. However, also wait 30 seconds at the very least - // to prevent constantly re-resolving. + // Success resolving, wait for the next ResolveNow. However, also wait 30 + // seconds at the very least to prevent constantly re-resolving. backoffIndex = 1 - timer = newTimerDNSResRate(minDNSResRate) + waitTime = internal.MinResolutionRate select { case <-d.ctx.Done(): - timer.Stop() return case <-d.rn: } } else { - // Poll on an error found in DNS Resolver or an error received from ClientConn. - timer = newTimer(backoff.DefaultExponential.Backoff(backoffIndex)) + // Poll on an error found in DNS Resolver or an error received from + // ClientConn. + waitTime = backoff.DefaultExponential.Backoff(backoffIndex) backoffIndex++ } select { case <-d.ctx.Done(): - timer.Stop() return - case <-timer.C: + case <-internal.TimeAfterFunc(waitTime): } } } -func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) { +func (d *dnsResolver) lookupSRV(ctx context.Context) ([]resolver.Address, error) { if !EnableSRVLookups { return nil, nil } var newAddrs []resolver.Address - _, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host) + _, srvs, err := d.resolver.LookupSRV(ctx, "grpclb", "tcp", d.host) if err != nil { err = handleDNSError(err, "SRV") // may become nil return nil, err } for _, s := range srvs { - lbAddrs, err := d.resolver.LookupHost(d.ctx, s.Target) + lbAddrs, err := d.resolver.LookupHost(ctx, s.Target) if err != nil { err = handleDNSError(err, "A") // may become nil if err == nil { @@ -278,7 +262,8 @@ func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) { } func handleDNSError(err error, lookupType string) error { - if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary { + dnsErr, ok := err.(*net.DNSError) + if ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary { // Timeouts and temporary errors should be communicated to gRPC to // attempt another DNS query (with backoff). Other errors should be // suppressed (they may represent the absence of a TXT record). @@ -291,8 +276,8 @@ func handleDNSError(err error, lookupType string) error { return err } -func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult { - ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host) +func (d *dnsResolver) lookupTXT(ctx context.Context) *serviceconfig.ParseResult { + ss, err := d.resolver.LookupTXT(ctx, txtPrefix+d.host) if err != nil { if envconfig.TXTErrIgnore { return nil @@ -307,18 +292,20 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult { res += s } - // TXT record must have "grpc_config=" attribute in order to be used as service config. + // TXT record must have "grpc_config=" attribute in order to be used as + // service config. if !strings.HasPrefix(res, txtAttribute) { logger.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute) - // This is not an error; it is the equivalent of not having a service config. + // This is not an error; it is the equivalent of not having a service + // config. return nil } sc := canaryingSC(strings.TrimPrefix(res, txtAttribute)) return d.cc.ParseServiceConfig(sc) } -func (d *dnsResolver) lookupHost() ([]resolver.Address, error) { - addrs, err := d.resolver.LookupHost(d.ctx, d.host) +func (d *dnsResolver) lookupHost(ctx context.Context) ([]resolver.Address, error) { + addrs, err := d.resolver.LookupHost(ctx, d.host) if err != nil { err = handleDNSError(err, "A") return nil, err @@ -336,8 +323,10 @@ func (d *dnsResolver) lookupHost() ([]resolver.Address, error) { } func (d *dnsResolver) lookup() (*resolver.State, error) { - srv, srvErr := d.lookupSRV() - addrs, hostErr := d.lookupHost() + ctx, cancel := context.WithTimeout(d.ctx, ResolvingTimeout) + defer cancel() + srv, srvErr := d.lookupSRV(ctx) + addrs, hostErr := d.lookupHost(ctx) if hostErr != nil && (srvErr != nil || len(srv) == 0) { return nil, hostErr } @@ -347,14 +336,15 @@ func (d *dnsResolver) lookup() (*resolver.State, error) { state = grpclbstate.Set(state, &grpclbstate.State{BalancerAddresses: srv}) } if !d.disableServiceConfig { - state.ServiceConfig = d.lookupTXT() + state.ServiceConfig = d.lookupTXT(ctx) } return &state, nil } -// formatIP returns ok = false if addr is not a valid textual representation of an IP address. -// If addr is an IPv4 address, return the addr and ok = true. -// If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true. +// formatIP returns ok = false if addr is not a valid textual representation of +// an IP address. If addr is an IPv4 address, return the addr and ok = true. +// If addr is an IPv6 address, return the addr enclosed in square brackets and +// ok = true. func formatIP(addr string) (addrIP string, ok bool) { ip := net.ParseIP(addr) if ip == nil { @@ -366,10 +356,10 @@ func formatIP(addr string) (addrIP string, ok bool) { return "[" + addr + "]", true } -// parseTarget takes the user input target string and default port, returns formatted host and port info. -// If target doesn't specify a port, set the port to be the defaultPort. -// If target is in IPv6 format and host-name is enclosed in square brackets, brackets -// are stripped when setting the host. +// parseTarget takes the user input target string and default port, returns +// formatted host and port info. If target doesn't specify a port, set the port +// to be the defaultPort. If target is in IPv6 format and host-name is enclosed +// in square brackets, brackets are stripped when setting the host. // examples: // target: "www.google.com" defaultPort: "443" returns host: "www.google.com", port: "443" // target: "ipv4-host:80" defaultPort: "443" returns host: "ipv4-host", port: "80" @@ -377,7 +367,7 @@ func formatIP(addr string) (addrIP string, ok bool) { // target: ":80" defaultPort: "443" returns host: "localhost", port: "80" func parseTarget(target, defaultPort string) (host, port string, err error) { if target == "" { - return "", "", errMissingAddr + return "", "", internal.ErrMissingAddr } if ip := net.ParseIP(target); ip != nil { // target is an IPv4 or IPv6(without brackets) address @@ -385,12 +375,14 @@ func parseTarget(target, defaultPort string) (host, port string, err error) { } if host, port, err = net.SplitHostPort(target); err == nil { if port == "" { - // If the port field is empty (target ends with colon), e.g. "[::1]:", this is an error. - return "", "", errEndsWithColon + // If the port field is empty (target ends with colon), e.g. "[::1]:", + // this is an error. + return "", "", internal.ErrEndsWithColon } // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port if host == "" { - // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed. + // Keep consistent with net.Dial(): If the host is empty, as in ":80", + // the local system is assumed. host = "localhost" } return host, port, nil diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go b/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go new file mode 100644 index 000000000..c7fc557d0 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go @@ -0,0 +1,70 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package internal contains functionality internal to the dns resolver package. +package internal + +import ( + "context" + "errors" + "net" + "time" +) + +// NetResolver groups the methods on net.Resolver that are used by the DNS +// resolver implementation. This allows the default net.Resolver instance to be +// overidden from tests. +type NetResolver interface { + LookupHost(ctx context.Context, host string) (addrs []string, err error) + LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) + LookupTXT(ctx context.Context, name string) (txts []string, err error) +} + +var ( + // ErrMissingAddr is the error returned when building a DNS resolver when + // the provided target name is empty. + ErrMissingAddr = errors.New("dns resolver: missing address") + + // ErrEndsWithColon is the error returned when building a DNS resolver when + // the provided target name ends with a colon that is supposed to be the + // separator between host and port. E.g. "::" is a valid address as it is + // an IPv6 address (host only) and "[::]:" is invalid as it ends with a + // colon as the host and port separator + ErrEndsWithColon = errors.New("dns resolver: missing port after port-separator colon") +) + +// The following vars are overridden from tests. +var ( + // MinResolutionRate is the minimum rate at which re-resolutions are + // allowed. This helps to prevent excessive re-resolution. + MinResolutionRate = 30 * time.Second + + // TimeAfterFunc is used by the DNS resolver to wait for the given duration + // to elapse. In non-test code, this is implemented by time.After. In test + // code, this can be used to control the amount of time the resolver is + // blocked waiting for the duration to elapse. + TimeAfterFunc func(time.Duration) <-chan time.Time + + // NewNetResolver returns the net.Resolver instance for the given target. + NewNetResolver func(string) (NetResolver, error) + + // AddressDialer is the dialer used to dial the DNS server. It accepts the + // Host portion of the URL corresponding to the user's dial target and + // returns a dial function. + AddressDialer func(address string) func(context.Context, string, string) (net.Conn, error) +) diff --git a/vendor/google.golang.org/grpc/internal/resolver/unix/unix.go b/vendor/google.golang.org/grpc/internal/resolver/unix/unix.go index 160911687..27cd81af9 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/unix/unix.go +++ b/vendor/google.golang.org/grpc/internal/resolver/unix/unix.go @@ -61,6 +61,10 @@ func (b *builder) Scheme() string { return b.scheme } +func (b *builder) OverrideAuthority(resolver.Target) string { + return "localhost" +} + type nopResolver struct { } diff --git a/vendor/google.golang.org/grpc/internal/status/status.go b/vendor/google.golang.org/grpc/internal/status/status.go index b0ead4f54..c7dbc8205 100644 --- a/vendor/google.golang.org/grpc/internal/status/status.go +++ b/vendor/google.golang.org/grpc/internal/status/status.go @@ -31,10 +31,11 @@ import ( "errors" "fmt" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes" spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/protoadapt" + "google.golang.org/protobuf/types/known/anypb" ) // Status represents an RPC status code, message, and details. It is immutable @@ -43,13 +44,41 @@ type Status struct { s *spb.Status } +// NewWithProto returns a new status including details from statusProto. This +// is meant to be used by the gRPC library only. +func NewWithProto(code codes.Code, message string, statusProto []string) *Status { + if len(statusProto) != 1 { + // No grpc-status-details bin header, or multiple; just ignore. + return &Status{s: &spb.Status{Code: int32(code), Message: message}} + } + st := &spb.Status{} + if err := proto.Unmarshal([]byte(statusProto[0]), st); err != nil { + // Probably not a google.rpc.Status proto; do not provide details. + return &Status{s: &spb.Status{Code: int32(code), Message: message}} + } + if st.Code == int32(code) { + // The codes match between the grpc-status header and the + // grpc-status-details-bin header; use the full details proto. + return &Status{s: st} + } + return &Status{ + s: &spb.Status{ + Code: int32(codes.Internal), + Message: fmt.Sprintf( + "grpc-status-details-bin mismatch: grpc-status=%v, grpc-message=%q, grpc-status-details-bin=%+v", + code, message, st, + ), + }, + } +} + // New returns a Status representing c and msg. func New(c codes.Code, msg string) *Status { return &Status{s: &spb.Status{Code: int32(c), Message: msg}} } // Newf returns New(c, fmt.Sprintf(format, a...)). -func Newf(c codes.Code, format string, a ...interface{}) *Status { +func Newf(c codes.Code, format string, a ...any) *Status { return New(c, fmt.Sprintf(format, a...)) } @@ -64,7 +93,7 @@ func Err(c codes.Code, msg string) error { } // Errorf returns Error(c, fmt.Sprintf(format, a...)). -func Errorf(c codes.Code, format string, a ...interface{}) error { +func Errorf(c codes.Code, format string, a ...any) error { return Err(c, fmt.Sprintf(format, a...)) } @@ -102,14 +131,14 @@ func (s *Status) Err() error { // WithDetails returns a new status with the provided details messages appended to the status. // If any errors are encountered, it returns nil and the first error encountered. -func (s *Status) WithDetails(details ...proto.Message) (*Status, error) { +func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) { if s.Code() == codes.OK { return nil, errors.New("no error details for status with code OK") } // s.Code() != OK implies that s.Proto() != nil. p := s.Proto() for _, detail := range details { - any, err := ptypes.MarshalAny(detail) + any, err := anypb.New(protoadapt.MessageV2Of(detail)) if err != nil { return nil, err } @@ -120,18 +149,18 @@ func (s *Status) WithDetails(details ...proto.Message) (*Status, error) { // Details returns a slice of details messages attached to the status. // If a detail cannot be decoded, the error is returned in place of the detail. -func (s *Status) Details() []interface{} { +func (s *Status) Details() []any { if s == nil || s.s == nil { return nil } - details := make([]interface{}, 0, len(s.s.Details)) + details := make([]any, 0, len(s.s.Details)) for _, any := range s.s.Details { - detail := &ptypes.DynamicAny{} - if err := ptypes.UnmarshalAny(any, detail); err != nil { + detail, err := any.UnmarshalNew() + if err != nil { details = append(details, err) continue } - details = append(details, detail.Message) + details = append(details, detail) } return details } diff --git a/vendor/google.golang.org/grpc/internal/channelz/util_nonlinux.go b/vendor/google.golang.org/grpc/internal/tcp_keepalive_others.go similarity index 69% rename from vendor/google.golang.org/grpc/internal/channelz/util_nonlinux.go rename to vendor/google.golang.org/grpc/internal/tcp_keepalive_others.go index 837ddc402..4f347edd4 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/util_nonlinux.go +++ b/vendor/google.golang.org/grpc/internal/tcp_keepalive_others.go @@ -1,9 +1,7 @@ -//go:build !linux -// +build !linux +//go:build !unix && !windows /* - * - * Copyright 2018 gRPC authors. + * Copyright 2023 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +17,13 @@ * */ -package channelz +package internal + +import ( + "net" +) -// GetSocketOption gets the socket option info of the conn. -func GetSocketOption(c interface{}) *SocketOptionData { - return nil +// NetDialerWithTCPKeepalive returns a vanilla net.Dialer on non-unix platforms. +func NetDialerWithTCPKeepalive() *net.Dialer { + return &net.Dialer{} } diff --git a/vendor/google.golang.org/grpc/internal/tcp_keepalive_unix.go b/vendor/google.golang.org/grpc/internal/tcp_keepalive_unix.go new file mode 100644 index 000000000..078137b7f --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/tcp_keepalive_unix.go @@ -0,0 +1,54 @@ +//go:build unix + +/* + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package internal + +import ( + "net" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// NetDialerWithTCPKeepalive returns a net.Dialer that enables TCP keepalives on +// the underlying connection with OS default values for keepalive parameters. +// +// TODO: Once https://github.com/golang/go/issues/62254 lands, and the +// appropriate Go version becomes less than our least supported Go version, we +// should look into using the new API to make things more straightforward. +func NetDialerWithTCPKeepalive() *net.Dialer { + return &net.Dialer{ + // Setting a negative value here prevents the Go stdlib from overriding + // the values of TCP keepalive time and interval. It also prevents the + // Go stdlib from enabling TCP keepalives by default. + KeepAlive: time.Duration(-1), + // This method is called after the underlying network socket is created, + // but before dialing the socket (or calling its connect() method). The + // combination of unconditionally enabling TCP keepalives here, and + // disabling the overriding of TCP keepalive parameters by setting the + // KeepAlive field to a negative value above, results in OS defaults for + // the TCP keealive interval and time parameters. + Control: func(_, _ string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1) + }) + }, + } +} diff --git a/vendor/google.golang.org/grpc/internal/tcp_keepalive_windows.go b/vendor/google.golang.org/grpc/internal/tcp_keepalive_windows.go new file mode 100644 index 000000000..fd7d43a89 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/tcp_keepalive_windows.go @@ -0,0 +1,54 @@ +//go:build windows + +/* + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package internal + +import ( + "net" + "syscall" + "time" + + "golang.org/x/sys/windows" +) + +// NetDialerWithTCPKeepalive returns a net.Dialer that enables TCP keepalives on +// the underlying connection with OS default values for keepalive parameters. +// +// TODO: Once https://github.com/golang/go/issues/62254 lands, and the +// appropriate Go version becomes less than our least supported Go version, we +// should look into using the new API to make things more straightforward. +func NetDialerWithTCPKeepalive() *net.Dialer { + return &net.Dialer{ + // Setting a negative value here prevents the Go stdlib from overriding + // the values of TCP keepalive time and interval. It also prevents the + // Go stdlib from enabling TCP keepalives by default. + KeepAlive: time.Duration(-1), + // This method is called after the underlying network socket is created, + // but before dialing the socket (or calling its connect() method). The + // combination of unconditionally enabling TCP keepalives here, and + // disabling the overriding of TCP keepalive parameters by setting the + // KeepAlive field to a negative value above, results in OS defaults for + // the TCP keealive interval and time parameters. + Control: func(_, _ string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_KEEPALIVE, 1) + }) + }, + } +} diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index be5a9c81e..83c382982 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -40,7 +40,7 @@ var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) { } type itemNode struct { - it interface{} + it any next *itemNode } @@ -49,7 +49,7 @@ type itemList struct { tail *itemNode } -func (il *itemList) enqueue(i interface{}) { +func (il *itemList) enqueue(i any) { n := &itemNode{it: i} if il.tail == nil { il.head, il.tail = n, n @@ -61,11 +61,11 @@ func (il *itemList) enqueue(i interface{}) { // peek returns the first item in the list without removing it from the // list. -func (il *itemList) peek() interface{} { +func (il *itemList) peek() any { return il.head.it } -func (il *itemList) dequeue() interface{} { +func (il *itemList) dequeue() any { if il.head == nil { return nil } @@ -336,7 +336,7 @@ func (c *controlBuffer) put(it cbItem) error { return err } -func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (bool, error) { +func (c *controlBuffer) executeAndPut(f func(it any) bool, it cbItem) (bool, error) { var wakeUp bool c.mu.Lock() if c.err != nil { @@ -373,7 +373,7 @@ func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (b } // Note argument f should never be nil. -func (c *controlBuffer) execute(f func(it interface{}) bool, it interface{}) (bool, error) { +func (c *controlBuffer) execute(f func(it any) bool, it any) (bool, error) { c.mu.Lock() if c.err != nil { c.mu.Unlock() @@ -387,7 +387,7 @@ func (c *controlBuffer) execute(f func(it interface{}) bool, it interface{}) (bo return true, nil } -func (c *controlBuffer) get(block bool) (interface{}, error) { +func (c *controlBuffer) get(block bool) (any, error) { for { c.mu.Lock() if c.err != nil { @@ -535,8 +535,8 @@ const minBatchSize = 1000 // size is too low to give stream goroutines a chance to fill it up. // // Upon exiting, if the error causing the exit is not an I/O error, run() -// flushes and closes the underlying connection. Otherwise, the connection is -// left open to allow the I/O error to be encountered by the reader instead. +// flushes the underlying connection. The connection is always left open to +// allow different closing behavior on the client and server. func (l *loopyWriter) run() (err error) { defer func() { if l.logger.V(logLevel) { @@ -544,7 +544,6 @@ func (l *loopyWriter) run() (err error) { } if !isIOError(err) { l.framer.writer.Flush() - l.conn.Close() } l.cbuf.finish() }() @@ -830,7 +829,7 @@ func (l *loopyWriter) goAwayHandler(g *goAway) error { return nil } -func (l *loopyWriter) handle(i interface{}) error { +func (l *loopyWriter) handle(i any) error { switch i := i.(type) { case *incomingWindowUpdate: l.incomingWindowUpdateHandler(i) diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go index 98f80e3fa..4a3ddce29 100644 --- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go @@ -35,7 +35,6 @@ import ( "sync" "time" - "github.com/golang/protobuf/proto" "golang.org/x/net/http2" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -45,20 +44,17 @@ import ( "google.golang.org/grpc/peer" "google.golang.org/grpc/stats" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" ) // NewServerHandlerTransport returns a ServerTransport handling gRPC from // inside an http.Handler, or writes an HTTP error to w and returns an error. // It requires that the http Server supports HTTP/2. func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []stats.Handler) (ServerTransport, error) { - if r.ProtoMajor != 2 { - msg := "gRPC requires HTTP/2" - http.Error(w, msg, http.StatusBadRequest) - return nil, errors.New(msg) - } - if r.Method != "POST" { + if r.Method != http.MethodPost { + w.Header().Set("Allow", http.MethodPost) msg := fmt.Sprintf("invalid gRPC request method %q", r.Method) - http.Error(w, msg, http.StatusBadRequest) + http.Error(w, msg, http.StatusMethodNotAllowed) return nil, errors.New(msg) } contentType := r.Header.Get("Content-Type") @@ -69,17 +65,36 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []s http.Error(w, msg, http.StatusUnsupportedMediaType) return nil, errors.New(msg) } + if r.ProtoMajor != 2 { + msg := "gRPC requires HTTP/2" + http.Error(w, msg, http.StatusHTTPVersionNotSupported) + return nil, errors.New(msg) + } if _, ok := w.(http.Flusher); !ok { msg := "gRPC requires a ResponseWriter supporting http.Flusher" http.Error(w, msg, http.StatusInternalServerError) return nil, errors.New(msg) } + var localAddr net.Addr + if la := r.Context().Value(http.LocalAddrContextKey); la != nil { + localAddr, _ = la.(net.Addr) + } + var authInfo credentials.AuthInfo + if r.TLS != nil { + authInfo = credentials.TLSInfo{State: *r.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}} + } + p := peer.Peer{ + Addr: strAddr(r.RemoteAddr), + LocalAddr: localAddr, + AuthInfo: authInfo, + } st := &serverHandlerTransport{ rw: w, req: r, closedCh: make(chan struct{}), writes: make(chan func()), + peer: p, contentType: contentType, contentSubtype: contentSubtype, stats: stats, @@ -134,6 +149,8 @@ type serverHandlerTransport struct { headerMD metadata.MD + peer peer.Peer + closeOnce sync.Once closedCh chan struct{} // closed on Close @@ -165,7 +182,13 @@ func (ht *serverHandlerTransport) Close(err error) { }) } -func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) } +func (ht *serverHandlerTransport) Peer() *peer.Peer { + return &peer.Peer{ + Addr: ht.peer.Addr, + LocalAddr: ht.peer.LocalAddr, + AuthInfo: ht.peer.AuthInfo, + } +} // strAddr is a net.Addr backed by either a TCP "ip:port" string, or // the empty string if unknown. @@ -220,18 +243,20 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro h.Set("Grpc-Message", encodeGrpcMessage(m)) } + s.hdrMu.Lock() if p := st.Proto(); p != nil && len(p.Details) > 0 { + delete(s.trailer, grpcStatusDetailsBinHeader) stBytes, err := proto.Marshal(p) if err != nil { // TODO: return error instead, when callers are able to handle it. panic(err) } - h.Set("Grpc-Status-Details-Bin", encodeBinHeader(stBytes)) + h.Set(grpcStatusDetailsBinHeader, encodeBinHeader(stBytes)) } - if md := s.Trailer(); len(md) > 0 { - for k, vv := range md { + if len(s.trailer) > 0 { + for k, vv := range s.trailer { // Clients don't tolerate reading restricted headers after some non restricted ones were sent. if isReservedHeader(k) { continue @@ -243,6 +268,7 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro } } } + s.hdrMu.Unlock() }) if err == nil { // transport has not been closed @@ -287,7 +313,7 @@ func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) { } // writeCustomHeaders sets custom headers set on the stream via SetHeader -// on the first write call (Write, WriteHeader, or WriteStatus). +// on the first write call (Write, WriteHeader, or WriteStatus) func (ht *serverHandlerTransport) writeCustomHeaders(s *Stream) { h := ht.rw.Header() @@ -344,10 +370,8 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error { return err } -func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) { +func (ht *serverHandlerTransport) HandleStreams(ctx context.Context, startStream func(*Stream)) { // With this transport type there will be exactly 1 stream: this HTTP request. - - ctx := ht.req.Context() var cancel context.CancelFunc if ht.timeoutSet { ctx, cancel = context.WithTimeout(ctx, ht.timeout) @@ -367,34 +391,19 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace ht.Close(errors.New("request is done processing")) }() + ctx = metadata.NewIncomingContext(ctx, ht.headerMD) req := ht.req - s := &Stream{ - id: 0, // irrelevant - requestRead: func(int) {}, - cancel: cancel, - buf: newRecvBuffer(), - st: ht, - method: req.URL.Path, - recvCompress: req.Header.Get("grpc-encoding"), - contentSubtype: ht.contentSubtype, - } - pr := &peer.Peer{ - Addr: ht.RemoteAddr(), - } - if req.TLS != nil { - pr.AuthInfo = credentials.TLSInfo{State: *req.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}} - } - ctx = metadata.NewIncomingContext(ctx, ht.headerMD) - s.ctx = peer.NewContext(ctx, pr) - for _, sh := range ht.stats { - s.ctx = sh.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method}) - inHeader := &stats.InHeader{ - FullMethod: s.method, - RemoteAddr: ht.RemoteAddr(), - Compression: s.recvCompress, - } - sh.HandleRPC(s.ctx, inHeader) + id: 0, // irrelevant + ctx: ctx, + requestRead: func(int) {}, + cancel: cancel, + buf: newRecvBuffer(), + st: ht, + method: req.URL.Path, + recvCompress: req.Header.Get("grpc-encoding"), + contentSubtype: ht.contentSubtype, + headerWireLength: 0, // won't have access to header wire length until golang/go#18997. } s.trReader = &transportReader{ reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf, freeBuffer: func(*bytes.Buffer) {}}, diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index 326bf0848..deba0c4d9 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -36,6 +36,7 @@ import ( "golang.org/x/net/http2/hpack" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/channelz" icredentials "google.golang.org/grpc/internal/credentials" "google.golang.org/grpc/internal/grpclog" @@ -43,7 +44,7 @@ import ( "google.golang.org/grpc/internal/grpcutil" imetadata "google.golang.org/grpc/internal/metadata" istatus "google.golang.org/grpc/internal/status" - "google.golang.org/grpc/internal/syscall" + isyscall "google.golang.org/grpc/internal/syscall" "google.golang.org/grpc/internal/transport/networktype" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" @@ -58,6 +59,8 @@ import ( // atomically. var clientConnectionCounter uint64 +var metadataFromOutgoingContextRaw = internal.FromOutgoingContextRaw.(func(context.Context) (metadata.MD, [][]string, bool)) + // http2Client implements the ClientTransport interface with HTTP2. type http2Client struct { lastRead int64 // Keep this field 64-bit aligned. Accessed atomically. @@ -137,9 +140,7 @@ type http2Client struct { // variable. kpDormant bool - // Fields below are for channelz metric collection. - channelzID *channelz.Identifier - czData *channelzData + channelz *channelz.Socket onClose func(GoAwayReason) @@ -176,7 +177,7 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error if networkType == "tcp" && useProxy { return proxyDial(ctx, address, grpcUA) } - return (&net.Dialer{}).DialContext(ctx, networkType, address) + return internal.NetDialerWithTCPKeepalive().DialContext(ctx, networkType, address) } func isTemporary(err error) bool { @@ -262,7 +263,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } keepaliveEnabled := false if kp.Time != infinity { - if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { + if err = isyscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err) } keepaliveEnabled = true @@ -316,6 +317,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts if opts.MaxHeaderListSize != nil { maxHeaderListSize = *opts.MaxHeaderListSize } + t := &http2Client{ ctx: ctx, ctxDone: ctx.Done(), // Cache Done chan. @@ -330,7 +332,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts readerDone: make(chan struct{}), writerDone: make(chan struct{}), goAway: make(chan struct{}), - framer: newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize), + framer: newFramer(conn, writeBufSize, readBufSize, opts.SharedWriteBuffer, maxHeaderListSize), fc: &trInFlow{limit: uint32(icwz)}, scheme: scheme, activeStreams: make(map[uint32]*Stream), @@ -343,11 +345,25 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts maxConcurrentStreams: defaultMaxStreamsClient, streamQuota: defaultMaxStreamsClient, streamsQuotaAvailable: make(chan struct{}, 1), - czData: new(channelzData), keepaliveEnabled: keepaliveEnabled, bufferPool: newBufferPool(), onClose: onClose, } + var czSecurity credentials.ChannelzSecurityValue + if au, ok := authInfo.(credentials.ChannelzSecurityInfo); ok { + czSecurity = au.GetSecurityValue() + } + t.channelz = channelz.RegisterSocket( + &channelz.Socket{ + SocketType: channelz.SocketTypeNormal, + Parent: opts.ChannelzParent, + SocketMetrics: channelz.SocketMetrics{}, + EphemeralMetrics: t.socketMetrics, + LocalAddr: t.localAddr, + RemoteAddr: t.remoteAddr, + SocketOptions: channelz.GetSocketOption(t.conn), + Security: czSecurity, + }) t.logger = prefixLoggerForClientTransport(t) // Add peer information to the http2client context. t.ctx = peer.NewContext(t.ctx, t.getPeer()) @@ -378,10 +394,6 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } sh.HandleConn(t.ctx, connBegin) } - t.channelzID, err = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr)) - if err != nil { - return nil, err - } if t.keepaliveEnabled { t.kpDormancyCond = sync.NewCond(&t.mu) go t.keepalive() @@ -448,7 +460,13 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } go func() { t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) - t.loopy.run() + if err := t.loopy.run(); !isIOError(err) { + // Immediately close the connection, as the loopy writer returns + // when there are no more active streams and we were draining (the + // server sent a GOAWAY). For I/O errors, the reader will hit it + // after draining any remaining incoming data. + t.conn.Close() + } close(t.writerDone) }() return t, nil @@ -493,8 +511,9 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream { func (t *http2Client) getPeer() *peer.Peer { return &peer.Peer{ - Addr: t.remoteAddr, - AuthInfo: t.authInfo, // Can be nil + Addr: t.remoteAddr, + AuthInfo: t.authInfo, // Can be nil + LocalAddr: t.localAddr, } } @@ -566,7 +585,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-trace-bin", Value: encodeBinHeader(b)}) } - if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok { + if md, added, ok := metadataFromOutgoingContextRaw(ctx); ok { var k string for k, vv := range md { // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set. @@ -746,8 +765,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, return ErrConnClosing } if channelz.IsOn() { - atomic.AddInt64(&t.czData.streamsStarted, 1) - atomic.StoreInt64(&t.czData.lastStreamCreatedTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.StreamsStarted.Add(1) + t.channelz.SocketMetrics.LastLocalStreamCreatedTimestamp.Store(time.Now().UnixNano()) } // If the keepalive goroutine has gone dormant, wake it up. if t.kpDormant { @@ -762,7 +781,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, firstTry := true var ch chan struct{} transportDrainRequired := false - checkForStreamQuota := func(it interface{}) bool { + checkForStreamQuota := func(it any) bool { if t.streamQuota <= 0 { // Can go negative if server decreases it. if firstTry { t.waitingStreams++ @@ -800,7 +819,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, return true } var hdrListSizeErr error - checkForHeaderListSize := func(it interface{}) bool { + checkForHeaderListSize := func(it any) bool { if t.maxSendHeaderListSize == nil { return true } @@ -815,7 +834,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, return true } for { - success, err := t.controlBuf.executeAndPut(func(it interface{}) bool { + success, err := t.controlBuf.executeAndPut(func(it any) bool { return checkForHeaderListSize(it) && checkForStreamQuota(it) }, hdr) if err != nil { @@ -918,16 +937,16 @@ func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2. t.mu.Unlock() if channelz.IsOn() { if eosReceived { - atomic.AddInt64(&t.czData.streamsSucceeded, 1) + t.channelz.SocketMetrics.StreamsSucceeded.Add(1) } else { - atomic.AddInt64(&t.czData.streamsFailed, 1) + t.channelz.SocketMetrics.StreamsFailed.Add(1) } } }, rst: rst, rstCode: rstCode, } - addBackStreamQuota := func(interface{}) bool { + addBackStreamQuota := func(any) bool { t.streamQuota++ if t.streamQuota > 0 && t.waitingStreams > 0 { select { @@ -975,7 +994,7 @@ func (t *http2Client) Close(err error) { t.controlBuf.finish() t.cancel() t.conn.Close() - channelz.RemoveEntry(t.channelzID) + channelz.RemoveEntry(t.channelz.ID) // Append info about previous goaways if there were any, since this may be important // for understanding the root cause for this connection to be closed. _, goAwayDebugMessage := t.GetGoAwayReason() @@ -1080,7 +1099,7 @@ func (t *http2Client) updateWindow(s *Stream, n uint32) { // for the transport and the stream based on the current bdp // estimation. func (t *http2Client) updateFlowControl(n uint32) { - updateIWS := func(interface{}) bool { + updateIWS := func(any) bool { t.initialWindowSize = int32(n) t.mu.Lock() for _, s := range t.activeStreams { @@ -1233,7 +1252,7 @@ func (t *http2Client) handleSettings(f *http2.SettingsFrame, isFirst bool) { } updateFuncs = append(updateFuncs, updateStreamQuota) } - t.controlBuf.executeAndPut(func(interface{}) bool { + t.controlBuf.executeAndPut(func(any) bool { for _, f := range updateFuncs { f() } @@ -1321,10 +1340,8 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) { for streamID, stream := range t.activeStreams { if streamID > id && streamID <= upperLimit { // The stream was unprocessed by the server. - if streamID > id && streamID <= upperLimit { - atomic.StoreUint32(&stream.unprocessed, 1) - streamsToClose = append(streamsToClose, stream) - } + atomic.StoreUint32(&stream.unprocessed, 1) + streamsToClose = append(streamsToClose, stream) } } t.mu.Unlock() @@ -1399,7 +1416,6 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { mdata = make(map[string][]string) contentTypeErr = "malformed header: missing HTTP content-type" grpcMessage string - statusGen *status.Status recvCompress string httpStatusCode *int httpStatusErr string @@ -1434,12 +1450,6 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { rawStatusCode = codes.Code(uint32(code)) case "grpc-message": grpcMessage = decodeGrpcMessage(hf.Value) - case "grpc-status-details-bin": - var err error - statusGen, err = decodeGRPCStatusDetails(hf.Value) - if err != nil { - headerError = fmt.Sprintf("transport: malformed grpc-status-details-bin: %v", err) - } case ":status": if hf.Value == "200" { httpStatusErr = "" @@ -1505,14 +1515,15 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { return } - isHeader := false - - // If headerChan hasn't been closed yet - if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { - s.headerValid = true - if !endStream { - // HEADERS frame block carries a Response-Headers. - isHeader = true + // For headers, set them in s.header and close headerChan. For trailers or + // trailers-only, closeStream will set the trailers and close headerChan as + // needed. + if !endStream { + // If headerChan hasn't been closed yet (expected, given we checked it + // above, but something else could have potentially closed the whole + // stream). + if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { + s.headerValid = true // These values can be set without any synchronization because // stream goroutine will read it only after seeing a closed // headerChan which we'll close after setting this. @@ -1520,15 +1531,12 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { if len(mdata) > 0 { s.header = mdata } - } else { - // HEADERS frame block carries a Trailers-Only. - s.noHeaders = true + close(s.headerChan) } - close(s.headerChan) } for _, sh := range t.statsHandlers { - if isHeader { + if !endStream { inHeader := &stats.InHeader{ Client: true, WireLength: int(frame.Header().Length), @@ -1550,13 +1558,12 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { return } - if statusGen == nil { - statusGen = status.New(rawStatusCode, grpcMessage) - } + status := istatus.NewWithProto(rawStatusCode, grpcMessage, mdata[grpcStatusDetailsBinHeader]) - // if client received END_STREAM from server while stream was still active, send RST_STREAM - rst := s.getState() == streamActive - t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, statusGen, mdata, true) + // If client received END_STREAM from server while stream was still active, + // send RST_STREAM. + rstStream := s.getState() == streamActive + t.closeStream(s, io.EOF, rstStream, http2.ErrCodeNo, status, mdata, true) } // readServerPreface reads and handles the initial settings frame from the @@ -1710,7 +1717,7 @@ func (t *http2Client) keepalive() { // keepalive timer expired. In both cases, we need to send a ping. if !outstandingPing { if channelz.IsOn() { - atomic.AddInt64(&t.czData.kpCount, 1) + t.channelz.SocketMetrics.KeepAlivesSent.Add(1) } t.controlBuf.put(p) timeoutLeft = t.kp.Timeout @@ -1740,40 +1747,23 @@ func (t *http2Client) GoAway() <-chan struct{} { return t.goAway } -func (t *http2Client) ChannelzMetric() *channelz.SocketInternalMetric { - s := channelz.SocketInternalMetric{ - StreamsStarted: atomic.LoadInt64(&t.czData.streamsStarted), - StreamsSucceeded: atomic.LoadInt64(&t.czData.streamsSucceeded), - StreamsFailed: atomic.LoadInt64(&t.czData.streamsFailed), - MessagesSent: atomic.LoadInt64(&t.czData.msgSent), - MessagesReceived: atomic.LoadInt64(&t.czData.msgRecv), - KeepAlivesSent: atomic.LoadInt64(&t.czData.kpCount), - LastLocalStreamCreatedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastStreamCreatedTime)), - LastMessageSentTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgSentTime)), - LastMessageReceivedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgRecvTime)), - LocalFlowControlWindow: int64(t.fc.getSize()), - SocketOptions: channelz.GetSocketOption(t.conn), - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // RemoteName : - } - if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok { - s.Security = au.GetSecurityValue() - } - s.RemoteFlowControlWindow = t.getOutFlowWindow() - return &s +func (t *http2Client) socketMetrics() *channelz.EphemeralSocketMetrics { + return &channelz.EphemeralSocketMetrics{ + LocalFlowControlWindow: int64(t.fc.getSize()), + RemoteFlowControlWindow: t.getOutFlowWindow(), + } } func (t *http2Client) RemoteAddr() net.Addr { return t.remoteAddr } func (t *http2Client) IncrMsgSent() { - atomic.AddInt64(&t.czData.msgSent, 1) - atomic.StoreInt64(&t.czData.lastMsgSentTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.MessagesSent.Add(1) + t.channelz.SocketMetrics.LastMessageSentTimestamp.Store(time.Now().UnixNano()) } func (t *http2Client) IncrMsgRecv() { - atomic.AddInt64(&t.czData.msgRecv, 1) - atomic.StoreInt64(&t.czData.lastMsgRecvTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.MessagesReceived.Add(1) + t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Store(time.Now().UnixNano()) } func (t *http2Client) getOutFlowWindow() int64 { diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index ec4eef213..d582e0471 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -32,13 +32,13 @@ import ( "sync/atomic" "time" - "github.com/golang/protobuf/proto" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/syscall" + "google.golang.org/protobuf/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -68,18 +68,15 @@ var serverConnectionCounter uint64 // http2Server implements the ServerTransport interface with HTTP2. type http2Server struct { - lastRead int64 // Keep this field 64-bit aligned. Accessed atomically. - ctx context.Context - done chan struct{} - conn net.Conn - loopy *loopyWriter - readerDone chan struct{} // sync point to enable testing. - writerDone chan struct{} // sync point to enable testing. - remoteAddr net.Addr - localAddr net.Addr - authInfo credentials.AuthInfo // auth info about the connection - inTapHandle tap.ServerInHandle - framer *framer + lastRead int64 // Keep this field 64-bit aligned. Accessed atomically. + done chan struct{} + conn net.Conn + loopy *loopyWriter + readerDone chan struct{} // sync point to enable testing. + loopyWriterDone chan struct{} + peer peer.Peer + inTapHandle tap.ServerInHandle + framer *framer // The max number of concurrent streams. maxStreams uint32 // controlBuf delivers all the control related tasks (e.g., window @@ -121,8 +118,7 @@ type http2Server struct { idle time.Time // Fields below are for channelz metric collection. - channelzID *channelz.Identifier - czData *channelzData + channelz *channelz.Socket bufferPool *bufferPool connectionID uint64 @@ -165,7 +161,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, if config.MaxHeaderListSize != nil { maxHeaderListSize = *config.MaxHeaderListSize } - framer := newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize) + framer := newFramer(conn, writeBufSize, readBufSize, config.SharedWriteBuffer, maxHeaderListSize) // Send initial settings as connection preface to client. isettings := []http2.Setting{{ ID: http2.SettingMaxFrameSize, @@ -233,7 +229,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, kp.Timeout = defaultServerKeepaliveTimeout } if kp.Time != infinity { - if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { + if err = syscall.SetTCPUserTimeout(rawConn, kp.Timeout); err != nil { return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err) } } @@ -243,16 +239,18 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, } done := make(chan struct{}) + peer := peer.Peer{ + Addr: conn.RemoteAddr(), + LocalAddr: conn.LocalAddr(), + AuthInfo: authInfo, + } t := &http2Server{ - ctx: setConnection(context.Background(), rawConn), done: done, conn: conn, - remoteAddr: conn.RemoteAddr(), - localAddr: conn.LocalAddr(), - authInfo: authInfo, + peer: peer, framer: framer, readerDone: make(chan struct{}), - writerDone: make(chan struct{}), + loopyWriterDone: make(chan struct{}), maxStreams: config.MaxStreams, inTapHandle: config.InTapHandle, fc: &trInFlow{limit: uint32(icwz)}, @@ -263,12 +261,25 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, idle: time.Now(), kep: kep, initialWindowSize: iwz, - czData: new(channelzData), bufferPool: newBufferPool(), } + var czSecurity credentials.ChannelzSecurityValue + if au, ok := authInfo.(credentials.ChannelzSecurityInfo); ok { + czSecurity = au.GetSecurityValue() + } + t.channelz = channelz.RegisterSocket( + &channelz.Socket{ + SocketType: channelz.SocketTypeNormal, + Parent: config.ChannelzParent, + SocketMetrics: channelz.SocketMetrics{}, + EphemeralMetrics: t.socketMetrics, + LocalAddr: t.peer.LocalAddr, + RemoteAddr: t.peer.Addr, + SocketOptions: channelz.GetSocketOption(t.conn), + Security: czSecurity, + }, + ) t.logger = prefixLoggerForServerTransport(t) - // Add peer information to the http2server context. - t.ctx = peer.NewContext(t.ctx, t.getPeer()) t.controlBuf = newControlBuffer(t.done) if dynamicWindow { @@ -277,18 +288,6 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, updateFlowControl: t.updateFlowControl, } } - for _, sh := range t.stats { - t.ctx = sh.TagConn(t.ctx, &stats.ConnTagInfo{ - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - }) - connBegin := &stats.ConnBegin{} - sh.HandleConn(t.ctx, connBegin) - } - t.channelzID, err = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr)) - if err != nil { - return nil, err - } t.connectionID = atomic.AddUint64(&serverConnectionCounter, 1) t.framer.writer.Flush() @@ -333,8 +332,26 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, go func() { t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler - t.loopy.run() - close(t.writerDone) + err := t.loopy.run() + close(t.loopyWriterDone) + if !isIOError(err) { + // Close the connection if a non-I/O error occurs (for I/O errors + // the reader will also encounter the error and close). Wait 1 + // second before closing the connection, or when the reader is done + // (i.e. the client already closed the connection or a connection + // error occurred). This avoids the potential problem where there + // is unread data on the receive side of the connection, which, if + // closed, would lead to a TCP RST instead of FIN, and the client + // encountering errors. For more info: + // https://github.com/grpc/grpc-go/issues/5358 + timer := time.NewTimer(time.Second) + defer timer.Stop() + select { + case <-t.readerDone: + case <-timer.C: + } + t.conn.Close() + } }() go t.keepalive() return t, nil @@ -342,7 +359,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, // operateHeaders takes action on the decoded headers. Returns an error if fatal // error encountered and transport needs to close, otherwise returns nil. -func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) error { +func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeadersFrame, handle func(*Stream)) error { // Acquire max stream ID lock for entire duration t.maxStreamMu.Lock() defer t.maxStreamMu.Unlock() @@ -369,10 +386,11 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( buf := newRecvBuffer() s := &Stream{ - id: streamID, - st: t, - buf: buf, - fc: &inFlow{limit: uint32(t.initialWindowSize)}, + id: streamID, + st: t, + buf: buf, + fc: &inFlow{limit: uint32(t.initialWindowSize)}, + headerWireLength: int(frame.Header().Length), } var ( // if false, content-type was missing or invalid @@ -511,9 +529,9 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( s.state = streamReadDone } if timeoutSet { - s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout) + s.ctx, s.cancel = context.WithTimeout(ctx, timeout) } else { - s.ctx, s.cancel = context.WithCancel(t.ctx) + s.ctx, s.cancel = context.WithCancel(ctx) } // Attach the received metadata to the context. @@ -561,7 +579,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( } if t.inTapHandle != nil { var err error - if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method}); err != nil { + if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method, Header: mdata}); err != nil { t.mu.Unlock() if t.logger.V(logLevel) { t.logger.Infof("Aborting the stream early due to InTapHandle failure: %v", err) @@ -586,25 +604,12 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( } t.mu.Unlock() if channelz.IsOn() { - atomic.AddInt64(&t.czData.streamsStarted, 1) - atomic.StoreInt64(&t.czData.lastStreamCreatedTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.StreamsStarted.Add(1) + t.channelz.SocketMetrics.LastRemoteStreamCreatedTimestamp.Store(time.Now().UnixNano()) } s.requestRead = func(n int) { t.adjustWindow(s, uint32(n)) } - s.ctx = traceCtx(s.ctx, s.method) - for _, sh := range t.stats { - s.ctx = sh.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method}) - inHeader := &stats.InHeader{ - FullMethod: s.method, - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - Compression: s.recvCompress, - WireLength: int(frame.Header().Length), - Header: mdata.Copy(), - } - sh.HandleRPC(s.ctx, inHeader) - } s.ctxDone = s.ctx.Done() s.wq = newWriteQuota(defaultWriteQuota, s.ctxDone) s.trReader = &transportReader{ @@ -630,8 +635,11 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( // HandleStreams receives incoming streams using the given handler. This is // typically run in a separate goroutine. // traceCtx attaches trace to ctx and returns the new context. -func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) { - defer close(t.readerDone) +func (t *http2Server) HandleStreams(ctx context.Context, handle func(*Stream)) { + defer func() { + close(t.readerDone) + <-t.loopyWriterDone + }() for { t.controlBuf.throttle() frame, err := t.framer.fr.ReadFrame() @@ -656,18 +664,20 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context. } continue } - if err == io.EOF || err == io.ErrUnexpectedEOF { - t.Close(err) - return - } t.Close(err) return } switch frame := frame.(type) { case *http2.MetaHeadersFrame: - if err := t.operateHeaders(frame, handle, traceCtx); err != nil { - t.Close(err) - break + if err := t.operateHeaders(ctx, frame, handle); err != nil { + // Any error processing client headers, e.g. invalid stream ID, + // is considered a protocol violation. + t.controlBuf.put(&goAway{ + code: http2.ErrCodeProtocol, + debugData: []byte(err.Error()), + closeConn: err, + }) + continue } case *http2.DataFrame: t.handleData(frame) @@ -850,7 +860,7 @@ func (t *http2Server) handleSettings(f *http2.SettingsFrame) { } return nil }) - t.controlBuf.executeAndPut(func(interface{}) bool { + t.controlBuf.executeAndPut(func(any) bool { for _, f := range updateFuncs { f() } @@ -934,7 +944,7 @@ func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD) return headerFields } -func (t *http2Server) checkForHeaderListSize(it interface{}) bool { +func (t *http2Server) checkForHeaderListSize(it any) bool { if t.maxSendHeaderListSize == nil { return true } @@ -980,7 +990,12 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { } } if err := t.writeHeaderLocked(s); err != nil { - return status.Convert(err).Err() + switch e := err.(type) { + case ConnectionError: + return status.Error(codes.Unavailable, e.Desc) + default: + return status.Convert(err).Err() + } } return nil } @@ -1053,12 +1068,15 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error { headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())}) if p := st.Proto(); p != nil && len(p.Details) > 0 { + // Do not use the user's grpc-status-details-bin (if present) if we are + // even attempting to set our own. + delete(s.trailer, grpcStatusDetailsBinHeader) stBytes, err := proto.Marshal(p) if err != nil { // TODO: return error instead, when callers are able to handle it. t.logger.Errorf("Failed to marshal rpc status: %s, error: %v", pretty.ToJSON(p), err) } else { - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)}) + headerFields = append(headerFields, hpack.HeaderField{Name: grpcStatusDetailsBinHeader, Value: encodeBinHeader(stBytes)}) } } @@ -1195,7 +1213,7 @@ func (t *http2Server) keepalive() { } if !outstandingPing { if channelz.IsOn() { - atomic.AddInt64(&t.czData.kpCount, 1) + t.channelz.SocketMetrics.KeepAlivesSent.Add(1) } t.controlBuf.put(p) kpTimeoutLeft = t.kp.Timeout @@ -1235,15 +1253,11 @@ func (t *http2Server) Close(err error) { if err := t.conn.Close(); err != nil && t.logger.V(logLevel) { t.logger.Infof("Error closing underlying net.Conn during Close: %v", err) } - channelz.RemoveEntry(t.channelzID) + channelz.RemoveEntry(t.channelz.ID) // Cancel all active streams. for _, s := range streams { s.cancel() } - for _, sh := range t.stats { - connEnd := &stats.ConnEnd{} - sh.HandleConn(t.ctx, connEnd) - } } // deleteStream deletes the stream s from transport's active streams. @@ -1260,9 +1274,9 @@ func (t *http2Server) deleteStream(s *Stream, eosReceived bool) { if channelz.IsOn() { if eosReceived { - atomic.AddInt64(&t.czData.streamsSucceeded, 1) + t.channelz.SocketMetrics.StreamsSucceeded.Add(1) } else { - atomic.AddInt64(&t.czData.streamsFailed, 1) + t.channelz.SocketMetrics.StreamsFailed.Add(1) } } } @@ -1309,10 +1323,6 @@ func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eo }) } -func (t *http2Server) RemoteAddr() net.Addr { - return t.remoteAddr -} - func (t *http2Server) Drain(debugData string) { t.mu.Lock() defer t.mu.Unlock() @@ -1349,6 +1359,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil { return false, err } + t.framer.writer.Flush() if retErr != nil { return false, retErr } @@ -1369,7 +1380,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { return false, err } go func() { - timer := time.NewTimer(time.Minute) + timer := time.NewTimer(5 * time.Second) defer timer.Stop() select { case <-t.drainEvent.Done(): @@ -1382,38 +1393,21 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { return false, nil } -func (t *http2Server) ChannelzMetric() *channelz.SocketInternalMetric { - s := channelz.SocketInternalMetric{ - StreamsStarted: atomic.LoadInt64(&t.czData.streamsStarted), - StreamsSucceeded: atomic.LoadInt64(&t.czData.streamsSucceeded), - StreamsFailed: atomic.LoadInt64(&t.czData.streamsFailed), - MessagesSent: atomic.LoadInt64(&t.czData.msgSent), - MessagesReceived: atomic.LoadInt64(&t.czData.msgRecv), - KeepAlivesSent: atomic.LoadInt64(&t.czData.kpCount), - LastRemoteStreamCreatedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastStreamCreatedTime)), - LastMessageSentTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgSentTime)), - LastMessageReceivedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgRecvTime)), - LocalFlowControlWindow: int64(t.fc.getSize()), - SocketOptions: channelz.GetSocketOption(t.conn), - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // RemoteName : - } - if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok { - s.Security = au.GetSecurityValue() - } - s.RemoteFlowControlWindow = t.getOutFlowWindow() - return &s +func (t *http2Server) socketMetrics() *channelz.EphemeralSocketMetrics { + return &channelz.EphemeralSocketMetrics{ + LocalFlowControlWindow: int64(t.fc.getSize()), + RemoteFlowControlWindow: t.getOutFlowWindow(), + } } func (t *http2Server) IncrMsgSent() { - atomic.AddInt64(&t.czData.msgSent, 1) - atomic.StoreInt64(&t.czData.lastMsgSentTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.MessagesSent.Add(1) + t.channelz.SocketMetrics.LastMessageSentTimestamp.Add(1) } func (t *http2Server) IncrMsgRecv() { - atomic.AddInt64(&t.czData.msgRecv, 1) - atomic.StoreInt64(&t.czData.lastMsgRecvTime, time.Now().UnixNano()) + t.channelz.SocketMetrics.MessagesReceived.Add(1) + t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Add(1) } func (t *http2Server) getOutFlowWindow() int64 { @@ -1431,10 +1425,12 @@ func (t *http2Server) getOutFlowWindow() int64 { } } -func (t *http2Server) getPeer() *peer.Peer { +// Peer returns the peer of the transport. +func (t *http2Server) Peer() *peer.Peer { return &peer.Peer{ - Addr: t.remoteAddr, - AuthInfo: t.authInfo, // Can be nil + Addr: t.peer.Addr, + LocalAddr: t.peer.LocalAddr, + AuthInfo: t.peer.AuthInfo, // Can be nil } } @@ -1459,6 +1455,6 @@ func GetConnection(ctx context.Context) net.Conn { // SetConnection adds the connection to the context to be able to get // information about the destination ip and port for an incoming RPC. This also // allows any unary or streaming interceptors to see the connection. -func setConnection(ctx context.Context, conn net.Conn) context.Context { +func SetConnection(ctx context.Context, conn net.Conn) context.Context { return context.WithValue(ctx, connectionKey{}, conn) } diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go index 19cbb18f5..39cef3bd4 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http_util.go +++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go @@ -30,15 +30,13 @@ import ( "net/url" "strconv" "strings" + "sync" "time" "unicode/utf8" - "github.com/golang/protobuf/proto" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" - spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) const ( @@ -87,6 +85,8 @@ var ( } ) +var grpcStatusDetailsBinHeader = "grpc-status-details-bin" + // isReservedHeader checks whether hdr belongs to HTTP2 headers // reserved by gRPC protocol. Any other headers are classified as the // user-specified metadata. @@ -102,7 +102,6 @@ func isReservedHeader(hdr string) bool { "grpc-message", "grpc-status", "grpc-timeout", - "grpc-status-details-bin", // Intentionally exclude grpc-previous-rpc-attempts and // grpc-retry-pushback-ms, which are "reserved", but their API // intentionally works via metadata. @@ -153,18 +152,6 @@ func decodeMetadataHeader(k, v string) (string, error) { return v, nil } -func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) { - v, err := decodeBinHeader(rawDetails) - if err != nil { - return nil, err - } - st := &spb.Status{} - if err = proto.Unmarshal(v, st); err != nil { - return nil, err - } - return status.FromProto(st), nil -} - type timeoutUnit uint8 const ( @@ -309,6 +296,7 @@ func decodeGrpcMessageUnchecked(msg string) string { } type bufWriter struct { + pool *sync.Pool buf []byte offset int batchSize int @@ -316,12 +304,17 @@ type bufWriter struct { err error } -func newBufWriter(conn net.Conn, batchSize int) *bufWriter { - return &bufWriter{ - buf: make([]byte, batchSize*2), +func newBufWriter(conn net.Conn, batchSize int, pool *sync.Pool) *bufWriter { + w := &bufWriter{ batchSize: batchSize, conn: conn, + pool: pool, + } + // this indicates that we should use non shared buf + if pool == nil { + w.buf = make([]byte, batchSize) } + return w } func (w *bufWriter) Write(b []byte) (n int, err error) { @@ -332,19 +325,34 @@ func (w *bufWriter) Write(b []byte) (n int, err error) { n, err = w.conn.Write(b) return n, toIOError(err) } + if w.buf == nil { + b := w.pool.Get().(*[]byte) + w.buf = *b + } for len(b) > 0 { nn := copy(w.buf[w.offset:], b) b = b[nn:] w.offset += nn n += nn if w.offset >= w.batchSize { - err = w.Flush() + err = w.flushKeepBuffer() } } return n, err } func (w *bufWriter) Flush() error { + err := w.flushKeepBuffer() + // Only release the buffer if we are in a "shared" mode + if w.buf != nil && w.pool != nil { + b := w.buf + w.pool.Put(&b) + w.buf = nil + } + return err +} + +func (w *bufWriter) flushKeepBuffer() error { if w.err != nil { return w.err } @@ -381,7 +389,10 @@ type framer struct { fr *http2.Framer } -func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderListSize uint32) *framer { +var writeBufferPoolMap map[int]*sync.Pool = make(map[int]*sync.Pool) +var writeBufferMutex sync.Mutex + +func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, sharedWriteBuffer bool, maxHeaderListSize uint32) *framer { if writeBufferSize < 0 { writeBufferSize = 0 } @@ -389,7 +400,11 @@ func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderList if readBufferSize > 0 { r = bufio.NewReaderSize(r, readBufferSize) } - w := newBufWriter(conn, writeBufferSize) + var pool *sync.Pool + if sharedWriteBuffer { + pool = getWriteBufferPool(writeBufferSize) + } + w := newBufWriter(conn, writeBufferSize, pool) f := &framer{ writer: w, fr: http2.NewFramer(w, r), @@ -403,6 +418,23 @@ func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderList return f } +func getWriteBufferPool(size int) *sync.Pool { + writeBufferMutex.Lock() + defer writeBufferMutex.Unlock() + pool, ok := writeBufferPoolMap[size] + if ok { + return pool + } + pool = &sync.Pool{ + New: func() any { + b := make([]byte, size) + return &b + }, + } + writeBufferPoolMap[size] = pool + return pool +} + // parseDialTarget returns the network and address to pass to dialer. func parseDialTarget(target string) (string, string) { net := "tcp" diff --git a/vendor/google.golang.org/grpc/internal/transport/proxy.go b/vendor/google.golang.org/grpc/internal/transport/proxy.go index 415961987..24fa10325 100644 --- a/vendor/google.golang.org/grpc/internal/transport/proxy.go +++ b/vendor/google.golang.org/grpc/internal/transport/proxy.go @@ -28,6 +28,8 @@ import ( "net/http" "net/http/httputil" "net/url" + + "google.golang.org/grpc/internal" ) const proxyAuthHeaderKey = "Proxy-Authorization" @@ -112,7 +114,7 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri // proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy // is necessary, dials, does the HTTP CONNECT handshake, and returns the // connection. -func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) { +func proxyDial(ctx context.Context, addr string, grpcUA string) (net.Conn, error) { newAddr := addr proxyURL, err := mapAddress(addr) if err != nil { @@ -122,15 +124,15 @@ func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, newAddr = proxyURL.Host } - conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", newAddr) + conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", newAddr) if err != nil { - return + return nil, err } - if proxyURL != nil { + if proxyURL == nil { // proxy is disabled if proxyURL is nil. - conn, err = doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA) + return conn, err } - return + return doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA) } func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index aa1c89659..0d2a6e47f 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -28,6 +28,7 @@ import ( "fmt" "io" "net" + "strings" "sync" "sync/atomic" "time" @@ -37,16 +38,13 @@ import ( "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" "google.golang.org/grpc/resolver" "google.golang.org/grpc/stats" "google.golang.org/grpc/status" "google.golang.org/grpc/tap" ) -// ErrNoHeaders is used as a signal that a trailers only response was received, -// and is not a real error. -var ErrNoHeaders = errors.New("stream has no headers") - const logLevel = 2 type bufferPool struct { @@ -56,7 +54,7 @@ type bufferPool struct { func newBufferPool() *bufferPool { return &bufferPool{ pool: sync.Pool{ - New: func() interface{} { + New: func() any { return new(bytes.Buffer) }, }, @@ -269,7 +267,8 @@ type Stream struct { // headerValid indicates whether a valid header was received. Only // meaningful after headerChan is closed (always call waitOnHeader() before // reading its value). Not valid on server side. - headerValid bool + headerValid bool + headerWireLength int // Only set on server side. // hdrMu protects header and trailer metadata on the server-side. hdrMu sync.Mutex @@ -364,8 +363,12 @@ func (s *Stream) SendCompress() string { // ClientAdvertisedCompressors returns the compressor names advertised by the // client via grpc-accept-encoding header. -func (s *Stream) ClientAdvertisedCompressors() string { - return s.clientAdvertisedCompressors +func (s *Stream) ClientAdvertisedCompressors() []string { + values := strings.Split(s.clientAdvertisedCompressors, ",") + for i, v := range values { + values[i] = strings.TrimSpace(v) + } + return values } // Done returns a channel which is closed when it receives the final status @@ -390,14 +393,10 @@ func (s *Stream) Header() (metadata.MD, error) { } s.waitOnHeader() - if !s.headerValid { + if !s.headerValid || s.noHeaders { return nil, s.status.Err() } - if s.noHeaders { - return nil, ErrNoHeaders - } - return s.header.Copy(), nil } @@ -433,6 +432,12 @@ func (s *Stream) Context() context.Context { return s.ctx } +// SetContext sets the context of the stream. This will be deleted once the +// stats handler callouts all move to gRPC layer. +func (s *Stream) SetContext(ctx context.Context) { + s.ctx = ctx +} + // Method returns the method for the stream. func (s *Stream) Method() string { return s.method @@ -445,6 +450,12 @@ func (s *Stream) Status() *status.Status { return s.status } +// HeaderWireLength returns the size of the headers of the stream as received +// from the wire. Valid only on the server. +func (s *Stream) HeaderWireLength() int { + return s.headerWireLength +} + // SetHeader sets the header metadata. This can be called multiple times. // Server side only. // This should not be called in parallel to other data writes. @@ -559,7 +570,8 @@ type ServerConfig struct { InitialConnWindowSize int32 WriteBufferSize int ReadBufferSize int - ChannelzParentID *channelz.Identifier + SharedWriteBuffer bool + ChannelzParent *channelz.Server MaxHeaderListSize *uint32 HeaderTableSize *uint32 } @@ -592,8 +604,10 @@ type ConnectOptions struct { WriteBufferSize int // ReadBufferSize sets the size of read buffer, which in turn determines how much data can be read at most for one read syscall. ReadBufferSize int - // ChannelzParentID sets the addrConn id which initiate the creation of this client transport. - ChannelzParentID *channelz.Identifier + // SharedWriteBuffer indicates whether connections should reuse write buffer + SharedWriteBuffer bool + // ChannelzParent sets the addrConn id which initiated the creation of this client transport. + ChannelzParent *channelz.SubChannel // MaxHeaderListSize sets the max (uncompressed) size of header list that is prepared to be received. MaxHeaderListSize *uint32 // UseProxy specifies if a proxy should be used. @@ -703,7 +717,7 @@ type ClientTransport interface { // Write methods for a given Stream will be called serially. type ServerTransport interface { // HandleStreams receives incoming streams using the given handler. - HandleStreams(func(*Stream), func(context.Context, string) context.Context) + HandleStreams(context.Context, func(*Stream)) // WriteHeader sends the header metadata for the given stream. // WriteHeader may not be called on all streams. @@ -722,8 +736,8 @@ type ServerTransport interface { // handlers will be terminated asynchronously. Close(err error) - // RemoteAddr returns the remote network address. - RemoteAddr() net.Addr + // Peer returns the peer of the server transport. + Peer() *peer.Peer // Drain notifies the client this ServerTransport stops accepting new RPCs. Drain(debugData string) @@ -736,7 +750,7 @@ type ServerTransport interface { } // connectionErrorf creates an ConnectionError with the specified error description. -func connectionErrorf(temp bool, e error, format string, a ...interface{}) ConnectionError { +func connectionErrorf(temp bool, e error, format string, a ...any) ConnectionError { return ConnectionError{ Desc: fmt.Sprintf(format, a...), temp: temp, @@ -806,30 +820,6 @@ const ( GoAwayTooManyPings GoAwayReason = 2 ) -// channelzData is used to store channelz related data for http2Client and http2Server. -// These fields cannot be embedded in the original structs (e.g. http2Client), since to do atomic -// operation on int64 variable on 32-bit machine, user is responsible to enforce memory alignment. -// Here, by grouping those int64 fields inside a struct, we are enforcing the alignment. -type channelzData struct { - kpCount int64 - // The number of streams that have started, including already finished ones. - streamsStarted int64 - // Client side: The number of streams that have ended successfully by receiving - // EoS bit set frame from server. - // Server side: The number of streams that have ended successfully by sending - // frame with EoS bit set. - streamsSucceeded int64 - streamsFailed int64 - // lastStreamCreatedTime stores the timestamp that the last stream gets created. It is of int64 type - // instead of time.Time since it's more costly to atomically update time.Time variable than int64 - // variable. The same goes for lastMsgSentTime and lastMsgRecvTime. - lastStreamCreatedTime int64 - msgSent int64 - msgRecv int64 - lastMsgSentTime int64 - lastMsgRecvTime int64 -} - // ContextErr converts the error from context package into a status error. func ContextErr(err error) error { switch err { diff --git a/vendor/google.golang.org/grpc/internal/xds_handshake_cluster.go b/vendor/google.golang.org/grpc/internal/xds_handshake_cluster.go deleted file mode 100644 index e8b492774..000000000 --- a/vendor/google.golang.org/grpc/internal/xds_handshake_cluster.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package internal - -import ( - "google.golang.org/grpc/attributes" - "google.golang.org/grpc/resolver" -) - -// handshakeClusterNameKey is the type used as the key to store cluster name in -// the Attributes field of resolver.Address. -type handshakeClusterNameKey struct{} - -// SetXDSHandshakeClusterName returns a copy of addr in which the Attributes field -// is updated with the cluster name. -func SetXDSHandshakeClusterName(addr resolver.Address, clusterName string) resolver.Address { - addr.Attributes = addr.Attributes.WithValue(handshakeClusterNameKey{}, clusterName) - return addr -} - -// GetXDSHandshakeClusterName returns cluster name stored in attr. -func GetXDSHandshakeClusterName(attr *attributes.Attributes) (string, bool) { - v := attr.Value(handshakeClusterNameKey{}) - name, ok := v.(string) - return name, ok -} diff --git a/vendor/google.golang.org/grpc/metadata/metadata.go b/vendor/google.golang.org/grpc/metadata/metadata.go index a2cdcaf12..1e9485fd6 100644 --- a/vendor/google.golang.org/grpc/metadata/metadata.go +++ b/vendor/google.golang.org/grpc/metadata/metadata.go @@ -25,8 +25,14 @@ import ( "context" "fmt" "strings" + + "google.golang.org/grpc/internal" ) +func init() { + internal.FromOutgoingContextRaw = fromOutgoingContextRaw +} + // DecodeKeyValue returns k, v, nil. // // Deprecated: use k and v directly instead. @@ -153,14 +159,16 @@ func Join(mds ...MD) MD { type mdIncomingKey struct{} type mdOutgoingKey struct{} -// NewIncomingContext creates a new context with incoming md attached. +// NewIncomingContext creates a new context with incoming md attached. md must +// not be modified after calling this function. func NewIncomingContext(ctx context.Context, md MD) context.Context { return context.WithValue(ctx, mdIncomingKey{}, md) } // NewOutgoingContext creates a new context with outgoing md attached. If used // in conjunction with AppendToOutgoingContext, NewOutgoingContext will -// overwrite any previously-appended metadata. +// overwrite any previously-appended metadata. md must not be modified after +// calling this function. func NewOutgoingContext(ctx context.Context, md MD) context.Context { return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md}) } @@ -203,7 +211,8 @@ func FromIncomingContext(ctx context.Context) (MD, bool) { } // ValueFromIncomingContext returns the metadata value corresponding to the metadata -// key from the incoming metadata if it exists. Key must be lower-case. +// key from the incoming metadata if it exists. Keys are matched in a case insensitive +// manner. // // # Experimental // @@ -219,33 +228,29 @@ func ValueFromIncomingContext(ctx context.Context, key string) []string { return copyOf(v) } for k, v := range md { - // We need to manually convert all keys to lower case, because MD is a - // map, and there's no guarantee that the MD attached to the context is - // created using our helper functions. - if strings.ToLower(k) == key { + // Case insenitive comparison: MD is a map, and there's no guarantee + // that the MD attached to the context is created using our helper + // functions. + if strings.EqualFold(k, key) { return copyOf(v) } } return nil } -// the returned slice must not be modified in place func copyOf(v []string) []string { vals := make([]string, len(v)) copy(vals, v) return vals } -// FromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD. +// fromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD. // // Remember to perform strings.ToLower on the keys, for both the returned MD (MD // is a map, there's no guarantee it's created using our helper functions) and // the extra kv pairs (AppendToOutgoingContext doesn't turn them into // lowercase). -// -// This is intended for gRPC-internal use ONLY. Users should use -// FromOutgoingContext instead. -func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) { +func fromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) { raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) if !ok { return nil, nil, false diff --git a/vendor/google.golang.org/grpc/peer/peer.go b/vendor/google.golang.org/grpc/peer/peer.go index e01d219ff..a821ff9b2 100644 --- a/vendor/google.golang.org/grpc/peer/peer.go +++ b/vendor/google.golang.org/grpc/peer/peer.go @@ -32,6 +32,8 @@ import ( type Peer struct { // Addr is the peer address. Addr net.Addr + // LocalAddr is the local address. + LocalAddr net.Addr // AuthInfo is the authentication information of the transport. // It is nil if there is no transport security being used. AuthInfo credentials.AuthInfo diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go index 02f975951..bf56faa76 100644 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ b/vendor/google.golang.org/grpc/picker_wrapper.go @@ -28,31 +28,31 @@ import ( "google.golang.org/grpc/internal/channelz" istatus "google.golang.org/grpc/internal/status" "google.golang.org/grpc/internal/transport" + "google.golang.org/grpc/stats" "google.golang.org/grpc/status" ) // pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick // actions and unblock when there's a picker update. type pickerWrapper struct { - mu sync.Mutex - done bool - idle bool - blockingCh chan struct{} - picker balancer.Picker + mu sync.Mutex + done bool + blockingCh chan struct{} + picker balancer.Picker + statsHandlers []stats.Handler // to record blocking picker calls } -func newPickerWrapper() *pickerWrapper { - return &pickerWrapper{blockingCh: make(chan struct{})} +func newPickerWrapper(statsHandlers []stats.Handler) *pickerWrapper { + return &pickerWrapper{ + blockingCh: make(chan struct{}), + statsHandlers: statsHandlers, + } } // updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. func (pw *pickerWrapper) updatePicker(p balancer.Picker) { pw.mu.Lock() - if pw.done || pw.idle { - // There is a small window where a picker update from the LB policy can - // race with the channel going to idle mode. If the picker is idle here, - // it is because the channel asked it to do so, and therefore it is sage - // to ignore the update from the LB policy. + if pw.done { pw.mu.Unlock() return } @@ -95,6 +95,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. var ch chan struct{} var lastPickErr error + for { pw.mu.Lock() if pw.done { @@ -129,6 +130,20 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. continue } + // If the channel is set, it means that the pick call had to wait for a + // new picker at some point. Either it's the first iteration and this + // function received the first picker, or a picker errored with + // ErrNoSubConnAvailable or errored with failfast set to false, which + // will trigger a continue to the next iteration. In the first case this + // conditional will hit if this call had to block (the channel is set). + // In the second case, the only way it will get to this conditional is + // if there is a new picker. + if ch != nil { + for _, sh := range pw.statsHandlers { + sh.HandleRPC(ctx, &stats.PickerUpdated{}) + } + } + ch = pw.blockingCh p := pw.picker pw.mu.Unlock() @@ -190,23 +205,15 @@ func (pw *pickerWrapper) close() { close(pw.blockingCh) } -func (pw *pickerWrapper) enterIdleMode() { - pw.mu.Lock() - defer pw.mu.Unlock() - if pw.done { - return - } - pw.idle = true -} - -func (pw *pickerWrapper) exitIdleMode() { +// reset clears the pickerWrapper and prepares it for being used again when idle +// mode is exited. +func (pw *pickerWrapper) reset() { pw.mu.Lock() defer pw.mu.Unlock() if pw.done { return } pw.blockingCh = make(chan struct{}) - pw.idle = false } // dropError is a wrapper error that indicates the LB policy wishes to drop the diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/pickfirst.go index abe266b02..e3ea42ba9 100644 --- a/vendor/google.golang.org/grpc/pickfirst.go +++ b/vendor/google.golang.org/grpc/pickfirst.go @@ -25,25 +25,28 @@ import ( "google.golang.org/grpc/balancer" "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/internal/envconfig" + internalgrpclog "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcrand" + "google.golang.org/grpc/internal/pretty" + "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" ) -// PickFirstBalancerName is the name of the pick_first balancer. -const PickFirstBalancerName = "pick_first" - -func newPickfirstBuilder() balancer.Builder { - return &pickfirstBuilder{} -} +const ( + // PickFirstBalancerName is the name of the pick_first balancer. + PickFirstBalancerName = "pick_first" + logPrefix = "[pick-first-lb %p] " +) type pickfirstBuilder struct{} -func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { - return &pickfirstBalancer{cc: cc} +func (pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { + b := &pickfirstBalancer{cc: cc} + b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(logPrefix, b)) + return b } -func (*pickfirstBuilder) Name() string { +func (pickfirstBuilder) Name() string { return PickFirstBalancerName } @@ -56,24 +59,24 @@ type pfConfig struct { ShuffleAddressList bool `json:"shuffleAddressList"` } -func (*pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { - cfg := &pfConfig{} - if err := json.Unmarshal(js, cfg); err != nil { +func (pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { + var cfg pfConfig + if err := json.Unmarshal(js, &cfg); err != nil { return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err) } return cfg, nil } type pickfirstBalancer struct { + logger *internalgrpclog.PrefixLogger state connectivity.State cc balancer.ClientConn subConn balancer.SubConn - cfg *pfConfig } func (b *pickfirstBalancer) ResolverError(err error) { - if logger.V(2) { - logger.Infof("pickfirstBalancer: ResolverError called with error: %v", err) + if b.logger.V(2) { + b.logger.Infof("Received error from the name resolver: %v", err) } if b.subConn == nil { b.state = connectivity.TransientFailure @@ -96,35 +99,44 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState // The resolver reported an empty address list. Treat it like an error by // calling b.ResolverError. if b.subConn != nil { - // Remove the old subConn. All addresses were removed, so it is no longer - // valid. - b.cc.RemoveSubConn(b.subConn) + // Shut down the old subConn. All addresses were removed, so it is + // no longer valid. + b.subConn.Shutdown() b.subConn = nil } b.ResolverError(errors.New("produced zero addresses")) return balancer.ErrBadResolverState } - if state.BalancerConfig != nil { - cfg, ok := state.BalancerConfig.(*pfConfig) - if !ok { - return fmt.Errorf("pickfirstBalancer: received nil or illegal BalancerConfig (type %T): %v", state.BalancerConfig, state.BalancerConfig) - } - b.cfg = cfg + // We don't have to guard this block with the env var because ParseConfig + // already does so. + cfg, ok := state.BalancerConfig.(pfConfig) + if state.BalancerConfig != nil && !ok { + return fmt.Errorf("pickfirst: received illegal BalancerConfig (type %T): %v", state.BalancerConfig, state.BalancerConfig) } - - if envconfig.PickFirstLBConfig && b.cfg != nil && b.cfg.ShuffleAddressList { + if cfg.ShuffleAddressList { + addrs = append([]resolver.Address{}, addrs...) grpcrand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) } + + if b.logger.V(2) { + b.logger.Infof("Received new config %s, resolver state %s", pretty.ToJSON(cfg), pretty.ToJSON(state.ResolverState)) + } + if b.subConn != nil { b.cc.UpdateAddresses(b.subConn, addrs) return nil } - subConn, err := b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{}) + var subConn balancer.SubConn + subConn, err := b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{ + StateListener: func(state balancer.SubConnState) { + b.updateSubConnState(subConn, state) + }, + }) if err != nil { - if logger.V(2) { - logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) + if b.logger.V(2) { + b.logger.Infof("Failed to create new SubConn: %v", err) } b.state = connectivity.TransientFailure b.cc.UpdateState(balancer.State{ @@ -143,13 +155,19 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState return nil } +// UpdateSubConnState is unused as a StateListener is always registered when +// creating SubConns. func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { - if logger.V(2) { - logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", subConn, state) + b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", subConn, state) +} + +func (b *pickfirstBalancer) updateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { + if b.logger.V(2) { + b.logger.Infof("Received SubConn state update: %p, %+v", subConn, state) } if b.subConn != subConn { - if logger.V(2) { - logger.Infof("pickfirstBalancer: ignored state change because subConn is not recognized") + if b.logger.V(2) { + b.logger.Infof("Ignored state change because subConn is not recognized") } return } @@ -221,7 +239,3 @@ func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { i.subConn.Connect() return balancer.PickResult{}, balancer.ErrNoSubConnAvailable } - -func init() { - balancer.Register(newPickfirstBuilder()) -} diff --git a/vendor/google.golang.org/grpc/preloader.go b/vendor/google.golang.org/grpc/preloader.go index cd4554785..73bd63364 100644 --- a/vendor/google.golang.org/grpc/preloader.go +++ b/vendor/google.golang.org/grpc/preloader.go @@ -37,7 +37,7 @@ type PreparedMsg struct { } // Encode marshalls and compresses the message using the codec and compressor for the stream. -func (p *PreparedMsg) Encode(s Stream, msg interface{}) error { +func (p *PreparedMsg) Encode(s Stream, msg any) error { ctx := s.Context() rpcInfo, ok := rpcInfoFromContext(ctx) if !ok { diff --git a/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go new file mode 100644 index 000000000..b54a3a322 --- /dev/null +++ b/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package dns implements a dns resolver to be installed as the default resolver +// in grpc. +// +// Deprecated: this package is imported by grpc and should not need to be +// imported directly by users. +package dns + +import ( + "time" + + "google.golang.org/grpc/internal/resolver/dns" + "google.golang.org/grpc/resolver" +) + +// SetResolvingTimeout sets the maximum duration for DNS resolution requests. +// +// This function affects the global timeout used by all channels using the DNS +// name resolver scheme. +// +// It must be called only at application startup, before any gRPC calls are +// made. Modifying this value after initialization is not thread-safe. +// +// The default value is 30 seconds. Setting the timeout too low may result in +// premature timeouts during resolution, while setting it too high may lead to +// unnecessary delays in service discovery. Choose a value appropriate for your +// specific needs and network environment. +func SetResolvingTimeout(timeout time.Duration) { + dns.ResolvingTimeout = timeout +} + +// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers. +// +// Deprecated: import grpc and use resolver.Get("dns") instead. +func NewBuilder() resolver.Builder { + return dns.NewBuilder() +} diff --git a/vendor/google.golang.org/grpc/resolver/manual/manual.go b/vendor/google.golang.org/grpc/resolver/manual/manual.go index f27978e12..f2efa2a2c 100644 --- a/vendor/google.golang.org/grpc/resolver/manual/manual.go +++ b/vendor/google.golang.org/grpc/resolver/manual/manual.go @@ -26,13 +26,16 @@ import ( "google.golang.org/grpc/resolver" ) -// NewBuilderWithScheme creates a new test resolver builder with the given scheme. +// NewBuilderWithScheme creates a new manual resolver builder with the given +// scheme. Every instance of the manual resolver may only ever be used with a +// single grpc.ClientConn. Otherwise, bad things will happen. func NewBuilderWithScheme(scheme string) *Resolver { return &Resolver{ - BuildCallback: func(resolver.Target, resolver.ClientConn, resolver.BuildOptions) {}, - ResolveNowCallback: func(resolver.ResolveNowOptions) {}, - CloseCallback: func() {}, - scheme: scheme, + BuildCallback: func(resolver.Target, resolver.ClientConn, resolver.BuildOptions) {}, + UpdateStateCallback: func(error) {}, + ResolveNowCallback: func(resolver.ResolveNowOptions) {}, + CloseCallback: func() {}, + scheme: scheme, } } @@ -42,6 +45,11 @@ type Resolver struct { // BuildCallback is called when the Build method is called. Must not be // nil. Must not be changed after the resolver may be built. BuildCallback func(resolver.Target, resolver.ClientConn, resolver.BuildOptions) + // UpdateStateCallback is called when the UpdateState method is called on + // the resolver. The value passed as argument to this callback is the value + // returned by the resolver.ClientConn. Must not be nil. Must not be + // changed after the resolver may be built. + UpdateStateCallback func(err error) // ResolveNowCallback is called when the ResolveNow method is called on the // resolver. Must not be nil. Must not be changed after the resolver may // be built. @@ -52,30 +60,34 @@ type Resolver struct { scheme string // Fields actually belong to the resolver. - mu sync.Mutex // Guards access to CC. - CC resolver.ClientConn - bootstrapState *resolver.State + // Guards access to below fields. + mu sync.Mutex + CC resolver.ClientConn + // Storing the most recent state update makes this resolver resilient to + // restarts, which is possible with channel idleness. + lastSeenState *resolver.State } // InitialState adds initial state to the resolver so that UpdateState doesn't // need to be explicitly called after Dial. func (r *Resolver) InitialState(s resolver.State) { - r.bootstrapState = &s + r.lastSeenState = &s } // Build returns itself for Resolver, because it's both a builder and a resolver. func (r *Resolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { + r.BuildCallback(target, cc, opts) r.mu.Lock() + defer r.mu.Unlock() r.CC = cc - r.mu.Unlock() - r.BuildCallback(target, cc, opts) - if r.bootstrapState != nil { - r.UpdateState(*r.bootstrapState) + if r.lastSeenState != nil { + err := r.CC.UpdateState(*r.lastSeenState) + go r.UpdateStateCallback(err) } return r, nil } -// Scheme returns the test scheme. +// Scheme returns the manual resolver's scheme. func (r *Resolver) Scheme() string { return r.scheme } @@ -93,13 +105,22 @@ func (r *Resolver) Close() { // UpdateState calls CC.UpdateState. func (r *Resolver) UpdateState(s resolver.State) { r.mu.Lock() - r.CC.UpdateState(s) - r.mu.Unlock() + defer r.mu.Unlock() + var err error + if r.CC == nil { + panic("cannot update state as grpc.Dial with resolver has not been called") + } + err = r.CC.UpdateState(s) + r.lastSeenState = &s + r.UpdateStateCallback(err) } // ReportError calls CC.ReportError. func (r *Resolver) ReportError(err error) { r.mu.Lock() + defer r.mu.Unlock() + if r.CC == nil { + panic("cannot report error as grpc.Dial with resolver has not been called") + } r.CC.ReportError(err) - r.mu.Unlock() } diff --git a/vendor/google.golang.org/grpc/resolver/map.go b/vendor/google.golang.org/grpc/resolver/map.go index efcb7f3ef..ada5b9bb7 100644 --- a/vendor/google.golang.org/grpc/resolver/map.go +++ b/vendor/google.golang.org/grpc/resolver/map.go @@ -20,7 +20,7 @@ package resolver type addressMapEntry struct { addr Address - value interface{} + value any } // AddressMap is a map of addresses to arbitrary values taking into account @@ -69,7 +69,7 @@ func (l addressMapEntryList) find(addr Address) int { } // Get returns the value for the address in the map, if present. -func (a *AddressMap) Get(addr Address) (value interface{}, ok bool) { +func (a *AddressMap) Get(addr Address) (value any, ok bool) { addrKey := toMapKey(&addr) entryList := a.m[addrKey] if entry := entryList.find(addr); entry != -1 { @@ -79,7 +79,7 @@ func (a *AddressMap) Get(addr Address) (value interface{}, ok bool) { } // Set updates or adds the value to the address in the map. -func (a *AddressMap) Set(addr Address, value interface{}) { +func (a *AddressMap) Set(addr Address, value any) { addrKey := toMapKey(&addr) entryList := a.m[addrKey] if entry := entryList.find(addr); entry != -1 { @@ -127,8 +127,8 @@ func (a *AddressMap) Keys() []Address { } // Values returns a slice of all current map values. -func (a *AddressMap) Values() []interface{} { - ret := make([]interface{}, 0, a.Len()) +func (a *AddressMap) Values() []any { + ret := make([]any, 0, a.Len()) for _, entryList := range a.m { for _, entry := range entryList { ret = append(ret, entry.value) @@ -136,3 +136,116 @@ func (a *AddressMap) Values() []interface{} { } return ret } + +type endpointNode struct { + addrs map[string]struct{} +} + +// Equal returns whether the unordered set of addrs are the same between the +// endpoint nodes. +func (en *endpointNode) Equal(en2 *endpointNode) bool { + if len(en.addrs) != len(en2.addrs) { + return false + } + for addr := range en.addrs { + if _, ok := en2.addrs[addr]; !ok { + return false + } + } + return true +} + +func toEndpointNode(endpoint Endpoint) endpointNode { + en := make(map[string]struct{}) + for _, addr := range endpoint.Addresses { + en[addr.Addr] = struct{}{} + } + return endpointNode{ + addrs: en, + } +} + +// EndpointMap is a map of endpoints to arbitrary values keyed on only the +// unordered set of address strings within an endpoint. This map is not thread +// safe, thus it is unsafe to access concurrently. Must be created via +// NewEndpointMap; do not construct directly. +type EndpointMap struct { + endpoints map[*endpointNode]any +} + +// NewEndpointMap creates a new EndpointMap. +func NewEndpointMap() *EndpointMap { + return &EndpointMap{ + endpoints: make(map[*endpointNode]any), + } +} + +// Get returns the value for the address in the map, if present. +func (em *EndpointMap) Get(e Endpoint) (value any, ok bool) { + en := toEndpointNode(e) + if endpoint := em.find(en); endpoint != nil { + return em.endpoints[endpoint], true + } + return nil, false +} + +// Set updates or adds the value to the address in the map. +func (em *EndpointMap) Set(e Endpoint, value any) { + en := toEndpointNode(e) + if endpoint := em.find(en); endpoint != nil { + em.endpoints[endpoint] = value + return + } + em.endpoints[&en] = value +} + +// Len returns the number of entries in the map. +func (em *EndpointMap) Len() int { + return len(em.endpoints) +} + +// Keys returns a slice of all current map keys, as endpoints specifying the +// addresses present in the endpoint keys, in which uniqueness is determined by +// the unordered set of addresses. Thus, endpoint information returned is not +// the full endpoint data (drops duplicated addresses and attributes) but can be +// used for EndpointMap accesses. +func (em *EndpointMap) Keys() []Endpoint { + ret := make([]Endpoint, 0, len(em.endpoints)) + for en := range em.endpoints { + var endpoint Endpoint + for addr := range en.addrs { + endpoint.Addresses = append(endpoint.Addresses, Address{Addr: addr}) + } + ret = append(ret, endpoint) + } + return ret +} + +// Values returns a slice of all current map values. +func (em *EndpointMap) Values() []any { + ret := make([]any, 0, len(em.endpoints)) + for _, val := range em.endpoints { + ret = append(ret, val) + } + return ret +} + +// find returns a pointer to the endpoint node in em if the endpoint node is +// already present. If not found, nil is returned. The comparisons are done on +// the unordered set of addresses within an endpoint. +func (em EndpointMap) find(e endpointNode) *endpointNode { + for endpoint := range em.endpoints { + if e.Equal(endpoint) { + return endpoint + } + } + return nil +} + +// Delete removes the specified endpoint from the map. +func (em *EndpointMap) Delete(e Endpoint) { + en := toEndpointNode(e) + if entry := em.find(en); entry != nil { + delete(em.endpoints, entry) + } +} diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go index 353c10b69..202854511 100644 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/vendor/google.golang.org/grpc/resolver/resolver.go @@ -29,6 +29,7 @@ import ( "google.golang.org/grpc/attributes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/internal" "google.golang.org/grpc/serviceconfig" ) @@ -63,39 +64,22 @@ func Get(scheme string) Builder { } // SetDefaultScheme sets the default scheme that will be used. The default -// default scheme is "passthrough". +// scheme is initially set to "passthrough". // // NOTE: this function must only be called during initialization time (i.e. in // an init() function), and is not thread-safe. The scheme set last overrides // previously set values. func SetDefaultScheme(scheme string) { defaultScheme = scheme + internal.UserSetDefaultScheme = true } -// GetDefaultScheme gets the default scheme that will be used. +// GetDefaultScheme gets the default scheme that will be used by grpc.Dial. If +// SetDefaultScheme is never called, the default scheme used by grpc.NewClient is "dns" instead. func GetDefaultScheme() string { return defaultScheme } -// AddressType indicates the address type returned by name resolution. -// -// Deprecated: use Attributes in Address instead. -type AddressType uint8 - -const ( - // Backend indicates the address is for a backend server. - // - // Deprecated: use Attributes in Address instead. - Backend AddressType = iota - // GRPCLB indicates the address is for a grpclb load balancer. - // - // Deprecated: to select the GRPCLB load balancing policy, use a service - // config with a corresponding loadBalancingConfig. To supply balancer - // addresses to the GRPCLB load balancing policy, set State.Attributes - // using balancer/grpclb/state.Set. - GRPCLB -) - // Address represents a server the client connects to. // // # Experimental @@ -111,9 +95,6 @@ type Address struct { // the address, instead of the hostname from the Dial target string. In most cases, // this should not be set. // - // If Type is GRPCLB, ServerName should be the name of the remote load - // balancer, not the name of the backend. - // // WARNING: ServerName must only be populated with trusted values. It // is insecure to populate it with data from untrusted inputs since untrusted // values could be used to bypass the authority checks performed by TLS. @@ -126,27 +107,29 @@ type Address struct { // BalancerAttributes contains arbitrary data about this address intended // for consumption by the LB policy. These attributes do not affect SubConn // creation, connection establishment, handshaking, etc. - BalancerAttributes *attributes.Attributes - - // Type is the type of this address. // - // Deprecated: use Attributes instead. - Type AddressType + // Deprecated: when an Address is inside an Endpoint, this field should not + // be used, and it will eventually be removed entirely. + BalancerAttributes *attributes.Attributes // Metadata is the information associated with Addr, which may be used // to make load balancing decision. // // Deprecated: use Attributes instead. - Metadata interface{} + Metadata any } // Equal returns whether a and o are identical. Metadata is compared directly, // not with any recursive introspection. +// +// This method compares all fields of the address. When used to tell apart +// addresses during subchannel creation or connection establishment, it might be +// more appropriate for the caller to implement custom equality logic. func (a Address) Equal(o Address) bool { return a.Addr == o.Addr && a.ServerName == o.ServerName && a.Attributes.Equal(o.Attributes) && a.BalancerAttributes.Equal(o.BalancerAttributes) && - a.Type == o.Type && a.Metadata == o.Metadata + a.Metadata == o.Metadata } // String returns JSON formatted string representation of the address. @@ -188,13 +171,42 @@ type BuildOptions struct { // field. In most cases though, it is not appropriate, and this field may // be ignored. Dialer func(context.Context, string) (net.Conn, error) + // Authority is the effective authority of the clientconn for which the + // resolver is built. + Authority string +} + +// An Endpoint is one network endpoint, or server, which may have multiple +// addresses with which it can be accessed. +type Endpoint struct { + // Addresses contains a list of addresses used to access this endpoint. + Addresses []Address + + // Attributes contains arbitrary data about this endpoint intended for + // consumption by the LB policy. + Attributes *attributes.Attributes } // State contains the current Resolver state relevant to the ClientConn. type State struct { // Addresses is the latest set of resolved addresses for the target. + // + // If a resolver sets Addresses but does not set Endpoints, one Endpoint + // will be created for each Address before the State is passed to the LB + // policy. The BalancerAttributes of each entry in Addresses will be set + // in Endpoints.Attributes, and be cleared in the Endpoint's Address's + // BalancerAttributes. + // + // Soon, Addresses will be deprecated and replaced fully by Endpoints. Addresses []Address + // Endpoints is the latest set of resolved endpoints for the target. + // + // If a resolver produces a State containing Endpoints but not Addresses, + // it must take care to ensure the LB policies it selects will support + // Endpoints. + Endpoints []Endpoint + // ServiceConfig contains the result from parsing the latest service // config. If it is nil, it indicates no service config is present or the // resolver does not provide service configs. @@ -234,11 +246,6 @@ type ClientConn interface { // // Deprecated: Use UpdateState instead. NewAddress(addresses []Address) - // NewServiceConfig is called by resolver to notify ClientConn a new - // service config. The service config should be provided as a json string. - // - // Deprecated: Use UpdateState instead. - NewServiceConfig(serviceConfig string) // ParseServiceConfig parses the provided service config and returns an // object that provides the parsed config. ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult @@ -254,20 +261,7 @@ type ClientConn interface { // target does not contain a scheme or if the parsed scheme is not registered // (i.e. no corresponding resolver available to resolve the endpoint), we will // apply the default scheme, and will attempt to reparse it. -// -// Examples: -// -// - "dns://some_authority/foo.bar" -// Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"} -// - "foo.bar" -// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"} -// - "unknown_scheme://authority/endpoint" -// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"} type Target struct { - // Deprecated: use URL.Scheme instead. - Scheme string - // Deprecated: use URL.Host instead. - Authority string // URL contains the parsed dial target with an optional default scheme added // to it if the original dial target contained no scheme or contained an // unregistered scheme. Any query params specified in the original dial @@ -293,6 +287,11 @@ func (t Target) Endpoint() string { return strings.TrimPrefix(endpoint, "/") } +// String returns the canonical string representation of Target. +func (t Target) String() string { + return t.URL.Scheme + "://" + t.URL.Host + "/" + t.Endpoint() +} + // Builder creates a resolver that will be used to watch name resolution updates. type Builder interface { // Build creates a new resolver for the given target. @@ -322,9 +321,12 @@ type Resolver interface { Close() } -// UnregisterForTesting removes the resolver builder with the given scheme from the -// resolver map. -// This function is for testing only. -func UnregisterForTesting(scheme string) { - delete(m, scheme) +// AuthorityOverrider is implemented by Builders that wish to override the +// default authority for the ClientConn. +// By default, the authority used is target.Endpoint(). +type AuthorityOverrider interface { + // OverrideAuthority returns the authority to use for a ClientConn with the + // given target. The implementation must generate it without blocking, + // typically in line, and must keep it unchanged. + OverrideAuthority(Target) string } diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go deleted file mode 100644 index b408b3688..000000000 --- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go +++ /dev/null @@ -1,239 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "context" - "strings" - "sync" - - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/internal/channelz" - "google.golang.org/grpc/internal/grpcsync" - "google.golang.org/grpc/internal/pretty" - "google.golang.org/grpc/resolver" - "google.golang.org/grpc/serviceconfig" -) - -// resolverStateUpdater wraps the single method used by ccResolverWrapper to -// report a state update from the actual resolver implementation. -type resolverStateUpdater interface { - updateResolverState(s resolver.State, err error) error -} - -// ccResolverWrapper is a wrapper on top of cc for resolvers. -// It implements resolver.ClientConn interface. -type ccResolverWrapper struct { - // The following fields are initialized when the wrapper is created and are - // read-only afterwards, and therefore can be accessed without a mutex. - cc resolverStateUpdater - channelzID *channelz.Identifier - ignoreServiceConfig bool - opts ccResolverWrapperOpts - serializer *grpcsync.CallbackSerializer // To serialize all incoming calls. - serializerCancel context.CancelFunc // To close the serializer, accessed only from close(). - - // All incoming (resolver --> gRPC) calls are guaranteed to execute in a - // mutually exclusive manner as they are scheduled on the serializer. - // Fields accessed *only* in these serializer callbacks, can therefore be - // accessed without a mutex. - curState resolver.State - - // mu guards access to the below fields. - mu sync.Mutex - closed bool - resolver resolver.Resolver // Accessed only from outgoing calls. -} - -// ccResolverWrapperOpts wraps the arguments to be passed when creating a new -// ccResolverWrapper. -type ccResolverWrapperOpts struct { - target resolver.Target // User specified dial target to resolve. - builder resolver.Builder // Resolver builder to use. - bOpts resolver.BuildOptions // Resolver build options to use. - channelzID *channelz.Identifier // Channelz identifier for the channel. -} - -// newCCResolverWrapper uses the resolver.Builder to build a Resolver and -// returns a ccResolverWrapper object which wraps the newly built resolver. -func newCCResolverWrapper(cc resolverStateUpdater, opts ccResolverWrapperOpts) (*ccResolverWrapper, error) { - ctx, cancel := context.WithCancel(context.Background()) - ccr := &ccResolverWrapper{ - cc: cc, - channelzID: opts.channelzID, - ignoreServiceConfig: opts.bOpts.DisableServiceConfig, - opts: opts, - serializer: grpcsync.NewCallbackSerializer(ctx), - serializerCancel: cancel, - } - - // Cannot hold the lock at build time because the resolver can send an - // update or error inline and these incoming calls grab the lock to schedule - // a callback in the serializer. - r, err := opts.builder.Build(opts.target, ccr, opts.bOpts) - if err != nil { - cancel() - return nil, err - } - - // Any error reported by the resolver at build time that leads to a - // re-resolution request from the balancer is dropped by grpc until we - // return from this function. So, we don't have to handle pending resolveNow - // requests here. - ccr.mu.Lock() - ccr.resolver = r - ccr.mu.Unlock() - - return ccr, nil -} - -func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) { - ccr.mu.Lock() - defer ccr.mu.Unlock() - - // ccr.resolver field is set only after the call to Build() returns. But in - // the process of building, the resolver may send an error update which when - // propagated to the balancer may result in a re-resolution request. - if ccr.closed || ccr.resolver == nil { - return - } - ccr.resolver.ResolveNow(o) -} - -func (ccr *ccResolverWrapper) close() { - ccr.mu.Lock() - if ccr.closed { - ccr.mu.Unlock() - return - } - - channelz.Info(logger, ccr.channelzID, "Closing the name resolver") - - // Close the serializer to ensure that no more calls from the resolver are - // handled, before actually closing the resolver. - ccr.serializerCancel() - ccr.closed = true - r := ccr.resolver - ccr.mu.Unlock() - - // Give enqueued callbacks a chance to finish. - <-ccr.serializer.Done - - // Spawn a goroutine to close the resolver (since it may block trying to - // cleanup all allocated resources) and return early. - go r.Close() -} - -// serializerScheduleLocked is a convenience method to schedule a function to be -// run on the serializer while holding ccr.mu. -func (ccr *ccResolverWrapper) serializerScheduleLocked(f func(context.Context)) { - ccr.mu.Lock() - ccr.serializer.Schedule(f) - ccr.mu.Unlock() -} - -// UpdateState is called by resolver implementations to report new state to gRPC -// which includes addresses and service config. -func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { - errCh := make(chan error, 1) - ok := ccr.serializer.Schedule(func(context.Context) { - ccr.addChannelzTraceEvent(s) - ccr.curState = s - if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState { - errCh <- balancer.ErrBadResolverState - return - } - errCh <- nil - }) - if !ok { - // The only time when Schedule() fail to add the callback to the - // serializer is when the serializer is closed, and this happens only - // when the resolver wrapper is closed. - return nil - } - return <-errCh -} - -// ReportError is called by resolver implementations to report errors -// encountered during name resolution to gRPC. -func (ccr *ccResolverWrapper) ReportError(err error) { - ccr.serializerScheduleLocked(func(_ context.Context) { - channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: reporting error to cc: %v", err) - ccr.cc.updateResolverState(resolver.State{}, err) - }) -} - -// NewAddress is called by the resolver implementation to send addresses to -// gRPC. -func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { - ccr.serializerScheduleLocked(func(_ context.Context) { - ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) - ccr.curState.Addresses = addrs - ccr.cc.updateResolverState(ccr.curState, nil) - }) -} - -// NewServiceConfig is called by the resolver implementation to send service -// configs to gRPC. -func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { - ccr.serializerScheduleLocked(func(_ context.Context) { - channelz.Infof(logger, ccr.channelzID, "ccResolverWrapper: got new service config: %s", sc) - if ccr.ignoreServiceConfig { - channelz.Info(logger, ccr.channelzID, "Service config lookups disabled; ignoring config") - return - } - scpr := parseServiceConfig(sc) - if scpr.Err != nil { - channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err) - return - } - ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) - ccr.curState.ServiceConfig = scpr - ccr.cc.updateResolverState(ccr.curState, nil) - }) -} - -// ParseServiceConfig is called by resolver implementations to parse a JSON -// representation of the service config. -func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult { - return parseServiceConfig(scJSON) -} - -// addChannelzTraceEvent adds a channelz trace event containing the new -// state received from resolver implementations. -func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) { - var updates []string - var oldSC, newSC *ServiceConfig - var oldOK, newOK bool - if ccr.curState.ServiceConfig != nil { - oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig) - } - if s.ServiceConfig != nil { - newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig) - } - if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) { - updates = append(updates, "service config updated") - } - if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 { - updates = append(updates, "resolver returned an empty address list") - } else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 { - updates = append(updates, "resolver returned new addresses") - } - channelz.Infof(logger, ccr.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; ")) -} diff --git a/vendor/google.golang.org/grpc/resolver_wrapper.go b/vendor/google.golang.org/grpc/resolver_wrapper.go new file mode 100644 index 000000000..9dcc9780f --- /dev/null +++ b/vendor/google.golang.org/grpc/resolver_wrapper.go @@ -0,0 +1,198 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "context" + "strings" + "sync" + + "google.golang.org/grpc/internal/channelz" + "google.golang.org/grpc/internal/grpcsync" + "google.golang.org/grpc/internal/pretty" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/serviceconfig" +) + +// ccResolverWrapper is a wrapper on top of cc for resolvers. +// It implements resolver.ClientConn interface. +type ccResolverWrapper struct { + // The following fields are initialized when the wrapper is created and are + // read-only afterwards, and therefore can be accessed without a mutex. + cc *ClientConn + ignoreServiceConfig bool + serializer *grpcsync.CallbackSerializer + serializerCancel context.CancelFunc + + resolver resolver.Resolver // only accessed within the serializer + + // The following fields are protected by mu. Caller must take cc.mu before + // taking mu. + mu sync.Mutex + curState resolver.State + closed bool +} + +// newCCResolverWrapper initializes the ccResolverWrapper. It can only be used +// after calling start, which builds the resolver. +func newCCResolverWrapper(cc *ClientConn) *ccResolverWrapper { + ctx, cancel := context.WithCancel(cc.ctx) + return &ccResolverWrapper{ + cc: cc, + ignoreServiceConfig: cc.dopts.disableServiceConfig, + serializer: grpcsync.NewCallbackSerializer(ctx), + serializerCancel: cancel, + } +} + +// start builds the name resolver using the resolver.Builder in cc and returns +// any error encountered. It must always be the first operation performed on +// any newly created ccResolverWrapper, except that close may be called instead. +func (ccr *ccResolverWrapper) start() error { + errCh := make(chan error) + ccr.serializer.Schedule(func(ctx context.Context) { + if ctx.Err() != nil { + return + } + opts := resolver.BuildOptions{ + DisableServiceConfig: ccr.cc.dopts.disableServiceConfig, + DialCreds: ccr.cc.dopts.copts.TransportCredentials, + CredsBundle: ccr.cc.dopts.copts.CredsBundle, + Dialer: ccr.cc.dopts.copts.Dialer, + Authority: ccr.cc.authority, + } + var err error + ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts) + errCh <- err + }) + return <-errCh +} + +func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) { + ccr.serializer.Schedule(func(ctx context.Context) { + if ctx.Err() != nil || ccr.resolver == nil { + return + } + ccr.resolver.ResolveNow(o) + }) +} + +// close initiates async shutdown of the wrapper. To determine the wrapper has +// finished shutting down, the channel should block on ccr.serializer.Done() +// without cc.mu held. +func (ccr *ccResolverWrapper) close() { + channelz.Info(logger, ccr.cc.channelz, "Closing the name resolver") + ccr.mu.Lock() + ccr.closed = true + ccr.mu.Unlock() + + ccr.serializer.Schedule(func(context.Context) { + if ccr.resolver == nil { + return + } + ccr.resolver.Close() + ccr.resolver = nil + }) + ccr.serializerCancel() +} + +// UpdateState is called by resolver implementations to report new state to gRPC +// which includes addresses and service config. +func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { + ccr.cc.mu.Lock() + ccr.mu.Lock() + if ccr.closed { + ccr.mu.Unlock() + ccr.cc.mu.Unlock() + return nil + } + if s.Endpoints == nil { + s.Endpoints = make([]resolver.Endpoint, 0, len(s.Addresses)) + for _, a := range s.Addresses { + ep := resolver.Endpoint{Addresses: []resolver.Address{a}, Attributes: a.BalancerAttributes} + ep.Addresses[0].BalancerAttributes = nil + s.Endpoints = append(s.Endpoints, ep) + } + } + ccr.addChannelzTraceEvent(s) + ccr.curState = s + ccr.mu.Unlock() + return ccr.cc.updateResolverStateAndUnlock(s, nil) +} + +// ReportError is called by resolver implementations to report errors +// encountered during name resolution to gRPC. +func (ccr *ccResolverWrapper) ReportError(err error) { + ccr.cc.mu.Lock() + ccr.mu.Lock() + if ccr.closed { + ccr.mu.Unlock() + ccr.cc.mu.Unlock() + return + } + ccr.mu.Unlock() + channelz.Warningf(logger, ccr.cc.channelz, "ccResolverWrapper: reporting error to cc: %v", err) + ccr.cc.updateResolverStateAndUnlock(resolver.State{}, err) +} + +// NewAddress is called by the resolver implementation to send addresses to +// gRPC. +func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { + ccr.cc.mu.Lock() + ccr.mu.Lock() + if ccr.closed { + ccr.mu.Unlock() + ccr.cc.mu.Unlock() + return + } + s := resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig} + ccr.addChannelzTraceEvent(s) + ccr.curState = s + ccr.mu.Unlock() + ccr.cc.updateResolverStateAndUnlock(s, nil) +} + +// ParseServiceConfig is called by resolver implementations to parse a JSON +// representation of the service config. +func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult { + return parseServiceConfig(scJSON) +} + +// addChannelzTraceEvent adds a channelz trace event containing the new +// state received from resolver implementations. +func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) { + var updates []string + var oldSC, newSC *ServiceConfig + var oldOK, newOK bool + if ccr.curState.ServiceConfig != nil { + oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig) + } + if s.ServiceConfig != nil { + newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig) + } + if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) { + updates = append(updates, "service config updated") + } + if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 { + updates = append(updates, "resolver returned an empty address list") + } else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 { + updates = append(updates, "resolver returned new addresses") + } + channelz.Infof(logger, ccr.cc.channelz, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; ")) +} diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go index 2030736a3..998e251dd 100644 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ b/vendor/google.golang.org/grpc/rpc_util.go @@ -75,7 +75,7 @@ func NewGZIPCompressorWithLevel(level int) (Compressor, error) { } return &gzipCompressor{ pool: sync.Pool{ - New: func() interface{} { + New: func() any { w, err := gzip.NewWriterLevel(io.Discard, level) if err != nil { panic(err) @@ -189,6 +189,20 @@ type EmptyCallOption struct{} func (EmptyCallOption) before(*callInfo) error { return nil } func (EmptyCallOption) after(*callInfo, *csAttempt) {} +// StaticMethod returns a CallOption which specifies that a call is being made +// to a method that is static, which means the method is known at compile time +// and doesn't change at runtime. This can be used as a signal to stats plugins +// that this method is safe to include as a key to a measurement. +func StaticMethod() CallOption { + return StaticMethodCallOption{} +} + +// StaticMethodCallOption is a CallOption that specifies that a call comes +// from a static method. +type StaticMethodCallOption struct { + EmptyCallOption +} + // Header returns a CallOptions that retrieves the header metadata // for a unary RPC. func Header(md *metadata.MD) CallOption { @@ -577,6 +591,9 @@ type parser struct { // The header of a gRPC message. Find more detail at // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md header [5]byte + + // recvBufferPool is the pool of shared receive buffers. + recvBufferPool SharedBufferPool } // recvMsg reads a complete gRPC message from the stream. @@ -610,9 +627,7 @@ func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byt if int(length) > maxReceiveMessageSize { return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize) } - // TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead - // of making it for each message: - msg = make([]byte, int(length)) + msg = p.recvBufferPool.Get(int(length)) if _, err := p.r.Read(msg); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF @@ -625,7 +640,7 @@ func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byt // encode serializes msg and returns a buffer containing the message, or an // error if it is too large to be transmitted by grpc. If msg is nil, it // generates an empty message. -func encode(c baseCodec, msg interface{}) ([]byte, error) { +func encode(c baseCodec, msg any) ([]byte, error) { if msg == nil { // NOTE: typed nils will not be caught by this check return nil, nil } @@ -639,14 +654,18 @@ func encode(c baseCodec, msg interface{}) ([]byte, error) { return b, nil } -// compress returns the input bytes compressed by compressor or cp. If both -// compressors are nil, returns nil. +// compress returns the input bytes compressed by compressor or cp. +// If both compressors are nil, or if the message has zero length, returns nil, +// indicating no compression was done. // // TODO(dfawley): eliminate cp parameter by wrapping Compressor in an encoding.Compressor. func compress(in []byte, cp Compressor, compressor encoding.Compressor) ([]byte, error) { if compressor == nil && cp == nil { return nil, nil } + if len(in) == 0 { + return nil, nil + } wrapErr := func(err error) error { return status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error()) } @@ -692,7 +711,7 @@ func msgHeader(data, compData []byte) (hdr []byte, payload []byte) { return hdr, data } -func outPayload(client bool, msg interface{}, data, payload []byte, t time.Time) *stats.OutPayload { +func outPayload(client bool, msg any, data, payload []byte, t time.Time) *stats.OutPayload { return &stats.OutPayload{ Client: client, Payload: msg, @@ -725,17 +744,19 @@ type payloadInfo struct { uncompressedBytes []byte } -func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) ([]byte, error) { - pf, d, err := p.recvMsg(maxReceiveMessageSize) +// recvAndDecompress reads a message from the stream, decompressing it if necessary. +// +// Cancelling the returned cancel function releases the buffer back to the pool. So the caller should cancel as soon as +// the buffer is no longer needed. +func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor, +) (uncompressedBuf []byte, cancel func(), err error) { + pf, compressedBuf, err := p.recvMsg(maxReceiveMessageSize) if err != nil { - return nil, err - } - if payInfo != nil { - payInfo.compressedLength = len(d) + return nil, nil, err } if st := checkRecvPayload(pf, s.RecvCompress(), compressor != nil || dc != nil); st != nil { - return nil, st.Err() + return nil, nil, st.Err() } var size int @@ -743,21 +764,35 @@ func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxRecei // To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor, // use this decompressor as the default. if dc != nil { - d, err = dc.Do(bytes.NewReader(d)) - size = len(d) + uncompressedBuf, err = dc.Do(bytes.NewReader(compressedBuf)) + size = len(uncompressedBuf) } else { - d, size, err = decompress(compressor, d, maxReceiveMessageSize) + uncompressedBuf, size, err = decompress(compressor, compressedBuf, maxReceiveMessageSize) } if err != nil { - return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err) + return nil, nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err) } if size > maxReceiveMessageSize { // TODO: Revisit the error code. Currently keep it consistent with java // implementation. - return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize) + return nil, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize) + } + } else { + uncompressedBuf = compressedBuf + } + + if payInfo != nil { + payInfo.compressedLength = len(compressedBuf) + payInfo.uncompressedBytes = uncompressedBuf + + cancel = func() {} + } else { + cancel = func() { + p.recvBufferPool.Put(&compressedBuf) } } - return d, nil + + return uncompressedBuf, cancel, nil } // Using compressor, decompress d, returning data and size. @@ -777,6 +812,9 @@ func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize // size is used as an estimate to size the buffer, but we // will read more data if available. // +MinRead so ReadFrom will not reallocate if size is correct. + // + // TODO: If we ensure that the buffer size is the same as the DecompressedSize, + // we can also utilize the recv buffer pool here. buf := bytes.NewBuffer(make([]byte, 0, size+bytes.MinRead)) bytesRead, err := buf.ReadFrom(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1)) return buf.Bytes(), int(bytesRead), err @@ -791,17 +829,16 @@ func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize // For the two compressor parameters, both should not be set, but if they are, // dc takes precedence over compressor. // TODO(dfawley): wrap the old compressor/decompressor using the new API? -func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m interface{}, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) error { - d, err := recvAndDecompress(p, s, dc, maxReceiveMessageSize, payInfo, compressor) +func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m any, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) error { + buf, cancel, err := recvAndDecompress(p, s, dc, maxReceiveMessageSize, payInfo, compressor) if err != nil { return err } - if err := c.Unmarshal(d, m); err != nil { + defer cancel() + + if err := c.Unmarshal(buf, m); err != nil { return status.Errorf(codes.Internal, "grpc: failed to unmarshal the received message: %v", err) } - if payInfo != nil { - payInfo.uncompressedBytes = d - } return nil } @@ -860,19 +897,22 @@ func ErrorDesc(err error) string { // Errorf returns nil if c is OK. // // Deprecated: use status.Errorf instead. -func Errorf(c codes.Code, format string, a ...interface{}) error { +func Errorf(c codes.Code, format string, a ...any) error { return status.Errorf(c, format, a...) } +var errContextCanceled = status.Error(codes.Canceled, context.Canceled.Error()) +var errContextDeadline = status.Error(codes.DeadlineExceeded, context.DeadlineExceeded.Error()) + // toRPCErr converts an error into an error from the status package. func toRPCErr(err error) error { switch err { case nil, io.EOF: return err case context.DeadlineExceeded: - return status.Error(codes.DeadlineExceeded, err.Error()) + return errContextDeadline case context.Canceled: - return status.Error(codes.Canceled, err.Error()) + return errContextCanceled case io.ErrUnexpectedEOF: return status.Error(codes.Internal, err.Error()) } @@ -922,19 +962,6 @@ func setCallInfoCodec(c *callInfo) error { return nil } -// channelzData is used to store channelz related data for ClientConn, addrConn and Server. -// These fields cannot be embedded in the original structs (e.g. ClientConn), since to do atomic -// operation on int64 variable on 32-bit machine, user is responsible to enforce memory alignment. -// Here, by grouping those int64 fields inside a struct, we are enforcing the alignment. -type channelzData struct { - callsStarted int64 - callsFailed int64 - callsSucceeded int64 - // lastCallStartedTime stores the timestamp that last call starts. It is of int64 type instead of - // time.Time since it's more costly to atomically update time.Time variable than int64 variable. - lastCallStartedTime int64 -} - // The SupportPackageIsVersion variables are referenced from generated protocol // buffer files to ensure compatibility with the gRPC version used. The latest // support package version is 7. @@ -948,6 +975,7 @@ const ( SupportPackageIsVersion5 = true SupportPackageIsVersion6 = true SupportPackageIsVersion7 = true + SupportPackageIsVersion8 = true ) const grpcUA = "grpc-go/" + Version diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 8869cc906..fd4558daa 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -33,8 +33,6 @@ import ( "sync/atomic" "time" - "golang.org/x/net/trace" - "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding" @@ -70,9 +68,10 @@ func init() { internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials { return srv.opts.creds } - internal.DrainServerTransports = func(srv *Server, addr string) { - srv.drainServerTransports(addr) + internal.IsRegisteredMethod = func(srv *Server, method string) bool { + return srv.isRegisteredMethod(method) } + internal.ServerFromContext = serverFromContext internal.AddGlobalServerOptions = func(opt ...ServerOption) { globalServerOptions = append(globalServerOptions, opt...) } @@ -81,12 +80,13 @@ func init() { } internal.BinaryLogger = binaryLogger internal.JoinServerOptions = newJoinServerOption + internal.RecvBufferPool = recvBufferPool } var statusOK = status.New(codes.OK, "") var logger = grpclog.Component("core") -type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error) +type methodHandler func(srv any, ctx context.Context, dec func(any) error, interceptor UnaryServerInterceptor) (any, error) // MethodDesc represents an RPC service's method specification. type MethodDesc struct { @@ -99,20 +99,20 @@ type ServiceDesc struct { ServiceName string // The pointer to the service interface. Used to check whether the user // provided implementation satisfies the interface requirements. - HandlerType interface{} + HandlerType any Methods []MethodDesc Streams []StreamDesc - Metadata interface{} + Metadata any } // serviceInfo wraps information about a service. It is very similar to // ServiceDesc and is constructed from it for internal purposes. type serviceInfo struct { // Contains the implementation for the methods in this service. - serviceImpl interface{} + serviceImpl any methods map[string]*MethodDesc streams map[string]*StreamDesc - mdata interface{} + mdata any } // Server is a gRPC server to serve RPC requests. @@ -129,17 +129,18 @@ type Server struct { drain bool cv *sync.Cond // signaled when connections close for GracefulStop services map[string]*serviceInfo // service name -> service info - events trace.EventLog + events traceEventLog quit *grpcsync.Event done *grpcsync.Event channelzRemoveOnce sync.Once - serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop + serveWG sync.WaitGroup // counts active Serve goroutines for Stop/GracefulStop + handlersWG sync.WaitGroup // counts active method handler goroutines - channelzID *channelz.Identifier - czData *channelzData + channelz *channelz.Server - serverWorkerChannel chan func() + serverWorkerChannel chan func() + serverWorkerChannelClose func() } type serverOptions struct { @@ -164,10 +165,13 @@ type serverOptions struct { initialConnWindowSize int32 writeBufferSize int readBufferSize int + sharedWriteBuffer bool connectionTimeout time.Duration maxHeaderListSize *uint32 headerTableSize *uint32 numServerWorkers uint32 + recvBufferPool SharedBufferPool + waitForHandlers bool } var defaultServerOptions = serverOptions{ @@ -177,6 +181,7 @@ var defaultServerOptions = serverOptions{ connectionTimeout: 120 * time.Second, writeBufferSize: defaultWriteBufSize, readBufferSize: defaultReadBufSize, + recvBufferPool: nopBufferPool{}, } var globalServerOptions []ServerOption @@ -228,12 +233,24 @@ func newJoinServerOption(opts ...ServerOption) ServerOption { return &joinServerOption{opts: opts} } +// SharedWriteBuffer allows reusing per-connection transport write buffer. +// If this option is set to true every connection will release the buffer after +// flushing the data on the wire. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func SharedWriteBuffer(val bool) ServerOption { + return newFuncServerOption(func(o *serverOptions) { + o.sharedWriteBuffer = val + }) +} + // WriteBufferSize determines how much data can be batched before doing a write -// on the wire. The corresponding memory allocation for this buffer will be -// twice the size to keep syscalls low. The default value for this buffer is -// 32KB. Zero or negative values will disable the write buffer such that each -// write will be on underlying connection. -// Note: A Send call may not directly translate to a write. +// on the wire. The default value for this buffer is 32KB. Zero or negative +// values will disable the write buffer such that each write will be on underlying +// connection. Note: A Send call may not directly translate to a write. func WriteBufferSize(s int) ServerOption { return newFuncServerOption(func(o *serverOptions) { o.writeBufferSize = s @@ -268,9 +285,9 @@ func InitialConnWindowSize(s int32) ServerOption { // KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server. func KeepaliveParams(kp keepalive.ServerParameters) ServerOption { - if kp.Time > 0 && kp.Time < time.Second { + if kp.Time > 0 && kp.Time < internal.KeepaliveMinServerPingTime { logger.Warning("Adjusting keepalive ping interval to minimum period of 1s") - kp.Time = time.Second + kp.Time = internal.KeepaliveMinServerPingTime } return newFuncServerOption(func(o *serverOptions) { @@ -550,6 +567,44 @@ func NumStreamWorkers(numServerWorkers uint32) ServerOption { }) } +// WaitForHandlers cause Stop to wait until all outstanding method handlers have +// exited before returning. If false, Stop will return as soon as all +// connections have closed, but method handlers may still be running. By +// default, Stop does not wait for method handlers to return. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func WaitForHandlers(w bool) ServerOption { + return newFuncServerOption(func(o *serverOptions) { + o.waitForHandlers = w + }) +} + +// RecvBufferPool returns a ServerOption that configures the server +// to use the provided shared buffer pool for parsing incoming messages. Depending +// on the application's workload, this could result in reduced memory allocation. +// +// If you are unsure about how to implement a memory pool but want to utilize one, +// begin with grpc.NewSharedBufferPool. +// +// Note: The shared buffer pool feature will not be active if any of the following +// options are used: StatsHandler, EnableTracing, or binary logging. In such +// cases, the shared buffer pool will be ignored. +// +// Deprecated: use experimental.WithRecvBufferPool instead. Will be deleted in +// v1.60.0 or later. +func RecvBufferPool(bufferPool SharedBufferPool) ServerOption { + return recvBufferPool(bufferPool) +} + +func recvBufferPool(bufferPool SharedBufferPool) ServerOption { + return newFuncServerOption(func(o *serverOptions) { + o.recvBufferPool = bufferPool + }) +} + // serverWorkerResetThreshold defines how often the stack must be reset. Every // N requests, by spawning a new goroutine in its place, a worker can reset its // stack so that large stacks don't live in memory forever. 2^16 should allow @@ -578,15 +633,14 @@ func (s *Server) serverWorker() { // connections to reduce the time spent overall on runtime.morestack. func (s *Server) initServerWorkers() { s.serverWorkerChannel = make(chan func()) + s.serverWorkerChannelClose = grpcsync.OnceFunc(func() { + close(s.serverWorkerChannel) + }) for i := uint32(0); i < s.opts.numServerWorkers; i++ { go s.serverWorker() } } -func (s *Server) stopServerWorkers() { - close(s.serverWorkerChannel) -} - // NewServer creates a gRPC server which has no service registered and has not // started to accept requests yet. func NewServer(opt ...ServerOption) *Server { @@ -604,28 +658,27 @@ func NewServer(opt ...ServerOption) *Server { services: make(map[string]*serviceInfo), quit: grpcsync.NewEvent(), done: grpcsync.NewEvent(), - czData: new(channelzData), + channelz: channelz.RegisterServer(""), } chainUnaryServerInterceptors(s) chainStreamServerInterceptors(s) s.cv = sync.NewCond(&s.mu) if EnableTracing { _, file, line, _ := runtime.Caller(1) - s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line)) + s.events = newTraceEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line)) } if s.opts.numServerWorkers > 0 { s.initServerWorkers() } - s.channelzID = channelz.RegisterServer(&channelzServer{s}, "") - channelz.Info(logger, s.channelzID, "Server created") + channelz.Info(logger, s.channelz, "Server created") return s } // printf records an event in s's event log, unless s has been stopped. // REQUIRES s.mu is held. -func (s *Server) printf(format string, a ...interface{}) { +func (s *Server) printf(format string, a ...any) { if s.events != nil { s.events.Printf(format, a...) } @@ -633,7 +686,7 @@ func (s *Server) printf(format string, a ...interface{}) { // errorf records an error in s's event log, unless s has been stopped. // REQUIRES s.mu is held. -func (s *Server) errorf(format string, a ...interface{}) { +func (s *Server) errorf(format string, a ...any) { if s.events != nil { s.events.Errorf(format, a...) } @@ -648,14 +701,14 @@ type ServiceRegistrar interface { // once the server has started serving. // desc describes the service and its methods and handlers. impl is the // service implementation which is passed to the method handlers. - RegisterService(desc *ServiceDesc, impl interface{}) + RegisterService(desc *ServiceDesc, impl any) } // RegisterService registers a service and its implementation to the gRPC // server. It is called from the IDL generated code. This must be called before // invoking Serve. If ss is non-nil (for legacy code), its type is checked to // ensure it implements sd.HandlerType. -func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) { +func (s *Server) RegisterService(sd *ServiceDesc, ss any) { if ss != nil { ht := reflect.TypeOf(sd.HandlerType).Elem() st := reflect.TypeOf(ss) @@ -666,7 +719,7 @@ func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) { s.register(sd, ss) } -func (s *Server) register(sd *ServiceDesc, ss interface{}) { +func (s *Server) register(sd *ServiceDesc, ss any) { s.mu.Lock() defer s.mu.Unlock() s.printf("RegisterService(%q)", sd.ServiceName) @@ -707,7 +760,7 @@ type MethodInfo struct { type ServiceInfo struct { Methods []MethodInfo // Metadata is the metadata specified in ServiceDesc when registering service. - Metadata interface{} + Metadata any } // GetServiceInfo returns a map from service names to ServiceInfo. @@ -745,20 +798,13 @@ var ErrServerStopped = errors.New("grpc: the server has been stopped") type listenSocket struct { net.Listener - channelzID *channelz.Identifier -} - -func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric { - return &channelz.SocketInternalMetric{ - SocketOptions: channelz.GetSocketOption(l.Listener), - LocalAddr: l.Listener.Addr(), - } + channelz *channelz.Socket } func (l *listenSocket) Close() error { err := l.Listener.Close() - channelz.RemoveEntry(l.channelzID) - channelz.Info(logger, l.channelzID, "ListenSocket deleted") + channelz.RemoveEntry(l.channelz.ID) + channelz.Info(logger, l.channelz, "ListenSocket deleted") return err } @@ -768,6 +814,18 @@ func (l *listenSocket) Close() error { // Serve returns when lis.Accept fails with fatal errors. lis will be closed when // this method returns. // Serve will return a non-nil error unless Stop or GracefulStop is called. +// +// Note: All supported releases of Go (as of December 2023) override the OS +// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive +// with OS defaults for keepalive time and interval, callers need to do the +// following two things: +// - pass a net.Listener created by calling the Listen method on a +// net.ListenConfig with the `KeepAlive` field set to a negative value. This +// will result in the Go standard library not overriding OS defaults for TCP +// keepalive interval and time. But this will also result in the Go standard +// library not enabling TCP keepalives by default. +// - override the Accept method on the passed in net.Listener and set the +// SO_KEEPALIVE socket option to enable TCP keepalives, with OS defaults. func (s *Server) Serve(lis net.Listener) error { s.mu.Lock() s.printf("serving") @@ -788,7 +846,16 @@ func (s *Server) Serve(lis net.Listener) error { } }() - ls := &listenSocket{Listener: lis} + ls := &listenSocket{ + Listener: lis, + channelz: channelz.RegisterSocket(&channelz.Socket{ + SocketType: channelz.SocketTypeListen, + Parent: s.channelz, + RefName: lis.Addr().String(), + LocalAddr: lis.Addr(), + SocketOptions: channelz.GetSocketOption(lis)}, + ), + } s.lis[ls] = true defer func() { @@ -800,14 +867,8 @@ func (s *Server) Serve(lis net.Listener) error { s.mu.Unlock() }() - var err error - ls.channelzID, err = channelz.RegisterListenSocket(ls, s.channelzID, lis.Addr().String()) - if err != nil { - s.mu.Unlock() - return err - } s.mu.Unlock() - channelz.Info(logger, ls.channelzID, "ListenSocket created") + channelz.Info(logger, ls.channelz, "ListenSocket created") var tempDelay time.Duration // how long to sleep on accept failure for { @@ -875,24 +936,21 @@ func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) { return } + if cc, ok := rawConn.(interface { + PassServerTransport(transport.ServerTransport) + }); ok { + cc.PassServerTransport(st) + } + if !s.addConn(lisAddr, st) { return } go func() { - s.serveStreams(st) + s.serveStreams(context.Background(), st, rawConn) s.removeConn(lisAddr, st) }() } -func (s *Server) drainServerTransports(addr string) { - s.mu.Lock() - conns := s.conns[addr] - for st := range conns { - st.Drain("") - } - s.mu.Unlock() -} - // newHTTP2Transport sets up a http/2 transport (using the // gRPC http2 server transport in transport/http2_server.go). func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport { @@ -908,7 +966,8 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport { InitialConnWindowSize: s.opts.initialConnWindowSize, WriteBufferSize: s.opts.writeBufferSize, ReadBufferSize: s.opts.readBufferSize, - ChannelzParentID: s.channelzID, + SharedWriteBuffer: s.opts.sharedWriteBuffer, + ChannelzParent: s.channelz, MaxHeaderListSize: s.opts.maxHeaderListSize, HeaderTableSize: s.opts.headerTableSize, } @@ -922,7 +981,7 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport { if err != credentials.ErrConnDispatched { // Don't log on ErrConnDispatched and io.EOF to prevent log spam. if err != io.EOF { - channelz.Info(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err) + channelz.Info(logger, s.channelz, "grpc: Server.Serve failed to create ServerTransport: ", err) } c.Close() } @@ -932,19 +991,32 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport { return st } -func (s *Server) serveStreams(st transport.ServerTransport) { - defer st.Close(errors.New("finished serving streams for the server transport")) - var wg sync.WaitGroup +func (s *Server) serveStreams(ctx context.Context, st transport.ServerTransport, rawConn net.Conn) { + ctx = transport.SetConnection(ctx, rawConn) + ctx = peer.NewContext(ctx, st.Peer()) + for _, sh := range s.opts.statsHandlers { + ctx = sh.TagConn(ctx, &stats.ConnTagInfo{ + RemoteAddr: st.Peer().Addr, + LocalAddr: st.Peer().LocalAddr, + }) + sh.HandleConn(ctx, &stats.ConnBegin{}) + } - streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams) - st.HandleStreams(func(stream *transport.Stream) { - wg.Add(1) + defer func() { + st.Close(errors.New("finished serving streams for the server transport")) + for _, sh := range s.opts.statsHandlers { + sh.HandleConn(ctx, &stats.ConnEnd{}) + } + }() + streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams) + st.HandleStreams(ctx, func(stream *transport.Stream) { + s.handlersWG.Add(1) streamQuota.acquire() f := func() { defer streamQuota.release() - defer wg.Done() - s.handleStream(st, stream, s.traceInfo(st, stream)) + defer s.handlersWG.Done() + s.handleStream(st, stream) } if s.opts.numServerWorkers > 0 { @@ -956,14 +1028,7 @@ func (s *Server) serveStreams(st transport.ServerTransport) { } } go f() - }, func(ctx context.Context, method string) context.Context { - if !EnableTracing { - return ctx - } - tr := trace.New("grpc.Recv."+methodFamily(method), method) - return trace.NewContext(ctx, tr) }) - wg.Wait() } var _ http.Handler = (*Server)(nil) @@ -1007,31 +1072,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } defer s.removeConn(listenerAddressForServeHTTP, st) - s.serveStreams(st) -} - -// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled. -// If tracing is not enabled, it returns nil. -func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) { - if !EnableTracing { - return nil - } - tr, ok := trace.FromContext(stream.Context()) - if !ok { - return nil - } - - trInfo = &traceInfo{ - tr: tr, - firstLine: firstLine{ - client: false, - remoteAddr: st.RemoteAddr(), - }, - } - if dl, ok := stream.Context().Deadline(); ok { - trInfo.firstLine.deadline = time.Until(dl) - } - return trInfo + s.serveStreams(r.Context(), st, nil) } func (s *Server) addConn(addr string, st transport.ServerTransport) bool { @@ -1072,37 +1113,28 @@ func (s *Server) removeConn(addr string, st transport.ServerTransport) { } } -func (s *Server) channelzMetric() *channelz.ServerInternalMetric { - return &channelz.ServerInternalMetric{ - CallsStarted: atomic.LoadInt64(&s.czData.callsStarted), - CallsSucceeded: atomic.LoadInt64(&s.czData.callsSucceeded), - CallsFailed: atomic.LoadInt64(&s.czData.callsFailed), - LastCallStartedTimestamp: time.Unix(0, atomic.LoadInt64(&s.czData.lastCallStartedTime)), - } -} - func (s *Server) incrCallsStarted() { - atomic.AddInt64(&s.czData.callsStarted, 1) - atomic.StoreInt64(&s.czData.lastCallStartedTime, time.Now().UnixNano()) + s.channelz.ServerMetrics.CallsStarted.Add(1) + s.channelz.ServerMetrics.LastCallStartedTimestamp.Store(time.Now().UnixNano()) } func (s *Server) incrCallsSucceeded() { - atomic.AddInt64(&s.czData.callsSucceeded, 1) + s.channelz.ServerMetrics.CallsSucceeded.Add(1) } func (s *Server) incrCallsFailed() { - atomic.AddInt64(&s.czData.callsFailed, 1) + s.channelz.ServerMetrics.CallsFailed.Add(1) } -func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error { +func (s *Server) sendResponse(ctx context.Context, t transport.ServerTransport, stream *transport.Stream, msg any, cp Compressor, opts *transport.Options, comp encoding.Compressor) error { data, err := encode(s.getCodec(stream.ContentSubtype()), msg) if err != nil { - channelz.Error(logger, s.channelzID, "grpc: server failed to encode response: ", err) + channelz.Error(logger, s.channelz, "grpc: server failed to encode response: ", err) return err } compData, err := compress(data, cp, comp) if err != nil { - channelz.Error(logger, s.channelzID, "grpc: server failed to compress response: ", err) + channelz.Error(logger, s.channelz, "grpc: server failed to compress response: ", err) return err } hdr, payload := msgHeader(data, compData) @@ -1113,7 +1145,7 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str err = t.Write(stream, hdr, payload, opts) if err == nil { for _, sh := range s.opts.statsHandlers { - sh.HandleRPC(stream.Context(), outPayload(false, msg, data, payload, time.Now())) + sh.HandleRPC(ctx, outPayload(false, msg, data, payload, time.Now())) } } return err @@ -1141,7 +1173,7 @@ func chainUnaryServerInterceptors(s *Server) { } func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor { - return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) { + return func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (any, error) { return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler)) } } @@ -1150,12 +1182,12 @@ func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info if curr == len(interceptors)-1 { return finalHandler } - return func(ctx context.Context, req interface{}) (interface{}, error) { + return func(ctx context.Context, req any) (any, error) { return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler)) } } -func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, md *MethodDesc, trInfo *traceInfo) (err error) { +func (s *Server) processUnaryRPC(ctx context.Context, t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, md *MethodDesc, trInfo *traceInfo) (err error) { shs := s.opts.statsHandlers if len(shs) != 0 || trInfo != nil || channelz.IsOn() { if channelz.IsOn() { @@ -1169,7 +1201,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. IsClientStream: false, IsServerStream: false, } - sh.HandleRPC(stream.Context(), statsBegin) + sh.HandleRPC(ctx, statsBegin) } if trInfo != nil { trInfo.tr.LazyLog(&trInfo.firstLine, false) @@ -1187,7 +1219,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. defer func() { if trInfo != nil { if err != nil && err != io.EOF { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) + trInfo.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) trInfo.tr.SetError() } trInfo.tr.Finish() @@ -1201,7 +1233,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. if err != nil && err != io.EOF { end.Error = toRPCErr(err) } - sh.HandleRPC(stream.Context(), end) + sh.HandleRPC(ctx, end) } if channelz.IsOn() { @@ -1223,7 +1255,6 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. } } if len(binlogs) != 0 { - ctx := stream.Context() md, _ := metadata.FromIncomingContext(ctx) logEntry := &binarylog.ClientHeader{ Header: md, @@ -1294,22 +1325,25 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. if len(shs) != 0 || len(binlogs) != 0 { payInfo = &payloadInfo{} } - d, err := recvAndDecompress(&parser{r: stream}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp) + + d, cancel, err := recvAndDecompress(&parser{r: stream, recvBufferPool: s.opts.recvBufferPool}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp) if err != nil { if e := t.WriteStatus(stream, status.Convert(err)); e != nil { - channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e) + channelz.Warningf(logger, s.channelz, "grpc: Server.processUnaryRPC failed to write status: %v", e) } return err } if channelz.IsOn() { t.IncrMsgRecv() } - df := func(v interface{}) error { + df := func(v any) error { + defer cancel() + if err := s.getCodec(stream.ContentSubtype()).Unmarshal(d, v); err != nil { return status.Errorf(codes.Internal, "grpc: error unmarshalling request: %v", err) } for _, sh := range shs { - sh.HandleRPC(stream.Context(), &stats.InPayload{ + sh.HandleRPC(ctx, &stats.InPayload{ RecvTime: time.Now(), Payload: v, Length: len(d), @@ -1323,7 +1357,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Message: d, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), cm) + binlog.Log(ctx, cm) } } if trInfo != nil { @@ -1331,7 +1365,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. } return nil } - ctx := NewContextWithServerTransportStream(stream.Context(), stream) + ctx = NewContextWithServerTransportStream(ctx, stream) reply, appErr := md.Handler(info.serviceImpl, ctx, df, s.opts.unaryInt) if appErr != nil { appStatus, ok := status.FromError(appErr) @@ -1346,7 +1380,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. trInfo.tr.SetError() } if e := t.WriteStatus(stream, appStatus); e != nil { - channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e) + channelz.Warningf(logger, s.channelz, "grpc: Server.processUnaryRPC failed to write status: %v", e) } if len(binlogs) != 0 { if h, _ := stream.Header(); h.Len() > 0 { @@ -1356,7 +1390,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Header: h, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), sh) + binlog.Log(ctx, sh) } } st := &binarylog.ServerTrailer{ @@ -1364,7 +1398,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Err: appErr, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), st) + binlog.Log(ctx, st) } } return appErr @@ -1379,14 +1413,14 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. if stream.SendCompress() != sendCompressorName { comp = encoding.GetCompressor(stream.SendCompress()) } - if err := s.sendResponse(t, stream, reply, cp, opts, comp); err != nil { + if err := s.sendResponse(ctx, t, stream, reply, cp, opts, comp); err != nil { if err == io.EOF { // The entire stream is done (for unary RPC only). return err } if sts, ok := status.FromError(err); ok { if e := t.WriteStatus(stream, sts); e != nil { - channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e) + channelz.Warningf(logger, s.channelz, "grpc: Server.processUnaryRPC failed to write status: %v", e) } } else { switch st := err.(type) { @@ -1406,8 +1440,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Err: appErr, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), sh) - binlog.Log(stream.Context(), st) + binlog.Log(ctx, sh) + binlog.Log(ctx, st) } } return err @@ -1421,8 +1455,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Message: reply, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), sh) - binlog.Log(stream.Context(), sm) + binlog.Log(ctx, sh) + binlog.Log(ctx, sm) } } if channelz.IsOn() { @@ -1440,7 +1474,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. Err: appErr, } for _, binlog := range binlogs { - binlog.Log(stream.Context(), st) + binlog.Log(ctx, st) } } return t.WriteStatus(stream, statusOK) @@ -1468,7 +1502,7 @@ func chainStreamServerInterceptors(s *Server) { } func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor { - return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error { + return func(srv any, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error { return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler)) } } @@ -1477,12 +1511,12 @@ func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, inf if curr == len(interceptors)-1 { return finalHandler } - return func(srv interface{}, stream ServerStream) error { + return func(srv any, stream ServerStream) error { return interceptors[curr+1](srv, stream, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler)) } } -func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, sd *StreamDesc, trInfo *traceInfo) (err error) { +func (s *Server) processStreamingRPC(ctx context.Context, t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, sd *StreamDesc, trInfo *traceInfo) (err error) { if channelz.IsOn() { s.incrCallsStarted() } @@ -1496,15 +1530,15 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp IsServerStream: sd.ServerStreams, } for _, sh := range shs { - sh.HandleRPC(stream.Context(), statsBegin) + sh.HandleRPC(ctx, statsBegin) } } - ctx := NewContextWithServerTransportStream(stream.Context(), stream) + ctx = NewContextWithServerTransportStream(ctx, stream) ss := &serverStream{ ctx: ctx, t: t, s: stream, - p: &parser{r: stream}, + p: &parser{r: stream, recvBufferPool: s.opts.recvBufferPool}, codec: s.getCodec(stream.ContentSubtype()), maxReceiveMessageSize: s.opts.maxReceiveMessageSize, maxSendMessageSize: s.opts.maxSendMessageSize, @@ -1518,7 +1552,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp if trInfo != nil { ss.mu.Lock() if err != nil && err != io.EOF { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) + ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) ss.trInfo.tr.SetError() } ss.trInfo.tr.Finish() @@ -1535,7 +1569,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp end.Error = toRPCErr(err) } for _, sh := range shs { - sh.HandleRPC(stream.Context(), end) + sh.HandleRPC(ctx, end) } } @@ -1577,7 +1611,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp logEntry.PeerAddr = peer.Addr } for _, binlog := range ss.binlogs { - binlog.Log(stream.Context(), logEntry) + binlog.Log(ctx, logEntry) } } @@ -1621,7 +1655,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp trInfo.tr.LazyLog(&trInfo.firstLine, false) } var appErr error - var server interface{} + var server any if info != nil { server = info.serviceImpl } @@ -1655,7 +1689,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp Err: appErr, } for _, binlog := range ss.binlogs { - binlog.Log(stream.Context(), st) + binlog.Log(ctx, st) } } t.WriteStatus(ss.s, appStatus) @@ -1673,53 +1707,87 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp Err: appErr, } for _, binlog := range ss.binlogs { - binlog.Log(stream.Context(), st) + binlog.Log(ctx, st) } } return t.WriteStatus(ss.s, statusOK) } -func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream, trInfo *traceInfo) { +func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) { + ctx := stream.Context() + ctx = contextWithServer(ctx, s) + var ti *traceInfo + if EnableTracing { + tr := newTrace("grpc.Recv."+methodFamily(stream.Method()), stream.Method()) + ctx = newTraceContext(ctx, tr) + ti = &traceInfo{ + tr: tr, + firstLine: firstLine{ + client: false, + remoteAddr: t.Peer().Addr, + }, + } + if dl, ok := ctx.Deadline(); ok { + ti.firstLine.deadline = time.Until(dl) + } + } + sm := stream.Method() if sm != "" && sm[0] == '/' { sm = sm[1:] } pos := strings.LastIndex(sm, "/") if pos == -1 { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true) - trInfo.tr.SetError() + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{sm}}, true) + ti.tr.SetError() } errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) + ti.tr.SetError() } - channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err) + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) } - if trInfo != nil { - trInfo.tr.Finish() + if ti != nil { + ti.tr.Finish() } return } service := sm[:pos] method := sm[pos+1:] + md, _ := metadata.FromIncomingContext(ctx) + for _, sh := range s.opts.statsHandlers { + ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: stream.Method()}) + sh.HandleRPC(ctx, &stats.InHeader{ + FullMethod: stream.Method(), + RemoteAddr: t.Peer().Addr, + LocalAddr: t.Peer().LocalAddr, + Compression: stream.RecvCompress(), + WireLength: stream.HeaderWireLength(), + Header: md, + }) + } + // To have calls in stream callouts work. Will delete once all stats handler + // calls come from the gRPC layer. + stream.SetContext(ctx) + srv, knownService := s.services[service] if knownService { if md, ok := srv.methods[method]; ok { - s.processUnaryRPC(t, stream, srv, md, trInfo) + s.processUnaryRPC(ctx, t, stream, srv, md, ti) return } if sd, ok := srv.streams[method]; ok { - s.processStreamingRPC(t, stream, srv, sd, trInfo) + s.processStreamingRPC(ctx, t, stream, srv, sd, ti) return } } // Unknown service, or known server unknown method. if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil { - s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo) + s.processStreamingRPC(ctx, t, stream, nil, unknownDesc, ti) return } var errDesc string @@ -1728,19 +1796,19 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str } else { errDesc = fmt.Sprintf("unknown method %v for service %v", method, service) } - if trInfo != nil { - trInfo.tr.LazyPrintf("%s", errDesc) - trInfo.tr.SetError() + if ti != nil { + ti.tr.LazyPrintf("%s", errDesc) + ti.tr.SetError() } if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) + ti.tr.SetError() } - channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err) + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) } - if trInfo != nil { - trInfo.tr.Finish() + if ti != nil { + ti.tr.Finish() } } @@ -1795,62 +1863,71 @@ func ServerTransportStreamFromContext(ctx context.Context) ServerTransportStream // pending RPCs on the client side will get notified by connection // errors. func (s *Server) Stop() { - s.quit.Fire() + s.stop(false) +} - defer func() { - s.serveWG.Wait() - s.done.Fire() - }() +// GracefulStop stops the gRPC server gracefully. It stops the server from +// accepting new connections and RPCs and blocks until all the pending RPCs are +// finished. +func (s *Server) GracefulStop() { + s.stop(true) +} - s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) }) +func (s *Server) stop(graceful bool) { + s.quit.Fire() + defer s.done.Fire() + s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelz.ID) }) s.mu.Lock() - listeners := s.lis - s.lis = nil - conns := s.conns - s.conns = nil - // interrupt GracefulStop if Stop and GracefulStop are called concurrently. - s.cv.Broadcast() + s.closeListenersLocked() + // Wait for serving threads to be ready to exit. Only then can we be sure no + // new conns will be created. s.mu.Unlock() + s.serveWG.Wait() - for lis := range listeners { - lis.Close() + s.mu.Lock() + defer s.mu.Unlock() + + if graceful { + s.drainAllServerTransportsLocked() + } else { + s.closeServerTransportsLocked() } - for _, cs := range conns { - for st := range cs { - st.Close(errors.New("Server.Stop called")) - } + + for len(s.conns) != 0 { + s.cv.Wait() } + s.conns = nil + if s.opts.numServerWorkers > 0 { - s.stopServerWorkers() + // Closing the channel (only once, via grpcsync.OnceFunc) after all the + // connections have been closed above ensures that there are no + // goroutines executing the callback passed to st.HandleStreams (where + // the channel is written to). + s.serverWorkerChannelClose() + } + + if graceful || s.opts.waitForHandlers { + s.handlersWG.Wait() } - s.mu.Lock() if s.events != nil { s.events.Finish() s.events = nil } - s.mu.Unlock() } -// GracefulStop stops the gRPC server gracefully. It stops the server from -// accepting new connections and RPCs and blocks until all the pending RPCs are -// finished. -func (s *Server) GracefulStop() { - s.quit.Fire() - defer s.done.Fire() - - s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) }) - s.mu.Lock() - if s.conns == nil { - s.mu.Unlock() - return +// s.mu must be held by the caller. +func (s *Server) closeServerTransportsLocked() { + for _, conns := range s.conns { + for st := range conns { + st.Close(errors.New("Server.Stop called")) + } } +} - for lis := range s.lis { - lis.Close() - } - s.lis = nil +// s.mu must be held by the caller. +func (s *Server) drainAllServerTransportsLocked() { if !s.drain { for _, conns := range s.conns { for st := range conns { @@ -1859,22 +1936,14 @@ func (s *Server) GracefulStop() { } s.drain = true } +} - // Wait for serving threads to be ready to exit. Only then can we be sure no - // new conns will be created. - s.mu.Unlock() - s.serveWG.Wait() - s.mu.Lock() - - for len(s.conns) != 0 { - s.cv.Wait() - } - s.conns = nil - if s.events != nil { - s.events.Finish() - s.events = nil +// s.mu must be held by the caller. +func (s *Server) closeListenersLocked() { + for lis := range s.lis { + lis.Close() } - s.mu.Unlock() + s.lis = nil } // contentSubtype must be lowercase @@ -1888,11 +1957,50 @@ func (s *Server) getCodec(contentSubtype string) baseCodec { } codec := encoding.GetCodec(contentSubtype) if codec == nil { + logger.Warningf("Unsupported codec %q. Defaulting to %q for now. This will start to fail in future releases.", contentSubtype, proto.Name) return encoding.GetCodec(proto.Name) } return codec } +type serverKey struct{} + +// serverFromContext gets the Server from the context. +func serverFromContext(ctx context.Context) *Server { + s, _ := ctx.Value(serverKey{}).(*Server) + return s +} + +// contextWithServer sets the Server in the context. +func contextWithServer(ctx context.Context, server *Server) context.Context { + return context.WithValue(ctx, serverKey{}, server) +} + +// isRegisteredMethod returns whether the passed in method is registered as a +// method on the server. /service/method and service/method will match if the +// service and method are registered on the server. +func (s *Server) isRegisteredMethod(serviceMethod string) bool { + if serviceMethod != "" && serviceMethod[0] == '/' { + serviceMethod = serviceMethod[1:] + } + pos := strings.LastIndex(serviceMethod, "/") + if pos == -1 { // Invalid method name syntax. + return false + } + service := serviceMethod[:pos] + method := serviceMethod[pos+1:] + srv, knownService := s.services[service] + if knownService { + if _, ok := srv.methods[method]; ok { + return true + } + if _, ok := srv.streams[method]; ok { + return true + } + } + return false +} + // SetHeader sets the header metadata to be sent from the server to the client. // The context provided must be the context passed to the server's handler. // @@ -1994,7 +2102,7 @@ func ClientSupportedCompressors(ctx context.Context) ([]string, error) { return nil, fmt.Errorf("failed to fetch the stream from the given context %v", ctx) } - return strings.Split(stream.ClientAdvertisedCompressors(), ","), nil + return stream.ClientAdvertisedCompressors(), nil } // SetTrailer sets the trailer metadata that will be sent when an RPC returns. @@ -2024,17 +2132,9 @@ func Method(ctx context.Context) (string, bool) { return s.Method(), true } -type channelzServer struct { - s *Server -} - -func (c *channelzServer) ChannelzMetric() *channelz.ServerInternalMetric { - return c.s.channelzMetric() -} - // validateSendCompressor returns an error when given compressor name cannot be // handled by the server or the client based on the advertised compressors. -func validateSendCompressor(name, clientCompressors string) error { +func validateSendCompressor(name string, clientCompressors []string) error { if name == encoding.Identity { return nil } @@ -2043,7 +2143,7 @@ func validateSendCompressor(name, clientCompressors string) error { return fmt.Errorf("compressor not registered %q", name) } - for _, c := range strings.Split(clientCompressors, ",") { + for _, c := range clientCompressors { if c == name { return nil // found match } @@ -2054,12 +2154,12 @@ func validateSendCompressor(name, clientCompressors string) error { // atomicSemaphore implements a blocking, counting semaphore. acquire should be // called synchronously; release may be called asynchronously. type atomicSemaphore struct { - n int64 + n atomic.Int64 wait chan struct{} } func (q *atomicSemaphore) acquire() { - if atomic.AddInt64(&q.n, -1) < 0 { + if q.n.Add(-1) < 0 { // We ran out of quota. Block until a release happens. <-q.wait } @@ -2070,12 +2170,14 @@ func (q *atomicSemaphore) release() { // concurrent calls to acquire, but also note that with synchronous calls to // acquire, as our system does, n will never be less than -1. There are // fairness issues (queuing) to consider if this was to be generalized. - if atomic.AddInt64(&q.n, 1) <= 0 { + if q.n.Add(1) <= 0 { // An acquire was waiting on us. Unblock it. q.wait <- struct{}{} } } func newHandlerQuota(n uint32) *atomicSemaphore { - return &atomicSemaphore{n: int64(n), wait: make(chan struct{}, 1)} + a := &atomicSemaphore{wait: make(chan struct{}, 1)} + a.n.Store(int64(n)) + return a } diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go index 0df11fc09..2b35c5d21 100644 --- a/vendor/google.golang.org/grpc/service_config.go +++ b/vendor/google.golang.org/grpc/service_config.go @@ -25,8 +25,10 @@ import ( "reflect" "time" + "google.golang.org/grpc/balancer" "google.golang.org/grpc/codes" "google.golang.org/grpc/internal" + "google.golang.org/grpc/internal/balancer/gracefulswitch" internalserviceconfig "google.golang.org/grpc/internal/serviceconfig" "google.golang.org/grpc/serviceconfig" ) @@ -41,11 +43,6 @@ const maxInt = int(^uint(0) >> 1) // https://github.com/grpc/grpc/blob/master/doc/service_config.md type MethodConfig = internalserviceconfig.MethodConfig -type lbConfig struct { - name string - cfg serviceconfig.LoadBalancingConfig -} - // ServiceConfig is provided by the service provider and contains parameters for how // clients that connect to the service should behave. // @@ -55,14 +52,9 @@ type lbConfig struct { type ServiceConfig struct { serviceconfig.Config - // LB is the load balancer the service providers recommends. This is - // deprecated; lbConfigs is preferred. If lbConfig and LB are both present, - // lbConfig will be used. - LB *string - // lbConfig is the service config's load balancing configuration. If // lbConfig and LB are both present, lbConfig will be used. - lbConfig *lbConfig + lbConfig serviceconfig.LoadBalancingConfig // Methods contains a map for the methods in this service. If there is an // exact match for a method (i.e. /service/method) in the map, use the @@ -164,7 +156,7 @@ type jsonMC struct { // TODO(lyuxuan): delete this struct after cleaning up old service config implementation. type jsonSC struct { LoadBalancingPolicy *string - LoadBalancingConfig *internalserviceconfig.BalancerConfig + LoadBalancingConfig *json.RawMessage MethodConfig *[]jsonMC RetryThrottling *retryThrottlingPolicy HealthCheckConfig *healthCheckConfig @@ -184,18 +176,33 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult { return &serviceconfig.ParseResult{Err: err} } sc := ServiceConfig{ - LB: rsc.LoadBalancingPolicy, Methods: make(map[string]MethodConfig), retryThrottling: rsc.RetryThrottling, healthCheckConfig: rsc.HealthCheckConfig, rawJSONString: js, } - if c := rsc.LoadBalancingConfig; c != nil { - sc.lbConfig = &lbConfig{ - name: c.Name, - cfg: c.Config, + c := rsc.LoadBalancingConfig + if c == nil { + name := PickFirstBalancerName + if rsc.LoadBalancingPolicy != nil { + name = *rsc.LoadBalancingPolicy + } + if balancer.Get(name) == nil { + name = PickFirstBalancerName } + cfg := []map[string]any{{name: struct{}{}}} + strCfg, err := json.Marshal(cfg) + if err != nil { + return &serviceconfig.ParseResult{Err: fmt.Errorf("unexpected error marshaling simple LB config: %w", err)} + } + r := json.RawMessage(strCfg) + c = &r + } + cfg, err := gracefulswitch.ParseConfig(*c) + if err != nil { + return &serviceconfig.ParseResult{Err: err} } + sc.lbConfig = cfg if rsc.MethodConfig == nil { return &serviceconfig.ParseResult{Config: &sc} diff --git a/vendor/google.golang.org/grpc/shared_buffer_pool.go b/vendor/google.golang.org/grpc/shared_buffer_pool.go new file mode 100644 index 000000000..48a64cfe8 --- /dev/null +++ b/vendor/google.golang.org/grpc/shared_buffer_pool.go @@ -0,0 +1,154 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import "sync" + +// SharedBufferPool is a pool of buffers that can be shared, resulting in +// decreased memory allocation. Currently, in gRPC-go, it is only utilized +// for parsing incoming messages. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +type SharedBufferPool interface { + // Get returns a buffer with specified length from the pool. + // + // The returned byte slice may be not zero initialized. + Get(length int) []byte + + // Put returns a buffer to the pool. + Put(*[]byte) +} + +// NewSharedBufferPool creates a simple SharedBufferPool with buckets +// of different sizes to optimize memory usage. This prevents the pool from +// wasting large amounts of memory, even when handling messages of varying sizes. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func NewSharedBufferPool() SharedBufferPool { + return &simpleSharedBufferPool{ + pools: [poolArraySize]simpleSharedBufferChildPool{ + newBytesPool(level0PoolMaxSize), + newBytesPool(level1PoolMaxSize), + newBytesPool(level2PoolMaxSize), + newBytesPool(level3PoolMaxSize), + newBytesPool(level4PoolMaxSize), + newBytesPool(0), + }, + } +} + +// simpleSharedBufferPool is a simple implementation of SharedBufferPool. +type simpleSharedBufferPool struct { + pools [poolArraySize]simpleSharedBufferChildPool +} + +func (p *simpleSharedBufferPool) Get(size int) []byte { + return p.pools[p.poolIdx(size)].Get(size) +} + +func (p *simpleSharedBufferPool) Put(bs *[]byte) { + p.pools[p.poolIdx(cap(*bs))].Put(bs) +} + +func (p *simpleSharedBufferPool) poolIdx(size int) int { + switch { + case size <= level0PoolMaxSize: + return level0PoolIdx + case size <= level1PoolMaxSize: + return level1PoolIdx + case size <= level2PoolMaxSize: + return level2PoolIdx + case size <= level3PoolMaxSize: + return level3PoolIdx + case size <= level4PoolMaxSize: + return level4PoolIdx + default: + return levelMaxPoolIdx + } +} + +const ( + level0PoolMaxSize = 16 // 16 B + level1PoolMaxSize = level0PoolMaxSize * 16 // 256 B + level2PoolMaxSize = level1PoolMaxSize * 16 // 4 KB + level3PoolMaxSize = level2PoolMaxSize * 16 // 64 KB + level4PoolMaxSize = level3PoolMaxSize * 16 // 1 MB +) + +const ( + level0PoolIdx = iota + level1PoolIdx + level2PoolIdx + level3PoolIdx + level4PoolIdx + levelMaxPoolIdx + poolArraySize +) + +type simpleSharedBufferChildPool interface { + Get(size int) []byte + Put(any) +} + +type bufferPool struct { + sync.Pool + + defaultSize int +} + +func (p *bufferPool) Get(size int) []byte { + bs := p.Pool.Get().(*[]byte) + + if cap(*bs) < size { + p.Pool.Put(bs) + + return make([]byte, size) + } + + return (*bs)[:size] +} + +func newBytesPool(size int) simpleSharedBufferChildPool { + return &bufferPool{ + Pool: sync.Pool{ + New: func() any { + bs := make([]byte, size) + return &bs + }, + }, + defaultSize: size, + } +} + +// nopBufferPool is a buffer pool just makes new buffer without pooling. +type nopBufferPool struct { +} + +func (nopBufferPool) Get(length int) []byte { + return make([]byte, length) +} + +func (nopBufferPool) Put(*[]byte) { +} diff --git a/vendor/google.golang.org/grpc/stats/stats.go b/vendor/google.golang.org/grpc/stats/stats.go index 7a552a9b7..4ab70e2d4 100644 --- a/vendor/google.golang.org/grpc/stats/stats.go +++ b/vendor/google.golang.org/grpc/stats/stats.go @@ -59,12 +59,22 @@ func (s *Begin) IsClient() bool { return s.Client } func (s *Begin) isRPCStats() {} +// PickerUpdated indicates that the LB policy provided a new picker while the +// RPC was waiting for one. +type PickerUpdated struct{} + +// IsClient indicates if the stats information is from client side. Only Client +// Side interfaces with a Picker, thus always returns true. +func (*PickerUpdated) IsClient() bool { return true } + +func (*PickerUpdated) isRPCStats() {} + // InPayload contains the information for an incoming payload. type InPayload struct { // Client is true if this InPayload is from client side. Client bool // Payload is the payload with original type. - Payload interface{} + Payload any // Data is the serialized message payload. Data []byte @@ -134,7 +144,7 @@ type OutPayload struct { // Client is true if this OutPayload is from client side. Client bool // Payload is the payload with original type. - Payload interface{} + Payload any // Data is the serialized message payload. Data []byte // Length is the size of the uncompressed payload data. Does not include any diff --git a/vendor/google.golang.org/grpc/status/status.go b/vendor/google.golang.org/grpc/status/status.go index bcf2e4d81..a93360efb 100644 --- a/vendor/google.golang.org/grpc/status/status.go +++ b/vendor/google.golang.org/grpc/status/status.go @@ -50,7 +50,7 @@ func New(c codes.Code, msg string) *Status { } // Newf returns New(c, fmt.Sprintf(format, a...)). -func Newf(c codes.Code, format string, a ...interface{}) *Status { +func Newf(c codes.Code, format string, a ...any) *Status { return New(c, fmt.Sprintf(format, a...)) } @@ -60,7 +60,7 @@ func Error(c codes.Code, msg string) error { } // Errorf returns Error(c, fmt.Sprintf(format, a...)). -func Errorf(c codes.Code, format string, a ...interface{}) error { +func Errorf(c codes.Code, format string, a ...any) error { return Error(c, fmt.Sprintf(format, a...)) } @@ -99,25 +99,27 @@ func FromError(err error) (s *Status, ok bool) { } type grpcstatus interface{ GRPCStatus() *Status } if gs, ok := err.(grpcstatus); ok { - if gs.GRPCStatus() == nil { + grpcStatus := gs.GRPCStatus() + if grpcStatus == nil { // Error has status nil, which maps to codes.OK. There // is no sensible behavior for this, so we turn it into // an error with codes.Unknown and discard the existing // status. return New(codes.Unknown, err.Error()), false } - return gs.GRPCStatus(), true + return grpcStatus, true } var gs grpcstatus if errors.As(err, &gs) { - if gs.GRPCStatus() == nil { + grpcStatus := gs.GRPCStatus() + if grpcStatus == nil { // Error wraps an error that has status nil, which maps // to codes.OK. There is no sensible behavior for this, // so we turn it into an error with codes.Unknown and // discard the existing status. return New(codes.Unknown, err.Error()), false } - p := gs.GRPCStatus().Proto() + p := grpcStatus.Proto() p.Message = err.Error() return status.FromProto(p), true } diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index 10092685b..d939ffc63 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -27,10 +27,10 @@ import ( "sync" "time" - "golang.org/x/net/trace" "google.golang.org/grpc/balancer" "google.golang.org/grpc/codes" "google.golang.org/grpc/encoding" + "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/balancerload" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" @@ -47,6 +47,8 @@ import ( "google.golang.org/grpc/status" ) +var metadataFromOutgoingContextRaw = internal.FromOutgoingContextRaw.(func(context.Context) (metadata.MD, [][]string, bool)) + // StreamHandler defines the handler called by gRPC server to complete the // execution of a streaming RPC. // @@ -54,7 +56,7 @@ import ( // status package, or be one of the context errors. Otherwise, gRPC will use // codes.Unknown as the status code and err.Error() as the status message of the // RPC. -type StreamHandler func(srv interface{}, stream ServerStream) error +type StreamHandler func(srv any, stream ServerStream) error // StreamDesc represents a streaming RPC service's method specification. Used // on the server when registering services and on the client when initiating @@ -79,9 +81,9 @@ type Stream interface { // Deprecated: See ClientStream and ServerStream documentation instead. Context() context.Context // Deprecated: See ClientStream and ServerStream documentation instead. - SendMsg(m interface{}) error + SendMsg(m any) error // Deprecated: See ClientStream and ServerStream documentation instead. - RecvMsg(m interface{}) error + RecvMsg(m any) error } // ClientStream defines the client-side behavior of a streaming RPC. @@ -90,7 +92,9 @@ type Stream interface { // status package. type ClientStream interface { // Header returns the header metadata received from the server if there - // is any. It blocks if the metadata is not ready to read. + // is any. It blocks if the metadata is not ready to read. If the metadata + // is nil and the error is also nil, then the stream was terminated without + // headers, and the status can be discovered by calling RecvMsg. Header() (metadata.MD, error) // Trailer returns the trailer metadata from the server, if there is any. // It must only be called after stream.CloseAndRecv has returned, or @@ -126,7 +130,7 @@ type ClientStream interface { // // It is not safe to modify the message after calling SendMsg. Tracing // libraries and stats handlers may use the message lazily. - SendMsg(m interface{}) error + SendMsg(m any) error // RecvMsg blocks until it receives a message into m or the stream is // done. It returns io.EOF when the stream completes successfully. On // any other error, the stream is aborted and the error contains the RPC @@ -135,7 +139,7 @@ type ClientStream interface { // It is safe to have a goroutine calling SendMsg and another goroutine // calling RecvMsg on the same stream at the same time, but it is not // safe to call RecvMsg on the same stream in different goroutines. - RecvMsg(m interface{}) error + RecvMsg(m any) error } // NewStream creates a new Stream for the client side. This is typically @@ -155,11 +159,6 @@ type ClientStream interface { // If none of the above happen, a goroutine and a context will be leaked, and grpc // will not call the optionally-configured stats handler with a stats.End message. func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { - if err := cc.idlenessMgr.onCallBegin(); err != nil { - return nil, err - } - defer cc.idlenessMgr.onCallEnd() - // allow interceptor to see all applicable call options, which means those // configured as defaults from dial option as well as per-call options opts = combine(cc.dopts.callOptions, opts) @@ -176,7 +175,17 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth } func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { - if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok { + // Start tracking the RPC for idleness purposes. This is where a stream is + // created for both streaming and unary RPCs, and hence is a good place to + // track active RPC count. + if err := cc.idlenessMgr.OnCallBegin(); err != nil { + return nil, err + } + // Add a calloption, to decrement the active call count, that gets executed + // when the RPC completes. + opts = append([]CallOption{OnFinish(func(error) { cc.idlenessMgr.OnCallEnd() })}, opts...) + + if md, added, ok := metadataFromOutgoingContextRaw(ctx); ok { // validate md if err := imetadata.Validate(md); err != nil { return nil, status.Error(codes.Internal, err.Error()) @@ -421,7 +430,7 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error) var trInfo *traceInfo if EnableTracing { trInfo = &traceInfo{ - tr: trace.New("grpc.Sent."+methodFamily(method), method), + tr: newTrace("grpc.Sent."+methodFamily(method), method), firstLine: firstLine{ client: true, }, @@ -430,10 +439,10 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error) trInfo.firstLine.deadline = time.Until(deadline) } trInfo.tr.LazyLog(&trInfo.firstLine, false) - ctx = trace.NewContext(ctx, trInfo.tr) + ctx = newTraceContext(ctx, trInfo.tr) } - if cs.cc.parsedTarget.URL.Scheme == "xds" { + if cs.cc.parsedTarget.URL.Scheme == internal.GRPCResolverSchemeExtraMetadata { // Add extra metadata (metadata that will be added by transport) to context // so the balancer can see them. ctx = grpcutil.WithExtraMetadata(ctx, metadata.Pairs( @@ -507,7 +516,7 @@ func (a *csAttempt) newStream() error { return toRPCErr(nse.Err) } a.s = s - a.p = &parser{r: s} + a.p = &parser{r: s, recvBufferPool: a.cs.cc.dopts.recvBufferPool} return nil } @@ -646,13 +655,13 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) { if len(sps) == 1 { var e error if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 { - channelz.Infof(logger, cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0]) + channelz.Infof(logger, cs.cc.channelz, "Server retry pushback specified to abort (%q).", sps[0]) cs.retryThrottler.throttle() // This counts as a failure for throttling. return false, err } hasPushback = true } else if len(sps) > 1 { - channelz.Warningf(logger, cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps) + channelz.Warningf(logger, cs.cc.channelz, "Server retry pushback specified multiple values (%q); not retrying.", sps) cs.retryThrottler.throttle() // This counts as a failure for throttling. return false, err } @@ -788,23 +797,24 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func()) func (cs *clientStream) Header() (metadata.MD, error) { var m metadata.MD - noHeader := false err := cs.withRetry(func(a *csAttempt) error { var err error m, err = a.s.Header() - if err == transport.ErrNoHeaders { - noHeader = true - return nil - } return toRPCErr(err) }, cs.commitAttemptLocked) + if m == nil && err == nil { + // The stream ended with success. Finish the clientStream. + err = io.EOF + } + if err != nil { cs.finish(err) - return nil, err + // Do not return the error. The user should get it by calling Recv(). + return nil, nil } - if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged && !noHeader { + if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged && m != nil { // Only log if binary log is on and header has not been logged, and // there is actually headers to log. logEntry := &binarylog.ServerHeader{ @@ -820,6 +830,7 @@ func (cs *clientStream) Header() (metadata.MD, error) { binlog.Log(cs.ctx, logEntry) } } + return m, nil } @@ -860,7 +871,7 @@ func (cs *clientStream) bufferForRetryLocked(sz int, op func(a *csAttempt) error cs.buffer = append(cs.buffer, op) } -func (cs *clientStream) SendMsg(m interface{}) (err error) { +func (cs *clientStream) SendMsg(m any) (err error) { defer func() { if err != nil && err != io.EOF { // Call finish on the client stream for errors generated by this SendMsg @@ -904,7 +915,7 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) { return err } -func (cs *clientStream) RecvMsg(m interface{}) error { +func (cs *clientStream) RecvMsg(m any) error { if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged { // Call Header() to binary log header if it's not already logged. cs.Header() @@ -928,24 +939,6 @@ func (cs *clientStream) RecvMsg(m interface{}) error { if err != nil || !cs.desc.ServerStreams { // err != nil or non-server-streaming indicates end of stream. cs.finish(err) - - if len(cs.binlogs) != 0 { - // finish will not log Trailer. Log Trailer here. - logEntry := &binarylog.ServerTrailer{ - OnClientSide: true, - Trailer: cs.Trailer(), - Err: err, - } - if logEntry.Err == io.EOF { - logEntry.Err = nil - } - if peer, ok := peer.FromContext(cs.Context()); ok { - logEntry.PeerAddr = peer.Addr - } - for _, binlog := range cs.binlogs { - binlog.Log(cs.ctx, logEntry) - } - } } return err } @@ -1001,18 +994,30 @@ func (cs *clientStream) finish(err error) { } } } + cs.mu.Unlock() - // For binary logging. only log cancel in finish (could be caused by RPC ctx - // canceled or ClientConn closed). Trailer will be logged in RecvMsg. - // - // Only one of cancel or trailer needs to be logged. In the cases where - // users don't call RecvMsg, users must have already canceled the RPC. - if len(cs.binlogs) != 0 && status.Code(err) == codes.Canceled { - c := &binarylog.Cancel{ - OnClientSide: true, - } - for _, binlog := range cs.binlogs { - binlog.Log(cs.ctx, c) + // Only one of cancel or trailer needs to be logged. + if len(cs.binlogs) != 0 { + switch err { + case errContextCanceled, errContextDeadline, ErrClientConnClosing: + c := &binarylog.Cancel{ + OnClientSide: true, + } + for _, binlog := range cs.binlogs { + binlog.Log(cs.ctx, c) + } + default: + logEntry := &binarylog.ServerTrailer{ + OnClientSide: true, + Trailer: cs.Trailer(), + Err: err, + } + if peer, ok := peer.FromContext(cs.Context()); ok { + logEntry.PeerAddr = peer.Addr + } + for _, binlog := range cs.binlogs { + binlog.Log(cs.ctx, logEntry) + } } } if err == nil { @@ -1028,7 +1033,7 @@ func (cs *clientStream) finish(err error) { cs.cancel() } -func (a *csAttempt) sendMsg(m interface{}, hdr, payld, data []byte) error { +func (a *csAttempt) sendMsg(m any, hdr, payld, data []byte) error { cs := a.cs if a.trInfo != nil { a.mu.Lock() @@ -1055,7 +1060,7 @@ func (a *csAttempt) sendMsg(m interface{}, hdr, payld, data []byte) error { return nil } -func (a *csAttempt) recvMsg(m interface{}, payInfo *payloadInfo) (err error) { +func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) { cs := a.cs if len(a.statsHandlers) != 0 && payInfo == nil { payInfo = &payloadInfo{} @@ -1270,7 +1275,7 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin return nil, err } as.s = s - as.p = &parser{r: s} + as.p = &parser{r: s, recvBufferPool: ac.dopts.recvBufferPool} ac.incrCallsStarted() if desc != unaryStreamDesc { // Listen on stream context to cleanup when the stream context is @@ -1348,7 +1353,7 @@ func (as *addrConnStream) Context() context.Context { return as.s.Context() } -func (as *addrConnStream) SendMsg(m interface{}) (err error) { +func (as *addrConnStream) SendMsg(m any) (err error) { defer func() { if err != nil && err != io.EOF { // Call finish on the client stream for errors generated by this SendMsg @@ -1393,7 +1398,7 @@ func (as *addrConnStream) SendMsg(m interface{}) (err error) { return nil } -func (as *addrConnStream) RecvMsg(m interface{}) (err error) { +func (as *addrConnStream) RecvMsg(m any) (err error) { defer func() { if err != nil || !as.desc.ServerStreams { // err != nil or non-server-streaming indicates end of stream. @@ -1512,7 +1517,7 @@ type ServerStream interface { // // It is not safe to modify the message after calling SendMsg. Tracing // libraries and stats handlers may use the message lazily. - SendMsg(m interface{}) error + SendMsg(m any) error // RecvMsg blocks until it receives a message into m or the stream is // done. It returns io.EOF when the client has performed a CloseSend. On // any non-EOF error, the stream is aborted and the error contains the @@ -1521,7 +1526,7 @@ type ServerStream interface { // It is safe to have a goroutine calling SendMsg and another goroutine // calling RecvMsg on the same stream at the same time, but it is not // safe to call RecvMsg on the same stream in different goroutines. - RecvMsg(m interface{}) error + RecvMsg(m any) error } // serverStream implements a server side Stream. @@ -1602,7 +1607,7 @@ func (ss *serverStream) SetTrailer(md metadata.MD) { ss.s.SetTrailer(md) } -func (ss *serverStream) SendMsg(m interface{}) (err error) { +func (ss *serverStream) SendMsg(m any) (err error) { defer func() { if ss.trInfo != nil { ss.mu.Lock() @@ -1610,7 +1615,7 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) { if err == nil { ss.trInfo.tr.LazyLog(&payload{sent: true, msg: m}, true) } else { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) + ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) ss.trInfo.tr.SetError() } } @@ -1677,7 +1682,7 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) { return nil } -func (ss *serverStream) RecvMsg(m interface{}) (err error) { +func (ss *serverStream) RecvMsg(m any) (err error) { defer func() { if ss.trInfo != nil { ss.mu.Lock() @@ -1685,7 +1690,7 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) { if err == nil { ss.trInfo.tr.LazyLog(&payload{sent: false, msg: m}, true) } else if err != io.EOF { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) + ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) ss.trInfo.tr.SetError() } } @@ -1757,7 +1762,7 @@ func MethodFromServerStream(stream ServerStream) (string, bool) { // prepareMsg returns the hdr, payload and data // using the compressors passed or using the // passed preparedmsg -func prepareMsg(m interface{}, codec baseCodec, cp Compressor, comp encoding.Compressor) (hdr, payload, data []byte, err error) { +func prepareMsg(m any, codec baseCodec, cp Compressor, comp encoding.Compressor) (hdr, payload, data []byte, err error) { if preparedMsg, ok := m.(*PreparedMsg); ok { return preparedMsg.hdr, preparedMsg.payload, preparedMsg.encodedData, nil } diff --git a/vendor/google.golang.org/grpc/tap/tap.go b/vendor/google.golang.org/grpc/tap/tap.go index bfa5dfa40..07f012576 100644 --- a/vendor/google.golang.org/grpc/tap/tap.go +++ b/vendor/google.golang.org/grpc/tap/tap.go @@ -27,6 +27,8 @@ package tap import ( "context" + + "google.golang.org/grpc/metadata" ) // Info defines the relevant information needed by the handles. @@ -34,6 +36,10 @@ type Info struct { // FullMethodName is the string of grpc method (in the format of // /package.service/method). FullMethodName string + + // Header contains the header metadata received. + Header metadata.MD + // TODO: More to be added. } diff --git a/vendor/google.golang.org/grpc/trace.go b/vendor/google.golang.org/grpc/trace.go index 07a2d26b3..10f4f798f 100644 --- a/vendor/google.golang.org/grpc/trace.go +++ b/vendor/google.golang.org/grpc/trace.go @@ -26,8 +26,6 @@ import ( "strings" "sync" "time" - - "golang.org/x/net/trace" ) // EnableTracing controls whether to trace RPCs using the golang.org/x/net/trace package. @@ -44,9 +42,31 @@ func methodFamily(m string) string { return m } +// traceEventLog mirrors golang.org/x/net/trace.EventLog. +// +// It exists in order to avoid importing x/net/trace on grpcnotrace builds. +type traceEventLog interface { + Printf(format string, a ...any) + Errorf(format string, a ...any) + Finish() +} + +// traceLog mirrors golang.org/x/net/trace.Trace. +// +// It exists in order to avoid importing x/net/trace on grpcnotrace builds. +type traceLog interface { + LazyLog(x fmt.Stringer, sensitive bool) + LazyPrintf(format string, a ...any) + SetError() + SetRecycler(f func(any)) + SetTraceInfo(traceID, spanID uint64) + SetMaxEvents(m int) + Finish() +} + // traceInfo contains tracing information for an RPC. type traceInfo struct { - tr trace.Trace + tr traceLog firstLine firstLine } @@ -97,8 +117,8 @@ func truncate(x string, l int) string { // payload represents an RPC request or response payload. type payload struct { - sent bool // whether this is an outgoing payload - msg interface{} // e.g. a proto.Message + sent bool // whether this is an outgoing payload + msg any // e.g. a proto.Message // TODO(dsymonds): add stringifying info to codec, and limit how much we hold here? } @@ -111,7 +131,7 @@ func (p payload) String() string { type fmtStringer struct { format string - a []interface{} + a []any } func (f *fmtStringer) String() string { diff --git a/vendor/google.golang.org/grpc/trace_notrace.go b/vendor/google.golang.org/grpc/trace_notrace.go new file mode 100644 index 000000000..1da3a2308 --- /dev/null +++ b/vendor/google.golang.org/grpc/trace_notrace.go @@ -0,0 +1,52 @@ +//go:build grpcnotrace + +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +// grpcnotrace can be used to avoid importing golang.org/x/net/trace, which in +// turn enables binaries using gRPC-Go for dead code elimination, which can +// yield 10-15% improvements in binary size when tracing is not needed. + +import ( + "context" + "fmt" +) + +type notrace struct{} + +func (notrace) LazyLog(x fmt.Stringer, sensitive bool) {} +func (notrace) LazyPrintf(format string, a ...any) {} +func (notrace) SetError() {} +func (notrace) SetRecycler(f func(any)) {} +func (notrace) SetTraceInfo(traceID, spanID uint64) {} +func (notrace) SetMaxEvents(m int) {} +func (notrace) Finish() {} + +func newTrace(family, title string) traceLog { + return notrace{} +} + +func newTraceContext(ctx context.Context, tr traceLog) context.Context { + return ctx +} + +func newTraceEventLog(family, title string) traceEventLog { + return nil +} diff --git a/vendor/google.golang.org/grpc/trace_withtrace.go b/vendor/google.golang.org/grpc/trace_withtrace.go new file mode 100644 index 000000000..88d6e8571 --- /dev/null +++ b/vendor/google.golang.org/grpc/trace_withtrace.go @@ -0,0 +1,39 @@ +//go:build !grpcnotrace + +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "context" + + t "golang.org/x/net/trace" +) + +func newTrace(family, title string) traceLog { + return t.New(family, title) +} + +func newTraceContext(ctx context.Context, tr traceLog) context.Context { + return t.NewContext(ctx, tr) +} + +func newTraceEventLog(family, title string) traceEventLog { + return t.NewEventLog(family, title) +} diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 3cc754062..2556f7583 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.56.3" +const Version = "1.63.2" diff --git a/vendor/google.golang.org/grpc/vet.sh b/vendor/google.golang.org/grpc/vet.sh index a8e4732b3..7e6b92e49 100644 --- a/vendor/google.golang.org/grpc/vet.sh +++ b/vendor/google.golang.org/grpc/vet.sh @@ -35,14 +35,13 @@ if [[ "$1" = "-install" ]]; then # Install the pinned versions as defined in module tools. pushd ./test/tools go install \ - golang.org/x/lint/golint \ golang.org/x/tools/cmd/goimports \ honnef.co/go/tools/cmd/staticcheck \ github.com/client9/misspell/cmd/misspell popd if [[ -z "${VET_SKIP_PROTO}" ]]; then if [[ "${GITHUB_ACTIONS}" = "true" ]]; then - PROTOBUF_VERSION=22.0 # a.k.a v4.22.0 in pb.go files. + PROTOBUF_VERSION=25.2 # a.k.a. v4.22.0 in pb.go files. PROTOC_FILENAME=protoc-${PROTOBUF_VERSION}-linux-x86_64.zip pushd /home/runner/go wget https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/${PROTOC_FILENAME} @@ -77,12 +76,23 @@ fi not grep 'func Test[^(]' *_test.go not grep 'func Test[^(]' test/*.go +# - Check for typos in test function names +git grep 'func (s) ' -- "*_test.go" | not grep -v 'func (s) Test' +git grep 'func [A-Z]' -- "*_test.go" | not grep -v 'func Test\|Benchmark\|Example' + # - Do not import x/net/context. not git grep -l 'x/net/context' -- "*.go" +# - Do not use time.After except in tests. It has the potential to leak the +# timer since there is no way to stop it early. +git grep -l 'time.After(' -- "*.go" | not grep -v '_test.go\|test_utils\|testutils' + # - Do not import math/rand for real library code. Use internal/grpcrand for # thread safety. -git grep -l '"math/rand"' -- "*.go" 2>&1 | not grep -v '^examples\|^stress\|grpcrand\|^benchmark\|wrr_test' +git grep -l '"math/rand"' -- "*.go" 2>&1 | not grep -v '^examples\|^interop/stress\|grpcrand\|^benchmark\|wrr_test' + +# - Do not use "interface{}"; use "any" instead. +git grep -l 'interface{}' -- "*.go" 2>&1 | not grep -v '\.pb\.go\|protoc-gen-go-grpc\|grpc_testing_not_regenerate' # - Do not call grpclog directly. Use grpclog.Component instead. git grep -l -e 'grpclog.I' --or -e 'grpclog.W' --or -e 'grpclog.E' --or -e 'grpclog.F' --or -e 'grpclog.V' -- "*.go" | not grep -v '^grpclog/component.go\|^internal/grpctest/tlogger_test.go' @@ -90,13 +100,15 @@ git grep -l -e 'grpclog.I' --or -e 'grpclog.W' --or -e 'grpclog.E' --or -e 'grpc # - Ensure all ptypes proto packages are renamed when importing. not git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go" +# - Ensure all usages of grpc_testing package are renamed when importing. +not git grep "\(import \|^\s*\)\"google.golang.org/grpc/interop/grpc_testing" -- "*.go" + # - Ensure all xds proto imports are renamed to *pb or *grpc. git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.pb.go' | not grep -v 'pb "\|grpc "' misspell -error . -# - gofmt, goimports, golint (with exceptions for generated code), go vet, -# go mod tidy. +# - gofmt, goimports, go vet, go mod tidy. # Perform these checks on each module inside gRPC. for MOD_FILE in $(find . -name 'go.mod'); do MOD_DIR=$(dirname ${MOD_FILE}) @@ -104,105 +116,80 @@ for MOD_FILE in $(find . -name 'go.mod'); do go vet -all ./... | fail_on_output gofmt -s -d -l . 2>&1 | fail_on_output goimports -l . 2>&1 | not grep -vE "\.pb\.go" - golint ./... 2>&1 | not grep -vE "/grpc_testing_not_regenerate/.*\.pb\.go:" - go mod tidy -compat=1.17 + go mod tidy -compat=1.19 git status --porcelain 2>&1 | fail_on_output || \ (git status; git --no-pager diff; exit 1) popd done # - Collection of static analysis checks -# -# TODO(dfawley): don't use deprecated functions in examples or first-party -# plugins. -# TODO(dfawley): enable ST1019 (duplicate imports) but allow for protobufs. SC_OUT="$(mktemp)" -staticcheck -go 1.19 -checks 'inherit,-ST1015,-ST1019,-SA1019' ./... > "${SC_OUT}" || true -# Error if anything other than deprecation warnings are printed. -not grep -v "is deprecated:.*SA1019" "${SC_OUT}" -# Only ignore the following deprecated types/fields/functions. -not grep -Fv '.CredsBundle -.HeaderMap -.Metadata is deprecated: use Attributes -.NewAddress -.NewServiceConfig -.Type is deprecated: use Attributes -BuildVersion is deprecated -balancer.ErrTransientFailure -balancer.Picker -extDesc.Filename is deprecated -github.com/golang/protobuf/jsonpb is deprecated -grpc.CallCustomCodec -grpc.Code -grpc.Compressor -grpc.CustomCodec -grpc.Decompressor -grpc.MaxMsgSize -grpc.MethodConfig -grpc.NewGZIPCompressor -grpc.NewGZIPDecompressor -grpc.RPCCompressor -grpc.RPCDecompressor -grpc.ServiceConfig -grpc.WithCompressor -grpc.WithDecompressor -grpc.WithDialer -grpc.WithMaxMsgSize -grpc.WithServiceConfig -grpc.WithTimeout -http.CloseNotifier -info.SecurityVersion -proto is deprecated -proto.InternalMessageInfo is deprecated -proto.EnumName is deprecated -proto.ErrInternalBadWireType is deprecated -proto.FileDescriptor is deprecated -proto.Marshaler is deprecated -proto.MessageType is deprecated -proto.RegisterEnum is deprecated -proto.RegisterFile is deprecated -proto.RegisterType is deprecated -proto.RegisterExtension is deprecated -proto.RegisteredExtension is deprecated -proto.RegisteredExtensions is deprecated -proto.RegisterMapType is deprecated -proto.Unmarshaler is deprecated -resolver.Backend -resolver.GRPCLB +staticcheck -go 1.19 -checks 'all' ./... > "${SC_OUT}" || true + +# Error for anything other than checks that need exclusions. +grep -v "(ST1000)" "${SC_OUT}" | grep -v "(SA1019)" | grep -v "(ST1003)" | not grep -v "(ST1019)\|\(other import of\)" + +# Exclude underscore checks for generated code. +grep "(ST1003)" "${SC_OUT}" | not grep -v '\(.pb.go:\)\|\(code_string_test.go:\)\|\(grpc_testing_not_regenerate\)' + +# Error for duplicate imports not including grpc protos. +grep "(ST1019)\|\(other import of\)" "${SC_OUT}" | not grep -Fv 'XXXXX PleaseIgnoreUnused +channelz/grpc_channelz_v1" +go-control-plane/envoy +grpclb/grpc_lb_v1" +health/grpc_health_v1" +interop/grpc_testing" +orca/v3" +proto/grpc_gcp" +proto/grpc_lookup_v1" +reflection/grpc_reflection_v1" +reflection/grpc_reflection_v1alpha" +XXXXX PleaseIgnoreUnused' + +# Error for any package comments not in generated code. +grep "(ST1000)" "${SC_OUT}" | not grep -v "\.pb\.go:" + +# Only ignore the following deprecated types/fields/functions and exclude +# generated code. +grep "(SA1019)" "${SC_OUT}" | not grep -Fv 'XXXXX PleaseIgnoreUnused +XXXXX Protobuf related deprecation errors: +"github.com/golang/protobuf +.pb.go: +grpc_testing_not_regenerate +: ptypes. +proto.RegisterType +XXXXX gRPC internal usage deprecation errors: +"google.golang.org/grpc +: grpc. +: v1alpha. +: v1alphareflectionpb. +BalancerAttributes is deprecated: +CredsBundle is deprecated: +Metadata is deprecated: use Attributes instead. +NewSubConn is deprecated: +OverrideServerName is deprecated: +RemoveSubConn is deprecated: +SecurityVersion is deprecated: Target is deprecated: Use the Target field in the BuildOptions instead. -xxx_messageInfo_ -' "${SC_OUT}" - -# - special golint on package comments. -lint_package_comment_per_package() { - # Number of files in this go package. - fileCount=$(go list -f '{{len .GoFiles}}' $1) - if [ ${fileCount} -eq 0 ]; then - return 0 - fi - # Number of package errors generated by golint. - lintPackageCommentErrorsCount=$(golint --min_confidence 0 $1 | grep -c "should have a package comment") - # golint complains about every file that's missing the package comment. If the - # number of files for this package is greater than the number of errors, there's - # at least one file with package comment, good. Otherwise, fail. - if [ ${fileCount} -le ${lintPackageCommentErrorsCount} ]; then - echo "Package $1 (with ${fileCount} files) is missing package comment" - return 1 - fi -} -lint_package_comment() { - set +ex - - count=0 - for i in $(go list ./...); do - lint_package_comment_per_package "$i" - ((count += $?)) - done - - set -ex - return $count -} -lint_package_comment +UpdateAddresses is deprecated: +UpdateSubConnState is deprecated: +balancer.ErrTransientFailure is deprecated: +grpc/reflection/v1alpha/reflection.proto +SwitchTo is deprecated: +XXXXX xDS deprecated fields we support +.ExactMatch +.PrefixMatch +.SafeRegexMatch +.SuffixMatch +GetContainsMatch +GetExactMatch +GetMatchSubjectAltNames +GetPrefixMatch +GetSafeRegexMatch +GetSuffixMatch +GetTlsCertificateCertificateProviderInstance +GetValidationContextCertificateProviderInstance +XXXXX PleaseIgnoreUnused' echo SUCCESS diff --git a/vendor/google.golang.org/protobuf/encoding/protodelim/protodelim.go b/vendor/google.golang.org/protobuf/encoding/protodelim/protodelim.go new file mode 100644 index 000000000..2ef36bbcf --- /dev/null +++ b/vendor/google.golang.org/protobuf/encoding/protodelim/protodelim.go @@ -0,0 +1,160 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protodelim marshals and unmarshals varint size-delimited messages. +package protodelim + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/errors" + "google.golang.org/protobuf/proto" +) + +// MarshalOptions is a configurable varint size-delimited marshaler. +type MarshalOptions struct{ proto.MarshalOptions } + +// MarshalTo writes a varint size-delimited wire-format message to w. +// If w returns an error, MarshalTo returns it unchanged. +func (o MarshalOptions) MarshalTo(w io.Writer, m proto.Message) (int, error) { + msgBytes, err := o.MarshalOptions.Marshal(m) + if err != nil { + return 0, err + } + + sizeBytes := protowire.AppendVarint(nil, uint64(len(msgBytes))) + sizeWritten, err := w.Write(sizeBytes) + if err != nil { + return sizeWritten, err + } + msgWritten, err := w.Write(msgBytes) + if err != nil { + return sizeWritten + msgWritten, err + } + return sizeWritten + msgWritten, nil +} + +// MarshalTo writes a varint size-delimited wire-format message to w +// with the default options. +// +// See the documentation for [MarshalOptions.MarshalTo]. +func MarshalTo(w io.Writer, m proto.Message) (int, error) { + return MarshalOptions{}.MarshalTo(w, m) +} + +// UnmarshalOptions is a configurable varint size-delimited unmarshaler. +type UnmarshalOptions struct { + proto.UnmarshalOptions + + // MaxSize is the maximum size in wire-format bytes of a single message. + // Unmarshaling a message larger than MaxSize will return an error. + // A zero MaxSize will default to 4 MiB. + // Setting MaxSize to -1 disables the limit. + MaxSize int64 +} + +const defaultMaxSize = 4 << 20 // 4 MiB, corresponds to the default gRPC max request/response size + +// SizeTooLargeError is an error that is returned when the unmarshaler encounters a message size +// that is larger than its configured [UnmarshalOptions.MaxSize]. +type SizeTooLargeError struct { + // Size is the varint size of the message encountered + // that was larger than the provided MaxSize. + Size uint64 + + // MaxSize is the MaxSize limit configured in UnmarshalOptions, which Size exceeded. + MaxSize uint64 +} + +func (e *SizeTooLargeError) Error() string { + return fmt.Sprintf("message size %d exceeded unmarshaler's maximum configured size %d", e.Size, e.MaxSize) +} + +// Reader is the interface expected by [UnmarshalFrom]. +// It is implemented by *[bufio.Reader]. +type Reader interface { + io.Reader + io.ByteReader +} + +// UnmarshalFrom parses and consumes a varint size-delimited wire-format message +// from r. +// The provided message must be mutable (e.g., a non-nil pointer to a message). +// +// The error is [io.EOF] error only if no bytes are read. +// If an EOF happens after reading some but not all the bytes, +// UnmarshalFrom returns a non-io.EOF error. +// In particular if r returns a non-io.EOF error, UnmarshalFrom returns it unchanged, +// and if only a size is read with no subsequent message, [io.ErrUnexpectedEOF] is returned. +func (o UnmarshalOptions) UnmarshalFrom(r Reader, m proto.Message) error { + var sizeArr [binary.MaxVarintLen64]byte + sizeBuf := sizeArr[:0] + for i := range sizeArr { + b, err := r.ReadByte() + if err != nil { + // Immediate EOF is unexpected. + if err == io.EOF && i != 0 { + break + } + return err + } + sizeBuf = append(sizeBuf, b) + if b < 0x80 { + break + } + } + size, n := protowire.ConsumeVarint(sizeBuf) + if n < 0 { + return protowire.ParseError(n) + } + + maxSize := o.MaxSize + if maxSize == 0 { + maxSize = defaultMaxSize + } + if maxSize != -1 && size > uint64(maxSize) { + return errors.Wrap(&SizeTooLargeError{Size: size, MaxSize: uint64(maxSize)}, "") + } + + var b []byte + var err error + if br, ok := r.(*bufio.Reader); ok { + // Use the []byte from the bufio.Reader instead of having to allocate one. + // This reduces CPU usage and allocated bytes. + b, err = br.Peek(int(size)) + if err == nil { + defer br.Discard(int(size)) + } else { + b = nil + } + } + if b == nil { + b = make([]byte, size) + _, err = io.ReadFull(r, b) + } + + if err == io.EOF { + return io.ErrUnexpectedEOF + } + if err != nil { + return err + } + if err := o.Unmarshal(b, m); err != nil { + return err + } + return nil +} + +// UnmarshalFrom parses and consumes a varint size-delimited wire-format message +// from r with the default options. +// The provided message must be mutable (e.g., a non-nil pointer to a message). +// +// See the documentation for [UnmarshalOptions.UnmarshalFrom]. +func UnmarshalFrom(r Reader, m proto.Message) error { + return UnmarshalOptions{}.UnmarshalFrom(r, m) +} diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/decode.go b/vendor/google.golang.org/protobuf/encoding/protojson/decode.go index 5f28148d8..f47902371 100644 --- a/vendor/google.golang.org/protobuf/encoding/protojson/decode.go +++ b/vendor/google.golang.org/protobuf/encoding/protojson/decode.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/internal/encoding/json" "google.golang.org/protobuf/internal/encoding/messageset" "google.golang.org/protobuf/internal/errors" @@ -23,7 +24,7 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" ) -// Unmarshal reads the given []byte into the given proto.Message. +// Unmarshal reads the given []byte into the given [proto.Message]. // The provided message must be mutable (e.g., a non-nil pointer to a message). func Unmarshal(b []byte, m proto.Message) error { return UnmarshalOptions{}.Unmarshal(b, m) @@ -37,7 +38,7 @@ type UnmarshalOptions struct { // required fields will not return an error. AllowPartial bool - // If DiscardUnknown is set, unknown fields are ignored. + // If DiscardUnknown is set, unknown fields and enum name values are ignored. DiscardUnknown bool // Resolver is used for looking up types when unmarshaling @@ -47,9 +48,13 @@ type UnmarshalOptions struct { protoregistry.MessageTypeResolver protoregistry.ExtensionTypeResolver } + + // RecursionLimit limits how deeply messages may be nested. + // If zero, a default limit is applied. + RecursionLimit int } -// Unmarshal reads the given []byte and populates the given proto.Message +// Unmarshal reads the given []byte and populates the given [proto.Message] // using options in the UnmarshalOptions object. // It will clear the message first before setting the fields. // If it returns an error, the given message may be partially set. @@ -67,6 +72,9 @@ func (o UnmarshalOptions) unmarshal(b []byte, m proto.Message) error { if o.Resolver == nil { o.Resolver = protoregistry.GlobalTypes } + if o.RecursionLimit == 0 { + o.RecursionLimit = protowire.DefaultRecursionLimit + } dec := decoder{json.NewDecoder(b), o} if err := dec.unmarshalMessage(m.ProtoReflect(), false); err != nil { @@ -114,6 +122,10 @@ func (d decoder) syntaxError(pos int, f string, x ...interface{}) error { // unmarshalMessage unmarshals a message into the given protoreflect.Message. func (d decoder) unmarshalMessage(m protoreflect.Message, skipTypeURL bool) error { + d.opts.RecursionLimit-- + if d.opts.RecursionLimit < 0 { + return errors.New("exceeded max recursion depth") + } if unmarshal := wellKnownTypeUnmarshaler(m.Descriptor().FullName()); unmarshal != nil { return unmarshal(d, m) } @@ -266,7 +278,9 @@ func (d decoder) unmarshalSingular(m protoreflect.Message, fd protoreflect.Field if err != nil { return err } - m.Set(fd, val) + if val.IsValid() { + m.Set(fd, val) + } return nil } @@ -329,7 +343,7 @@ func (d decoder) unmarshalScalar(fd protoreflect.FieldDescriptor) (protoreflect. } case protoreflect.EnumKind: - if v, ok := unmarshalEnum(tok, fd); ok { + if v, ok := unmarshalEnum(tok, fd, d.opts.DiscardUnknown); ok { return v, nil } @@ -474,7 +488,7 @@ func unmarshalBytes(tok json.Token) (protoreflect.Value, bool) { return protoreflect.ValueOfBytes(b), true } -func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor) (protoreflect.Value, bool) { +func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor, discardUnknown bool) (protoreflect.Value, bool) { switch tok.Kind() { case json.String: // Lookup EnumNumber based on name. @@ -482,6 +496,9 @@ func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor) (protoreflec if enumVal := fd.Enum().Values().ByName(protoreflect.Name(s)); enumVal != nil { return protoreflect.ValueOfEnum(enumVal.Number()), true } + if discardUnknown { + return protoreflect.Value{}, true + } case json.Number: if n, ok := tok.Int(32); ok { @@ -542,7 +559,9 @@ func (d decoder) unmarshalList(list protoreflect.List, fd protoreflect.FieldDesc if err != nil { return err } - list.Append(val) + if val.IsValid() { + list.Append(val) + } } } @@ -609,8 +628,9 @@ Loop: if err != nil { return err } - - mmap.Set(pkey, pval) + if pval.IsValid() { + mmap.Set(pkey, pval) + } } return nil diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/doc.go b/vendor/google.golang.org/protobuf/encoding/protojson/doc.go index 21d5d2cb1..ae71007c1 100644 --- a/vendor/google.golang.org/protobuf/encoding/protojson/doc.go +++ b/vendor/google.golang.org/protobuf/encoding/protojson/doc.go @@ -6,6 +6,6 @@ // format. It follows the guide at // https://protobuf.dev/programming-guides/proto3#json. // -// This package produces a different output than the standard "encoding/json" +// This package produces a different output than the standard [encoding/json] // package, which does not operate correctly on protocol buffer messages. package protojson diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/encode.go b/vendor/google.golang.org/protobuf/encoding/protojson/encode.go index d09d22e13..3f75098b6 100644 --- a/vendor/google.golang.org/protobuf/encoding/protojson/encode.go +++ b/vendor/google.golang.org/protobuf/encoding/protojson/encode.go @@ -31,7 +31,7 @@ func Format(m proto.Message) string { return MarshalOptions{Multiline: true}.Format(m) } -// Marshal writes the given proto.Message in JSON format using default options. +// Marshal writes the given [proto.Message] in JSON format using default options. // Do not depend on the output being stable. It may change over time across // different versions of the program. func Marshal(m proto.Message) ([]byte, error) { @@ -81,6 +81,25 @@ type MarshalOptions struct { // â•šâ•â•â•â•â•â•â•â•§â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• EmitUnpopulated bool + // EmitDefaultValues specifies whether to emit default-valued primitive fields, + // empty lists, and empty maps. The fields affected are as follows: + // â•”â•â•â•â•â•â•â•â•¤â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•— + // â•‘ JSON │ Protobuf field â•‘ + // â• â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•£ + // â•‘ false │ non-optional scalar boolean fields â•‘ + // â•‘ 0 │ non-optional scalar numeric fields â•‘ + // â•‘ "" │ non-optional scalar string/byte fields â•‘ + // â•‘ [] │ empty repeated fields â•‘ + // â•‘ {} │ empty map fields â•‘ + // â•šâ•â•â•â•â•â•â•â•§â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• + // + // Behaves similarly to EmitUnpopulated, but does not emit "null"-value fields, + // i.e. presence-sensing fields that are omitted will remain omitted to preserve + // presence-sensing. + // EmitUnpopulated takes precedence over EmitDefaultValues since the former generates + // a strict superset of the latter. + EmitDefaultValues bool + // Resolver is used for looking up types when expanding google.protobuf.Any // messages. If nil, this defaults to using protoregistry.GlobalTypes. Resolver interface { @@ -102,17 +121,23 @@ func (o MarshalOptions) Format(m proto.Message) string { return string(b) } -// Marshal marshals the given proto.Message in the JSON format using options in +// Marshal marshals the given [proto.Message] in the JSON format using options in // MarshalOptions. Do not depend on the output being stable. It may change over // time across different versions of the program. func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { - return o.marshal(m) + return o.marshal(nil, m) +} + +// MarshalAppend appends the JSON format encoding of m to b, +// returning the result. +func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) { + return o.marshal(b, m) } // marshal is a centralized function that all marshal operations go through. // For profiling purposes, avoid changing the name of this function or // introducing other code paths for marshal that do not go through this. -func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { +func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) { if o.Multiline && o.Indent == "" { o.Indent = defaultIndent } @@ -120,7 +145,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { o.Resolver = protoregistry.GlobalTypes } - internalEnc, err := json.NewEncoder(o.Indent) + internalEnc, err := json.NewEncoder(b, o.Indent) if err != nil { return nil, err } @@ -128,7 +153,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { // Treat nil message interface as an empty message, // in which case the output in an empty JSON object. if m == nil { - return []byte("{}"), nil + return append(b, '{', '}'), nil } enc := encoder{internalEnc, o} @@ -172,7 +197,11 @@ func (m typeURLFieldRanger) Range(f func(protoreflect.FieldDescriptor, protorefl // unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range // method to additionally iterate over unpopulated fields. -type unpopulatedFieldRanger struct{ protoreflect.Message } +type unpopulatedFieldRanger struct { + protoreflect.Message + + skipNull bool +} func (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { fds := m.Descriptor().Fields() @@ -186,6 +215,9 @@ func (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, proto isProto2Scalar := fd.Syntax() == protoreflect.Proto2 && fd.Default().IsValid() isSingularMessage := fd.Cardinality() != protoreflect.Repeated && fd.Message() != nil if isProto2Scalar || isSingularMessage { + if m.skipNull { + continue + } v = protoreflect.Value{} // use invalid value to emit null } if !f(fd, v) { @@ -211,8 +243,11 @@ func (e encoder) marshalMessage(m protoreflect.Message, typeURL string) error { defer e.EndObject() var fields order.FieldRanger = m - if e.opts.EmitUnpopulated { - fields = unpopulatedFieldRanger{m} + switch { + case e.opts.EmitUnpopulated: + fields = unpopulatedFieldRanger{Message: m, skipNull: false} + case e.opts.EmitDefaultValues: + fields = unpopulatedFieldRanger{Message: m, skipNull: true} } if typeURL != "" { fields = typeURLFieldRanger{fields, typeURL} diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go b/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go index 6c37d4174..4b177c820 100644 --- a/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go +++ b/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go @@ -176,7 +176,7 @@ func (d decoder) unmarshalAny(m protoreflect.Message) error { // Use another decoder to parse the unread bytes for @type field. This // avoids advancing a read from current decoder because the current JSON // object may contain the fields of the embedded type. - dec := decoder{d.Clone(), UnmarshalOptions{}} + dec := decoder{d.Clone(), UnmarshalOptions{RecursionLimit: d.opts.RecursionLimit}} tok, err := findTypeURL(dec) switch err { case errEmptyObject: @@ -308,48 +308,29 @@ Loop: // array) in order to advance the read to the next JSON value. It relies on // the decoder returning an error if the types are not in valid sequence. func (d decoder) skipJSONValue() error { - tok, err := d.Read() - if err != nil { - return err - } - // Only need to continue reading for objects and arrays. - switch tok.Kind() { - case json.ObjectOpen: - for { - tok, err := d.Read() - if err != nil { - return err - } - switch tok.Kind() { - case json.ObjectClose: - return nil - case json.Name: - // Skip object field value. - if err := d.skipJSONValue(); err != nil { - return err - } - } + var open int + for { + tok, err := d.Read() + if err != nil { + return err } - - case json.ArrayOpen: - for { - tok, err := d.Peek() - if err != nil { - return err - } - switch tok.Kind() { - case json.ArrayClose: - d.Read() - return nil - default: - // Skip array item. - if err := d.skipJSONValue(); err != nil { - return err - } + switch tok.Kind() { + case json.ObjectClose, json.ArrayClose: + open-- + case json.ObjectOpen, json.ArrayOpen: + open++ + if open > d.opts.RecursionLimit { + return errors.New("exceeded max recursion depth") } + case json.EOF: + // This can only happen if there's a bug in Decoder.Read. + // Avoid an infinite loop if this does happen. + return errors.New("unexpected EOF") + } + if open == 0 { + return nil } } - return nil } // unmarshalAnyValue unmarshals the given custom-type message from the JSON diff --git a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go index 4921b2d4a..a45f112bc 100644 --- a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go +++ b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go @@ -21,7 +21,7 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" ) -// Unmarshal reads the given []byte into the given proto.Message. +// Unmarshal reads the given []byte into the given [proto.Message]. // The provided message must be mutable (e.g., a non-nil pointer to a message). func Unmarshal(b []byte, m proto.Message) error { return UnmarshalOptions{}.Unmarshal(b, m) @@ -51,7 +51,7 @@ type UnmarshalOptions struct { } } -// Unmarshal reads the given []byte and populates the given proto.Message +// Unmarshal reads the given []byte and populates the given [proto.Message] // using options in the UnmarshalOptions object. // The provided message must be mutable (e.g., a non-nil pointer to a message). func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error { @@ -739,7 +739,9 @@ func (d decoder) skipValue() error { case text.ListClose: return nil case text.MessageOpen: - return d.skipMessageValue() + if err := d.skipMessageValue(); err != nil { + return err + } default: // Skip items. This will not validate whether skipped values are // of the same type or not, same behavior as C++ diff --git a/vendor/google.golang.org/protobuf/encoding/prototext/encode.go b/vendor/google.golang.org/protobuf/encoding/prototext/encode.go index ebf6c6528..95967e811 100644 --- a/vendor/google.golang.org/protobuf/encoding/prototext/encode.go +++ b/vendor/google.golang.org/protobuf/encoding/prototext/encode.go @@ -33,7 +33,7 @@ func Format(m proto.Message) string { return MarshalOptions{Multiline: true}.Format(m) } -// Marshal writes the given proto.Message in textproto format using default +// Marshal writes the given [proto.Message] in textproto format using default // options. Do not depend on the output being stable. It may change over time // across different versions of the program. func Marshal(m proto.Message) ([]byte, error) { @@ -97,17 +97,23 @@ func (o MarshalOptions) Format(m proto.Message) string { return string(b) } -// Marshal writes the given proto.Message in textproto format using options in +// Marshal writes the given [proto.Message] in textproto format using options in // MarshalOptions object. Do not depend on the output being stable. It may // change over time across different versions of the program. func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { - return o.marshal(m) + return o.marshal(nil, m) +} + +// MarshalAppend appends the textproto format encoding of m to b, +// returning the result. +func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) { + return o.marshal(b, m) } // marshal is a centralized function that all marshal operations go through. // For profiling purposes, avoid changing the name of this function or // introducing other code paths for marshal that do not go through this. -func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { +func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) { var delims = [2]byte{'{', '}'} if o.Multiline && o.Indent == "" { @@ -117,7 +123,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { o.Resolver = protoregistry.GlobalTypes } - internalEnc, err := text.NewEncoder(o.Indent, delims, o.EmitASCII) + internalEnc, err := text.NewEncoder(b, o.Indent, delims, o.EmitASCII) if err != nil { return nil, err } @@ -125,7 +131,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { // Treat nil message interface as an empty message, // in which case there is nothing to output. if m == nil { - return []byte{}, nil + return b, nil } enc := encoder{internalEnc, o} diff --git a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go index f4b4686cf..e942bc983 100644 --- a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go +++ b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go @@ -6,7 +6,7 @@ // See https://protobuf.dev/programming-guides/encoding. // // For marshaling and unmarshaling entire protobuf messages, -// use the "google.golang.org/protobuf/proto" package instead. +// use the [google.golang.org/protobuf/proto] package instead. package protowire import ( @@ -87,7 +87,7 @@ func ParseError(n int) error { // ConsumeField parses an entire field record (both tag and value) and returns // the field number, the wire type, and the total length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). // // The total length includes the tag header and the end group marker (if the // field is a group). @@ -104,8 +104,8 @@ func ConsumeField(b []byte) (Number, Type, int) { } // ConsumeFieldValue parses a field value and returns its length. -// This assumes that the field Number and wire Type have already been parsed. -// This returns a negative length upon an error (see ParseError). +// This assumes that the field [Number] and wire [Type] have already been parsed. +// This returns a negative length upon an error (see [ParseError]). // // When parsing a group, the length includes the end group marker and // the end group is verified to match the starting field number. @@ -164,7 +164,7 @@ func AppendTag(b []byte, num Number, typ Type) []byte { } // ConsumeTag parses b as a varint-encoded tag, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeTag(b []byte) (Number, Type, int) { v, n := ConsumeVarint(b) if n < 0 { @@ -263,7 +263,7 @@ func AppendVarint(b []byte, v uint64) []byte { } // ConsumeVarint parses b as a varint-encoded uint64, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeVarint(b []byte) (v uint64, n int) { var y uint64 if len(b) <= 0 { @@ -384,7 +384,7 @@ func AppendFixed32(b []byte, v uint32) []byte { } // ConsumeFixed32 parses b as a little-endian uint32, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeFixed32(b []byte) (v uint32, n int) { if len(b) < 4 { return 0, errCodeTruncated @@ -412,7 +412,7 @@ func AppendFixed64(b []byte, v uint64) []byte { } // ConsumeFixed64 parses b as a little-endian uint64, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeFixed64(b []byte) (v uint64, n int) { if len(b) < 8 { return 0, errCodeTruncated @@ -432,7 +432,7 @@ func AppendBytes(b []byte, v []byte) []byte { } // ConsumeBytes parses b as a length-prefixed bytes value, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeBytes(b []byte) (v []byte, n int) { m, n := ConsumeVarint(b) if n < 0 { @@ -456,7 +456,7 @@ func AppendString(b []byte, v string) []byte { } // ConsumeString parses b as a length-prefixed bytes value, reporting its length. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeString(b []byte) (v string, n int) { bb, n := ConsumeBytes(b) return string(bb), n @@ -471,7 +471,7 @@ func AppendGroup(b []byte, num Number, v []byte) []byte { // ConsumeGroup parses b as a group value until the trailing end group marker, // and verifies that the end marker matches the provided num. The value v // does not contain the end marker, while the length does contain the end marker. -// This returns a negative length upon an error (see ParseError). +// This returns a negative length upon an error (see [ParseError]). func ConsumeGroup(num Number, b []byte) (v []byte, n int) { n = ConsumeFieldValue(num, StartGroupType, b) if n < 0 { @@ -495,8 +495,8 @@ func SizeGroup(num Number, n int) int { return n + SizeTag(num) } -// DecodeTag decodes the field Number and wire Type from its unified form. -// The Number is -1 if the decoded field number overflows int32. +// DecodeTag decodes the field [Number] and wire [Type] from its unified form. +// The [Number] is -1 if the decoded field number overflows int32. // Other than overflow, this does not check for field number validity. func DecodeTag(x uint64) (Number, Type) { // NOTE: MessageSet allows for larger field numbers than normal. @@ -506,7 +506,7 @@ func DecodeTag(x uint64) (Number, Type) { return Number(x >> 3), Type(x & 7) } -// EncodeTag encodes the field Number and wire Type into its unified form. +// EncodeTag encodes the field [Number] and wire [Type] into its unified form. func EncodeTag(num Number, typ Type) uint64 { return uint64(num)<<3 | uint64(typ&7) } diff --git a/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go b/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go index db5248e1b..a45625c8d 100644 --- a/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go +++ b/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go @@ -83,7 +83,13 @@ func formatListOpt(vs list, isRoot, allowMulti bool) string { case protoreflect.FileImports: for i := 0; i < vs.Len(); i++ { var rs records - rs.Append(reflect.ValueOf(vs.Get(i)), "Path", "Package", "IsPublic", "IsWeak") + rv := reflect.ValueOf(vs.Get(i)) + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Path"), "Path"}, + {rv.MethodByName("Package"), "Package"}, + {rv.MethodByName("IsPublic"), "IsPublic"}, + {rv.MethodByName("IsWeak"), "IsWeak"}, + }...) ss = append(ss, "{"+rs.Join()+"}") } return start + joinStrings(ss, allowMulti) + end @@ -92,34 +98,26 @@ func formatListOpt(vs list, isRoot, allowMulti bool) string { for i := 0; i < vs.Len(); i++ { m := reflect.ValueOf(vs).MethodByName("Get") v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface() - ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue)) + ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue, nil)) } return start + joinStrings(ss, allowMulti && isEnumValue) + end } } -// descriptorAccessors is a list of accessors to print for each descriptor. -// -// Do not print all accessors since some contain redundant information, -// while others are pointers that we do not want to follow since the descriptor -// is actually a cyclic graph. -// -// Using a list allows us to print the accessors in a sensible order. -var descriptorAccessors = map[reflect.Type][]string{ - reflect.TypeOf((*protoreflect.FileDescriptor)(nil)).Elem(): {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"}, - reflect.TypeOf((*protoreflect.MessageDescriptor)(nil)).Elem(): {"IsMapEntry", "Fields", "Oneofs", "ReservedNames", "ReservedRanges", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"}, - reflect.TypeOf((*protoreflect.FieldDescriptor)(nil)).Elem(): {"Number", "Cardinality", "Kind", "HasJSONName", "JSONName", "HasPresence", "IsExtension", "IsPacked", "IsWeak", "IsList", "IsMap", "MapKey", "MapValue", "HasDefault", "Default", "ContainingOneof", "ContainingMessage", "Message", "Enum"}, - reflect.TypeOf((*protoreflect.OneofDescriptor)(nil)).Elem(): {"Fields"}, // not directly used; must keep in sync with formatDescOpt - reflect.TypeOf((*protoreflect.EnumDescriptor)(nil)).Elem(): {"Values", "ReservedNames", "ReservedRanges"}, - reflect.TypeOf((*protoreflect.EnumValueDescriptor)(nil)).Elem(): {"Number"}, - reflect.TypeOf((*protoreflect.ServiceDescriptor)(nil)).Elem(): {"Methods"}, - reflect.TypeOf((*protoreflect.MethodDescriptor)(nil)).Elem(): {"Input", "Output", "IsStreamingClient", "IsStreamingServer"}, +type methodAndName struct { + method reflect.Value + name string } func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) { - io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')))) + io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')), nil)) } -func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string { + +func InternalFormatDescOptForTesting(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string { + return formatDescOpt(t, isRoot, allowMulti, record) +} + +func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string { rv := reflect.ValueOf(t) rt := rv.MethodByName("ProtoType").Type().In(0) @@ -129,26 +127,60 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string { } _, isFile := t.(protoreflect.FileDescriptor) - rs := records{allowMulti: allowMulti} + rs := records{ + allowMulti: allowMulti, + record: record, + } if t.IsPlaceholder() { if isFile { - rs.Append(rv, "Path", "Package", "IsPlaceholder") + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Path"), "Path"}, + {rv.MethodByName("Package"), "Package"}, + {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"}, + }...) } else { - rs.Append(rv, "FullName", "IsPlaceholder") + rs.Append(rv, []methodAndName{ + {rv.MethodByName("FullName"), "FullName"}, + {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"}, + }...) } } else { switch { case isFile: - rs.Append(rv, "Syntax") + rs.Append(rv, methodAndName{rv.MethodByName("Syntax"), "Syntax"}) case isRoot: - rs.Append(rv, "Syntax", "FullName") + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Syntax"), "Syntax"}, + {rv.MethodByName("FullName"), "FullName"}, + }...) default: - rs.Append(rv, "Name") + rs.Append(rv, methodAndName{rv.MethodByName("Name"), "Name"}) } switch t := t.(type) { case protoreflect.FieldDescriptor: - for _, s := range descriptorAccessors[rt] { - switch s { + accessors := []methodAndName{ + {rv.MethodByName("Number"), "Number"}, + {rv.MethodByName("Cardinality"), "Cardinality"}, + {rv.MethodByName("Kind"), "Kind"}, + {rv.MethodByName("HasJSONName"), "HasJSONName"}, + {rv.MethodByName("JSONName"), "JSONName"}, + {rv.MethodByName("HasPresence"), "HasPresence"}, + {rv.MethodByName("IsExtension"), "IsExtension"}, + {rv.MethodByName("IsPacked"), "IsPacked"}, + {rv.MethodByName("IsWeak"), "IsWeak"}, + {rv.MethodByName("IsList"), "IsList"}, + {rv.MethodByName("IsMap"), "IsMap"}, + {rv.MethodByName("MapKey"), "MapKey"}, + {rv.MethodByName("MapValue"), "MapValue"}, + {rv.MethodByName("HasDefault"), "HasDefault"}, + {rv.MethodByName("Default"), "Default"}, + {rv.MethodByName("ContainingOneof"), "ContainingOneof"}, + {rv.MethodByName("ContainingMessage"), "ContainingMessage"}, + {rv.MethodByName("Message"), "Message"}, + {rv.MethodByName("Enum"), "Enum"}, + } + for _, s := range accessors { + switch s.name { case "MapKey": if k := t.MapKey(); k != nil { rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()}) @@ -157,20 +189,20 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string { if v := t.MapValue(); v != nil { switch v.Kind() { case protoreflect.EnumKind: - rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Enum().FullName())}) + rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Enum().FullName())}) case protoreflect.MessageKind, protoreflect.GroupKind: - rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Message().FullName())}) + rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Message().FullName())}) default: - rs.recs = append(rs.recs, [2]string{"MapValue", v.Kind().String()}) + rs.AppendRecs("MapValue", [2]string{"MapValue", v.Kind().String()}) } } case "ContainingOneof": if od := t.ContainingOneof(); od != nil { - rs.recs = append(rs.recs, [2]string{"Oneof", string(od.Name())}) + rs.AppendRecs("ContainingOneof", [2]string{"Oneof", string(od.Name())}) } case "ContainingMessage": if t.IsExtension() { - rs.recs = append(rs.recs, [2]string{"Extendee", string(t.ContainingMessage().FullName())}) + rs.AppendRecs("ContainingMessage", [2]string{"Extendee", string(t.ContainingMessage().FullName())}) } case "Message": if !t.IsMap() { @@ -187,13 +219,61 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string { ss = append(ss, string(fs.Get(i).Name())) } if len(ss) > 0 { - rs.recs = append(rs.recs, [2]string{"Fields", "[" + joinStrings(ss, false) + "]"}) + rs.AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(ss, false) + "]"}) } - default: - rs.Append(rv, descriptorAccessors[rt]...) + + case protoreflect.FileDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Path"), "Path"}, + {rv.MethodByName("Package"), "Package"}, + {rv.MethodByName("Imports"), "Imports"}, + {rv.MethodByName("Messages"), "Messages"}, + {rv.MethodByName("Enums"), "Enums"}, + {rv.MethodByName("Extensions"), "Extensions"}, + {rv.MethodByName("Services"), "Services"}, + }...) + + case protoreflect.MessageDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("IsMapEntry"), "IsMapEntry"}, + {rv.MethodByName("Fields"), "Fields"}, + {rv.MethodByName("Oneofs"), "Oneofs"}, + {rv.MethodByName("ReservedNames"), "ReservedNames"}, + {rv.MethodByName("ReservedRanges"), "ReservedRanges"}, + {rv.MethodByName("RequiredNumbers"), "RequiredNumbers"}, + {rv.MethodByName("ExtensionRanges"), "ExtensionRanges"}, + {rv.MethodByName("Messages"), "Messages"}, + {rv.MethodByName("Enums"), "Enums"}, + {rv.MethodByName("Extensions"), "Extensions"}, + }...) + + case protoreflect.EnumDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Values"), "Values"}, + {rv.MethodByName("ReservedNames"), "ReservedNames"}, + {rv.MethodByName("ReservedRanges"), "ReservedRanges"}, + }...) + + case protoreflect.EnumValueDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Number"), "Number"}, + }...) + + case protoreflect.ServiceDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Methods"), "Methods"}, + }...) + + case protoreflect.MethodDescriptor: + rs.Append(rv, []methodAndName{ + {rv.MethodByName("Input"), "Input"}, + {rv.MethodByName("Output"), "Output"}, + {rv.MethodByName("IsStreamingClient"), "IsStreamingClient"}, + {rv.MethodByName("IsStreamingServer"), "IsStreamingServer"}, + }...) } - if rv.MethodByName("GoType").IsValid() { - rs.Append(rv, "GoType") + if m := rv.MethodByName("GoType"); m.IsValid() { + rs.Append(rv, methodAndName{m, "GoType"}) } } return start + rs.Join() + end @@ -202,19 +282,34 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string { type records struct { recs [][2]string allowMulti bool + + // record is a function that will be called for every Append() or + // AppendRecs() call, to be used for testing with the + // InternalFormatDescOptForTesting function. + record func(string) } -func (rs *records) Append(v reflect.Value, accessors ...string) { +func (rs *records) AppendRecs(fieldName string, newRecs [2]string) { + if rs.record != nil { + rs.record(fieldName) + } + rs.recs = append(rs.recs, newRecs) +} + +func (rs *records) Append(v reflect.Value, accessors ...methodAndName) { for _, a := range accessors { + if rs.record != nil { + rs.record(a.name) + } var rv reflect.Value - if m := v.MethodByName(a); m.IsValid() { - rv = m.Call(nil)[0] + if a.method.IsValid() { + rv = a.method.Call(nil)[0] } if v.Kind() == reflect.Struct && !rv.IsValid() { - rv = v.FieldByName(a) + rv = v.FieldByName(a.name) } if !rv.IsValid() { - panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a)) + panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name)) } if _, ok := rv.Interface().(protoreflect.Value); ok { rv = rv.MethodByName("Interface").Call(nil)[0] @@ -261,7 +356,7 @@ func (rs *records) Append(v reflect.Value, accessors ...string) { default: s = fmt.Sprint(v) } - rs.recs = append(rs.recs, [2]string{a, s}) + rs.recs = append(rs.recs, [2]string{a.name, s}) } } diff --git a/vendor/google.golang.org/protobuf/internal/editiondefaults/defaults.go b/vendor/google.golang.org/protobuf/internal/editiondefaults/defaults.go new file mode 100644 index 000000000..14656b65a --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/editiondefaults/defaults.go @@ -0,0 +1,12 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package editiondefaults contains the binary representation of the editions +// defaults. +package editiondefaults + +import _ "embed" + +//go:embed editions_defaults.binpb +var Defaults []byte diff --git a/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb b/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb new file mode 100644 index 000000000..18f075687 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb @@ -0,0 +1,4 @@ + +  (0æ +  (0ç +  (0è æ(è \ No newline at end of file diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go index d043a6ebe..d2b3ac031 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go @@ -121,7 +121,7 @@ func (d *Decoder) Read() (Token, error) { case ObjectClose: if len(d.openStack) == 0 || - d.lastToken.kind == comma || + d.lastToken.kind&(Name|comma) != 0 || d.openStack[len(d.openStack)-1] != ObjectOpen { return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) } diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go index fbdf34873..934f2dcb3 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go @@ -41,8 +41,10 @@ type Encoder struct { // // If indent is a non-empty string, it causes every entry for an Array or Object // to be preceded by the indent and trailed by a newline. -func NewEncoder(indent string) (*Encoder, error) { - e := &Encoder{} +func NewEncoder(buf []byte, indent string) (*Encoder, error) { + e := &Encoder{ + out: buf, + } if len(indent) > 0 { if strings.Trim(indent, " \t") != "" { return nil, errors.New("indent may only be composed of space or tab characters") @@ -176,13 +178,13 @@ func appendFloat(out []byte, n float64, bitSize int) []byte { // WriteInt writes out the given signed integer in JSON number value. func (e *Encoder) WriteInt(n int64) { e.prepareNext(scalar) - e.out = append(e.out, strconv.FormatInt(n, 10)...) + e.out = strconv.AppendInt(e.out, n, 10) } // WriteUint writes out the given unsigned integer in JSON number value. func (e *Encoder) WriteUint(n uint64) { e.prepareNext(scalar) - e.out = append(e.out, strconv.FormatUint(n, 10)...) + e.out = strconv.AppendUint(e.out, n, 10) } // StartObject writes out the '{' symbol. diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go index da289ccce..cf7aed77b 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go @@ -53,8 +53,10 @@ type encoderState struct { // If outputASCII is true, strings will be serialized in such a way that // multi-byte UTF-8 sequences are escaped. This property ensures that the // overall output is ASCII (as opposed to UTF-8). -func NewEncoder(indent string, delims [2]byte, outputASCII bool) (*Encoder, error) { - e := &Encoder{} +func NewEncoder(buf []byte, indent string, delims [2]byte, outputASCII bool) (*Encoder, error) { + e := &Encoder{ + encoderState: encoderState{out: buf}, + } if len(indent) > 0 { if strings.Trim(indent, " \t") != "" { return nil, errors.New("indent may only be composed of space and tab characters") @@ -195,13 +197,13 @@ func appendFloat(out []byte, n float64, bitSize int) []byte { // WriteInt writes out the given signed integer value. func (e *Encoder) WriteInt(n int64) { e.prepareNext(scalar) - e.out = append(e.out, strconv.FormatInt(n, 10)...) + e.out = strconv.AppendInt(e.out, n, 10) } // WriteUint writes out the given unsigned integer value. func (e *Encoder) WriteUint(n uint64) { e.prepareNext(scalar) - e.out = append(e.out, strconv.FormatUint(n, 10)...) + e.out = strconv.AppendUint(e.out, n, 10) } // WriteLiteral writes out the given string as a literal value without quotes. diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go index 7c3689bae..8826bcf40 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go @@ -21,11 +21,26 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" ) +// Edition is an Enum for proto2.Edition +type Edition int32 + +// These values align with the value of Enum in descriptor.proto which allows +// direct conversion between the proto enum and this enum. +const ( + EditionUnknown Edition = 0 + EditionProto2 Edition = 998 + EditionProto3 Edition = 999 + Edition2023 Edition = 1000 + EditionUnsupported Edition = 100000 +) + // The types in this file may have a suffix: // • L0: Contains fields common to all descriptors (except File) and // must be initialized up front. // • L1: Contains fields specific to a descriptor and -// must be initialized up front. +// must be initialized up front. If the associated proto uses Editions, the +// Editions features must always be resolved. If not explicitly set, the +// appropriate default must be resolved and set. // • L2: Contains fields that are lazily initialized when constructing // from the raw file descriptor. When constructing as a literal, the L2 // fields must be initialized up front. @@ -44,6 +59,7 @@ type ( } FileL1 struct { Syntax protoreflect.Syntax + Edition Edition // Only used if Syntax == Editions Path string Package protoreflect.FullName @@ -51,12 +67,41 @@ type ( Messages Messages Extensions Extensions Services Services + + EditionFeatures EditionFeatures } FileL2 struct { Options func() protoreflect.ProtoMessage Imports FileImports Locations SourceLocations } + + EditionFeatures struct { + // IsFieldPresence is true if field_presence is EXPLICIT + // https://protobuf.dev/editions/features/#field_presence + IsFieldPresence bool + // IsFieldPresence is true if field_presence is LEGACY_REQUIRED + // https://protobuf.dev/editions/features/#field_presence + IsLegacyRequired bool + // IsOpenEnum is true if enum_type is OPEN + // https://protobuf.dev/editions/features/#enum_type + IsOpenEnum bool + // IsPacked is true if repeated_field_encoding is PACKED + // https://protobuf.dev/editions/features/#repeated_field_encoding + IsPacked bool + // IsUTF8Validated is true if utf_validation is VERIFY + // https://protobuf.dev/editions/features/#utf8_validation + IsUTF8Validated bool + // IsDelimitedEncoded is true if message_encoding is DELIMITED + // https://protobuf.dev/editions/features/#message_encoding + IsDelimitedEncoded bool + // IsJSONCompliant is true if json_format is ALLOW + // https://protobuf.dev/editions/features/#json_format + IsJSONCompliant bool + // GenerateLegacyUnmarshalJSON determines if the plugin generates the + // UnmarshalJSON([]byte) error method for enums. + GenerateLegacyUnmarshalJSON bool + } ) func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd } @@ -117,6 +162,8 @@ type ( } EnumL1 struct { eagerValues bool // controls whether EnumL2.Values is already populated + + EditionFeatures EditionFeatures } EnumL2 struct { Options func() protoreflect.ProtoMessage @@ -178,6 +225,8 @@ type ( Extensions Extensions IsMapEntry bool // promoted from google.protobuf.MessageOptions IsMessageSet bool // promoted from google.protobuf.MessageOptions + + EditionFeatures EditionFeatures } MessageL2 struct { Options func() protoreflect.ProtoMessage @@ -210,6 +259,8 @@ type ( ContainingOneof protoreflect.OneofDescriptor // must be consistent with Message.Oneofs.Fields Enum protoreflect.EnumDescriptor Message protoreflect.MessageDescriptor + + EditionFeatures EditionFeatures } Oneof struct { @@ -219,6 +270,8 @@ type ( OneofL1 struct { Options func() protoreflect.ProtoMessage Fields OneofFields // must be consistent with Message.Fields.ContainingOneof + + EditionFeatures EditionFeatures } ) @@ -268,23 +321,36 @@ func (fd *Field) Options() protoreflect.ProtoMessage { } func (fd *Field) Number() protoreflect.FieldNumber { return fd.L1.Number } func (fd *Field) Cardinality() protoreflect.Cardinality { return fd.L1.Cardinality } -func (fd *Field) Kind() protoreflect.Kind { return fd.L1.Kind } -func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON } -func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) } -func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) } +func (fd *Field) Kind() protoreflect.Kind { + return fd.L1.Kind +} +func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON } +func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) } +func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) } func (fd *Field) HasPresence() bool { - return fd.L1.Cardinality != protoreflect.Repeated && (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 || fd.L1.Message != nil || fd.L1.ContainingOneof != nil) + if fd.L1.Cardinality == protoreflect.Repeated { + return false + } + explicitFieldPresence := fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsFieldPresence + return fd.Syntax() == protoreflect.Proto2 || explicitFieldPresence || fd.L1.Message != nil || fd.L1.ContainingOneof != nil } func (fd *Field) HasOptionalKeyword() bool { return (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional } func (fd *Field) IsPacked() bool { - if !fd.L1.HasPacked && fd.L0.ParentFile.L1.Syntax != protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Repeated { - switch fd.L1.Kind { - case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: - default: - return true - } + if fd.L1.Cardinality != protoreflect.Repeated { + return false + } + switch fd.L1.Kind { + case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: + return false + } + if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions { + return fd.L1.EditionFeatures.IsPacked + } + if fd.L0.ParentFile.L1.Syntax == protoreflect.Proto3 { + // proto3 repeated fields are packed by default. + return !fd.L1.HasPacked || fd.L1.IsPacked } return fd.L1.IsPacked } @@ -333,6 +399,9 @@ func (fd *Field) ProtoType(protoreflect.FieldDescriptor) {} // WARNING: This method is exempt from the compatibility promise and may be // removed in the future without warning. func (fd *Field) EnforceUTF8() bool { + if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions { + return fd.L1.EditionFeatures.IsUTF8Validated + } if fd.L1.HasEnforceUTF8 { return fd.L1.EnforceUTF8 } @@ -359,10 +428,11 @@ type ( L2 *ExtensionL2 // protected by fileDesc.once } ExtensionL1 struct { - Number protoreflect.FieldNumber - Extendee protoreflect.MessageDescriptor - Cardinality protoreflect.Cardinality - Kind protoreflect.Kind + Number protoreflect.FieldNumber + Extendee protoreflect.MessageDescriptor + Cardinality protoreflect.Cardinality + Kind protoreflect.Kind + EditionFeatures EditionFeatures } ExtensionL2 struct { Options func() protoreflect.ProtoMessage diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go index 4a1584c9d..237e64fd2 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go @@ -5,6 +5,7 @@ package filedesc import ( + "fmt" "sync" "google.golang.org/protobuf/encoding/protowire" @@ -98,6 +99,7 @@ func (fd *File) unmarshalSeed(b []byte) { var prevField protoreflect.FieldNumber var numEnums, numMessages, numExtensions, numServices int var posEnums, posMessages, posExtensions, posServices int + var options []byte b0 := b for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) @@ -113,6 +115,8 @@ func (fd *File) unmarshalSeed(b []byte) { fd.L1.Syntax = protoreflect.Proto2 case "proto3": fd.L1.Syntax = protoreflect.Proto3 + case "editions": + fd.L1.Syntax = protoreflect.Editions default: panic("invalid syntax") } @@ -120,6 +124,8 @@ func (fd *File) unmarshalSeed(b []byte) { fd.L1.Path = sb.MakeString(v) case genid.FileDescriptorProto_Package_field_number: fd.L1.Package = protoreflect.FullName(sb.MakeString(v)) + case genid.FileDescriptorProto_Options_field_number: + options = v case genid.FileDescriptorProto_EnumType_field_number: if prevField != genid.FileDescriptorProto_EnumType_field_number { if numEnums > 0 { @@ -154,6 +160,13 @@ func (fd *File) unmarshalSeed(b []byte) { numServices++ } prevField = num + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.FileDescriptorProto_Edition_field_number: + fd.L1.Edition = Edition(v) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] @@ -166,6 +179,15 @@ func (fd *File) unmarshalSeed(b []byte) { fd.L1.Syntax = protoreflect.Proto2 } + if fd.L1.Syntax == protoreflect.Editions { + fd.L1.EditionFeatures = getFeaturesFor(fd.L1.Edition) + } + + // Parse editions features from options if any + if options != nil { + fd.unmarshalSeedOptions(options) + } + // Must allocate all declarations before parsing each descriptor type // to ensure we handled all descriptors in "flattened ordering". if numEnums > 0 { @@ -219,6 +241,28 @@ func (fd *File) unmarshalSeed(b []byte) { } } +func (fd *File) unmarshalSeedOptions(b []byte) { + for b := b; len(b) > 0; { + num, typ, n := protowire.ConsumeTag(b) + b = b[n:] + switch typ { + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.FileOptions_Features_field_number: + if fd.Syntax() != protoreflect.Editions { + panic(fmt.Sprintf("invalid descriptor: using edition features in a proto with syntax %s", fd.Syntax())) + } + fd.L1.EditionFeatures = unmarshalFeatureSet(v, fd.L1.EditionFeatures) + } + default: + m := protowire.ConsumeFieldValue(num, typ, b) + b = b[m:] + } + } +} + func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protoreflect.Descriptor, i int) { ed.L0.ParentFile = pf ed.L0.Parent = pd @@ -275,6 +319,7 @@ func (md *Message) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protor md.L0.ParentFile = pf md.L0.Parent = pd md.L0.Index = i + md.L1.EditionFeatures = featuresFromParentDesc(md.Parent()) var prevField protoreflect.FieldNumber var numEnums, numMessages, numExtensions int @@ -380,6 +425,13 @@ func (md *Message) unmarshalSeedOptions(b []byte) { case genid.MessageOptions_MessageSetWireFormat_field_number: md.L1.IsMessageSet = protowire.DecodeBool(v) } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.MessageOptions_Features_field_number: + md.L1.EditionFeatures = unmarshalFeatureSet(v, md.L1.EditionFeatures) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go index 736a19a75..482a61cc1 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go @@ -414,6 +414,7 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref fd.L0.ParentFile = pf fd.L0.Parent = pd fd.L0.Index = i + fd.L1.EditionFeatures = featuresFromParentDesc(fd.Parent()) var rawTypeName []byte var rawOptions []byte @@ -465,6 +466,12 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref b = b[m:] } } + if fd.Syntax() == protoreflect.Editions && fd.L1.Kind == protoreflect.MessageKind && fd.L1.EditionFeatures.IsDelimitedEncoded { + fd.L1.Kind = protoreflect.GroupKind + } + if fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsLegacyRequired { + fd.L1.Cardinality = protoreflect.Required + } if rawTypeName != nil { name := makeFullName(sb, rawTypeName) switch fd.L1.Kind { @@ -497,6 +504,13 @@ func (fd *Field) unmarshalOptions(b []byte) { fd.L1.HasEnforceUTF8 = true fd.L1.EnforceUTF8 = protowire.DecodeBool(v) } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.FieldOptions_Features_field_number: + fd.L1.EditionFeatures = unmarshalFeatureSet(v, fd.L1.EditionFeatures) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] @@ -534,6 +548,7 @@ func (od *Oneof) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { var rawTypeName []byte var rawOptions []byte + xd.L1.EditionFeatures = featuresFromParentDesc(xd.L1.Extendee) xd.L2 = new(ExtensionL2) for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) @@ -565,6 +580,12 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { b = b[m:] } } + if xd.Syntax() == protoreflect.Editions && xd.L1.Kind == protoreflect.MessageKind && xd.L1.EditionFeatures.IsDelimitedEncoded { + xd.L1.Kind = protoreflect.GroupKind + } + if xd.Syntax() == protoreflect.Editions && xd.L1.EditionFeatures.IsLegacyRequired { + xd.L1.Cardinality = protoreflect.Required + } if rawTypeName != nil { name := makeFullName(sb, rawTypeName) switch xd.L1.Kind { @@ -589,6 +610,13 @@ func (xd *Extension) unmarshalOptions(b []byte) { case genid.FieldOptions_Packed_field_number: xd.L2.IsPacked = protowire.DecodeBool(v) } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.FieldOptions_Features_field_number: + xd.L1.EditionFeatures = unmarshalFeatureSet(v, xd.L1.EditionFeatures) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/editions.go b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go new file mode 100644 index 000000000..0375a49d4 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go @@ -0,0 +1,142 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filedesc + +import ( + "fmt" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/editiondefaults" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var defaultsCache = make(map[Edition]EditionFeatures) + +func init() { + unmarshalEditionDefaults(editiondefaults.Defaults) +} + +func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures { + for len(b) > 0 { + num, _, n := protowire.ConsumeTag(b) + b = b[n:] + switch num { + case genid.GoFeatures_LegacyUnmarshalJsonEnum_field_number: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + parent.GenerateLegacyUnmarshalJSON = protowire.DecodeBool(v) + default: + panic(fmt.Sprintf("unkown field number %d while unmarshalling GoFeatures", num)) + } + } + return parent +} + +func unmarshalFeatureSet(b []byte, parent EditionFeatures) EditionFeatures { + for len(b) > 0 { + num, typ, n := protowire.ConsumeTag(b) + b = b[n:] + switch typ { + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.FeatureSet_FieldPresence_field_number: + parent.IsFieldPresence = v == genid.FeatureSet_EXPLICIT_enum_value || v == genid.FeatureSet_LEGACY_REQUIRED_enum_value + parent.IsLegacyRequired = v == genid.FeatureSet_LEGACY_REQUIRED_enum_value + case genid.FeatureSet_EnumType_field_number: + parent.IsOpenEnum = v == genid.FeatureSet_OPEN_enum_value + case genid.FeatureSet_RepeatedFieldEncoding_field_number: + parent.IsPacked = v == genid.FeatureSet_PACKED_enum_value + case genid.FeatureSet_Utf8Validation_field_number: + parent.IsUTF8Validated = v == genid.FeatureSet_VERIFY_enum_value + case genid.FeatureSet_MessageEncoding_field_number: + parent.IsDelimitedEncoded = v == genid.FeatureSet_DELIMITED_enum_value + case genid.FeatureSet_JsonFormat_field_number: + parent.IsJSONCompliant = v == genid.FeatureSet_ALLOW_enum_value + default: + panic(fmt.Sprintf("unkown field number %d while unmarshalling FeatureSet", num)) + } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.GoFeatures_LegacyUnmarshalJsonEnum_field_number: + parent = unmarshalGoFeature(v, parent) + } + } + } + + return parent +} + +func featuresFromParentDesc(parentDesc protoreflect.Descriptor) EditionFeatures { + var parentFS EditionFeatures + switch p := parentDesc.(type) { + case *File: + parentFS = p.L1.EditionFeatures + case *Message: + parentFS = p.L1.EditionFeatures + default: + panic(fmt.Sprintf("unknown parent type %T", parentDesc)) + } + return parentFS +} + +func unmarshalEditionDefault(b []byte) { + var ed Edition + var fs EditionFeatures + for len(b) > 0 { + num, typ, n := protowire.ConsumeTag(b) + b = b[n:] + switch typ { + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number: + ed = Edition(v) + } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number: + fs = unmarshalFeatureSet(v, fs) + } + } + } + defaultsCache[ed] = fs +} + +func unmarshalEditionDefaults(b []byte) { + for len(b) > 0 { + num, _, n := protowire.ConsumeTag(b) + b = b[n:] + switch num { + case genid.FeatureSetDefaults_Defaults_field_number: + def, m := protowire.ConsumeBytes(b) + b = b[m:] + unmarshalEditionDefault(def) + case genid.FeatureSetDefaults_MinimumEdition_field_number, + genid.FeatureSetDefaults_MaximumEdition_field_number: + // We don't care about the minimum and maximum editions. If the + // edition we are looking for later on is not in the cache we know + // it is outside of the range between minimum and maximum edition. + _, m := protowire.ConsumeVarint(b) + b = b[m:] + default: + panic(fmt.Sprintf("unkown field number %d while unmarshalling EditionDefault", num)) + } + } +} + +func getFeaturesFor(ed Edition) EditionFeatures { + if def, ok := defaultsCache[ed]; ok { + return def + } + panic(fmt.Sprintf("unsupported edition: %v", ed)) +} diff --git a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go index 5c0e8f73f..40272c893 100644 --- a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go +++ b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go @@ -12,6 +12,27 @@ import ( const File_google_protobuf_descriptor_proto = "google/protobuf/descriptor.proto" +// Full and short names for google.protobuf.Edition. +const ( + Edition_enum_fullname = "google.protobuf.Edition" + Edition_enum_name = "Edition" +) + +// Enum values for google.protobuf.Edition. +const ( + Edition_EDITION_UNKNOWN_enum_value = 0 + Edition_EDITION_PROTO2_enum_value = 998 + Edition_EDITION_PROTO3_enum_value = 999 + Edition_EDITION_2023_enum_value = 1000 + Edition_EDITION_2024_enum_value = 1001 + Edition_EDITION_1_TEST_ONLY_enum_value = 1 + Edition_EDITION_2_TEST_ONLY_enum_value = 2 + Edition_EDITION_99997_TEST_ONLY_enum_value = 99997 + Edition_EDITION_99998_TEST_ONLY_enum_value = 99998 + Edition_EDITION_99999_TEST_ONLY_enum_value = 99999 + Edition_EDITION_MAX_enum_value = 2147483647 +) + // Names for google.protobuf.FileDescriptorSet. const ( FileDescriptorSet_message_name protoreflect.Name = "FileDescriptorSet" @@ -81,7 +102,7 @@ const ( FileDescriptorProto_Options_field_number protoreflect.FieldNumber = 8 FileDescriptorProto_SourceCodeInfo_field_number protoreflect.FieldNumber = 9 FileDescriptorProto_Syntax_field_number protoreflect.FieldNumber = 12 - FileDescriptorProto_Edition_field_number protoreflect.FieldNumber = 13 + FileDescriptorProto_Edition_field_number protoreflect.FieldNumber = 14 ) // Names for google.protobuf.DescriptorProto. @@ -183,13 +204,64 @@ const ( // Field names for google.protobuf.ExtensionRangeOptions. const ( ExtensionRangeOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" + ExtensionRangeOptions_Declaration_field_name protoreflect.Name = "declaration" + ExtensionRangeOptions_Features_field_name protoreflect.Name = "features" + ExtensionRangeOptions_Verification_field_name protoreflect.Name = "verification" ExtensionRangeOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.uninterpreted_option" + ExtensionRangeOptions_Declaration_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.declaration" + ExtensionRangeOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.features" + ExtensionRangeOptions_Verification_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.verification" ) // Field numbers for google.protobuf.ExtensionRangeOptions. const ( ExtensionRangeOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 + ExtensionRangeOptions_Declaration_field_number protoreflect.FieldNumber = 2 + ExtensionRangeOptions_Features_field_number protoreflect.FieldNumber = 50 + ExtensionRangeOptions_Verification_field_number protoreflect.FieldNumber = 3 +) + +// Full and short names for google.protobuf.ExtensionRangeOptions.VerificationState. +const ( + ExtensionRangeOptions_VerificationState_enum_fullname = "google.protobuf.ExtensionRangeOptions.VerificationState" + ExtensionRangeOptions_VerificationState_enum_name = "VerificationState" +) + +// Enum values for google.protobuf.ExtensionRangeOptions.VerificationState. +const ( + ExtensionRangeOptions_DECLARATION_enum_value = 0 + ExtensionRangeOptions_UNVERIFIED_enum_value = 1 +) + +// Names for google.protobuf.ExtensionRangeOptions.Declaration. +const ( + ExtensionRangeOptions_Declaration_message_name protoreflect.Name = "Declaration" + ExtensionRangeOptions_Declaration_message_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration" +) + +// Field names for google.protobuf.ExtensionRangeOptions.Declaration. +const ( + ExtensionRangeOptions_Declaration_Number_field_name protoreflect.Name = "number" + ExtensionRangeOptions_Declaration_FullName_field_name protoreflect.Name = "full_name" + ExtensionRangeOptions_Declaration_Type_field_name protoreflect.Name = "type" + ExtensionRangeOptions_Declaration_Reserved_field_name protoreflect.Name = "reserved" + ExtensionRangeOptions_Declaration_Repeated_field_name protoreflect.Name = "repeated" + + ExtensionRangeOptions_Declaration_Number_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.number" + ExtensionRangeOptions_Declaration_FullName_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.full_name" + ExtensionRangeOptions_Declaration_Type_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.type" + ExtensionRangeOptions_Declaration_Reserved_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.reserved" + ExtensionRangeOptions_Declaration_Repeated_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.repeated" +) + +// Field numbers for google.protobuf.ExtensionRangeOptions.Declaration. +const ( + ExtensionRangeOptions_Declaration_Number_field_number protoreflect.FieldNumber = 1 + ExtensionRangeOptions_Declaration_FullName_field_number protoreflect.FieldNumber = 2 + ExtensionRangeOptions_Declaration_Type_field_number protoreflect.FieldNumber = 3 + ExtensionRangeOptions_Declaration_Reserved_field_number protoreflect.FieldNumber = 5 + ExtensionRangeOptions_Declaration_Repeated_field_number protoreflect.FieldNumber = 6 ) // Names for google.protobuf.FieldDescriptorProto. @@ -246,12 +318,41 @@ const ( FieldDescriptorProto_Type_enum_name = "Type" ) +// Enum values for google.protobuf.FieldDescriptorProto.Type. +const ( + FieldDescriptorProto_TYPE_DOUBLE_enum_value = 1 + FieldDescriptorProto_TYPE_FLOAT_enum_value = 2 + FieldDescriptorProto_TYPE_INT64_enum_value = 3 + FieldDescriptorProto_TYPE_UINT64_enum_value = 4 + FieldDescriptorProto_TYPE_INT32_enum_value = 5 + FieldDescriptorProto_TYPE_FIXED64_enum_value = 6 + FieldDescriptorProto_TYPE_FIXED32_enum_value = 7 + FieldDescriptorProto_TYPE_BOOL_enum_value = 8 + FieldDescriptorProto_TYPE_STRING_enum_value = 9 + FieldDescriptorProto_TYPE_GROUP_enum_value = 10 + FieldDescriptorProto_TYPE_MESSAGE_enum_value = 11 + FieldDescriptorProto_TYPE_BYTES_enum_value = 12 + FieldDescriptorProto_TYPE_UINT32_enum_value = 13 + FieldDescriptorProto_TYPE_ENUM_enum_value = 14 + FieldDescriptorProto_TYPE_SFIXED32_enum_value = 15 + FieldDescriptorProto_TYPE_SFIXED64_enum_value = 16 + FieldDescriptorProto_TYPE_SINT32_enum_value = 17 + FieldDescriptorProto_TYPE_SINT64_enum_value = 18 +) + // Full and short names for google.protobuf.FieldDescriptorProto.Label. const ( FieldDescriptorProto_Label_enum_fullname = "google.protobuf.FieldDescriptorProto.Label" FieldDescriptorProto_Label_enum_name = "Label" ) +// Enum values for google.protobuf.FieldDescriptorProto.Label. +const ( + FieldDescriptorProto_LABEL_OPTIONAL_enum_value = 1 + FieldDescriptorProto_LABEL_REPEATED_enum_value = 3 + FieldDescriptorProto_LABEL_REQUIRED_enum_value = 2 +) + // Names for google.protobuf.OneofDescriptorProto. const ( OneofDescriptorProto_message_name protoreflect.Name = "OneofDescriptorProto" @@ -423,7 +524,6 @@ const ( FileOptions_CcGenericServices_field_name protoreflect.Name = "cc_generic_services" FileOptions_JavaGenericServices_field_name protoreflect.Name = "java_generic_services" FileOptions_PyGenericServices_field_name protoreflect.Name = "py_generic_services" - FileOptions_PhpGenericServices_field_name protoreflect.Name = "php_generic_services" FileOptions_Deprecated_field_name protoreflect.Name = "deprecated" FileOptions_CcEnableArenas_field_name protoreflect.Name = "cc_enable_arenas" FileOptions_ObjcClassPrefix_field_name protoreflect.Name = "objc_class_prefix" @@ -433,6 +533,7 @@ const ( FileOptions_PhpNamespace_field_name protoreflect.Name = "php_namespace" FileOptions_PhpMetadataNamespace_field_name protoreflect.Name = "php_metadata_namespace" FileOptions_RubyPackage_field_name protoreflect.Name = "ruby_package" + FileOptions_Features_field_name protoreflect.Name = "features" FileOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" FileOptions_JavaPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_package" @@ -445,7 +546,6 @@ const ( FileOptions_CcGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_generic_services" FileOptions_JavaGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_generic_services" FileOptions_PyGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.py_generic_services" - FileOptions_PhpGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_generic_services" FileOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.deprecated" FileOptions_CcEnableArenas_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_enable_arenas" FileOptions_ObjcClassPrefix_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.objc_class_prefix" @@ -455,6 +555,7 @@ const ( FileOptions_PhpNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_namespace" FileOptions_PhpMetadataNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_metadata_namespace" FileOptions_RubyPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.ruby_package" + FileOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.features" FileOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.uninterpreted_option" ) @@ -470,7 +571,6 @@ const ( FileOptions_CcGenericServices_field_number protoreflect.FieldNumber = 16 FileOptions_JavaGenericServices_field_number protoreflect.FieldNumber = 17 FileOptions_PyGenericServices_field_number protoreflect.FieldNumber = 18 - FileOptions_PhpGenericServices_field_number protoreflect.FieldNumber = 42 FileOptions_Deprecated_field_number protoreflect.FieldNumber = 23 FileOptions_CcEnableArenas_field_number protoreflect.FieldNumber = 31 FileOptions_ObjcClassPrefix_field_number protoreflect.FieldNumber = 36 @@ -480,6 +580,7 @@ const ( FileOptions_PhpNamespace_field_number protoreflect.FieldNumber = 41 FileOptions_PhpMetadataNamespace_field_number protoreflect.FieldNumber = 44 FileOptions_RubyPackage_field_number protoreflect.FieldNumber = 45 + FileOptions_Features_field_number protoreflect.FieldNumber = 50 FileOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -489,6 +590,13 @@ const ( FileOptions_OptimizeMode_enum_name = "OptimizeMode" ) +// Enum values for google.protobuf.FileOptions.OptimizeMode. +const ( + FileOptions_SPEED_enum_value = 1 + FileOptions_CODE_SIZE_enum_value = 2 + FileOptions_LITE_RUNTIME_enum_value = 3 +) + // Names for google.protobuf.MessageOptions. const ( MessageOptions_message_name protoreflect.Name = "MessageOptions" @@ -502,6 +610,7 @@ const ( MessageOptions_Deprecated_field_name protoreflect.Name = "deprecated" MessageOptions_MapEntry_field_name protoreflect.Name = "map_entry" MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts" + MessageOptions_Features_field_name protoreflect.Name = "features" MessageOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" MessageOptions_MessageSetWireFormat_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.message_set_wire_format" @@ -509,6 +618,7 @@ const ( MessageOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated" MessageOptions_MapEntry_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.map_entry" MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated_legacy_json_field_conflicts" + MessageOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.features" MessageOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.uninterpreted_option" ) @@ -519,6 +629,7 @@ const ( MessageOptions_Deprecated_field_number protoreflect.FieldNumber = 3 MessageOptions_MapEntry_field_number protoreflect.FieldNumber = 7 MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 11 + MessageOptions_Features_field_number protoreflect.FieldNumber = 12 MessageOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -539,7 +650,9 @@ const ( FieldOptions_Weak_field_name protoreflect.Name = "weak" FieldOptions_DebugRedact_field_name protoreflect.Name = "debug_redact" FieldOptions_Retention_field_name protoreflect.Name = "retention" - FieldOptions_Target_field_name protoreflect.Name = "target" + FieldOptions_Targets_field_name protoreflect.Name = "targets" + FieldOptions_EditionDefaults_field_name protoreflect.Name = "edition_defaults" + FieldOptions_Features_field_name protoreflect.Name = "features" FieldOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" FieldOptions_Ctype_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.ctype" @@ -551,7 +664,9 @@ const ( FieldOptions_Weak_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.weak" FieldOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.debug_redact" FieldOptions_Retention_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.retention" - FieldOptions_Target_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.target" + FieldOptions_Targets_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.targets" + FieldOptions_EditionDefaults_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.edition_defaults" + FieldOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.features" FieldOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.uninterpreted_option" ) @@ -566,7 +681,9 @@ const ( FieldOptions_Weak_field_number protoreflect.FieldNumber = 10 FieldOptions_DebugRedact_field_number protoreflect.FieldNumber = 16 FieldOptions_Retention_field_number protoreflect.FieldNumber = 17 - FieldOptions_Target_field_number protoreflect.FieldNumber = 18 + FieldOptions_Targets_field_number protoreflect.FieldNumber = 19 + FieldOptions_EditionDefaults_field_number protoreflect.FieldNumber = 20 + FieldOptions_Features_field_number protoreflect.FieldNumber = 21 FieldOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -576,24 +693,80 @@ const ( FieldOptions_CType_enum_name = "CType" ) +// Enum values for google.protobuf.FieldOptions.CType. +const ( + FieldOptions_STRING_enum_value = 0 + FieldOptions_CORD_enum_value = 1 + FieldOptions_STRING_PIECE_enum_value = 2 +) + // Full and short names for google.protobuf.FieldOptions.JSType. const ( FieldOptions_JSType_enum_fullname = "google.protobuf.FieldOptions.JSType" FieldOptions_JSType_enum_name = "JSType" ) +// Enum values for google.protobuf.FieldOptions.JSType. +const ( + FieldOptions_JS_NORMAL_enum_value = 0 + FieldOptions_JS_STRING_enum_value = 1 + FieldOptions_JS_NUMBER_enum_value = 2 +) + // Full and short names for google.protobuf.FieldOptions.OptionRetention. const ( FieldOptions_OptionRetention_enum_fullname = "google.protobuf.FieldOptions.OptionRetention" FieldOptions_OptionRetention_enum_name = "OptionRetention" ) +// Enum values for google.protobuf.FieldOptions.OptionRetention. +const ( + FieldOptions_RETENTION_UNKNOWN_enum_value = 0 + FieldOptions_RETENTION_RUNTIME_enum_value = 1 + FieldOptions_RETENTION_SOURCE_enum_value = 2 +) + // Full and short names for google.protobuf.FieldOptions.OptionTargetType. const ( FieldOptions_OptionTargetType_enum_fullname = "google.protobuf.FieldOptions.OptionTargetType" FieldOptions_OptionTargetType_enum_name = "OptionTargetType" ) +// Enum values for google.protobuf.FieldOptions.OptionTargetType. +const ( + FieldOptions_TARGET_TYPE_UNKNOWN_enum_value = 0 + FieldOptions_TARGET_TYPE_FILE_enum_value = 1 + FieldOptions_TARGET_TYPE_EXTENSION_RANGE_enum_value = 2 + FieldOptions_TARGET_TYPE_MESSAGE_enum_value = 3 + FieldOptions_TARGET_TYPE_FIELD_enum_value = 4 + FieldOptions_TARGET_TYPE_ONEOF_enum_value = 5 + FieldOptions_TARGET_TYPE_ENUM_enum_value = 6 + FieldOptions_TARGET_TYPE_ENUM_ENTRY_enum_value = 7 + FieldOptions_TARGET_TYPE_SERVICE_enum_value = 8 + FieldOptions_TARGET_TYPE_METHOD_enum_value = 9 +) + +// Names for google.protobuf.FieldOptions.EditionDefault. +const ( + FieldOptions_EditionDefault_message_name protoreflect.Name = "EditionDefault" + FieldOptions_EditionDefault_message_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault" +) + +// Field names for google.protobuf.FieldOptions.EditionDefault. +const ( + FieldOptions_EditionDefault_Edition_field_name protoreflect.Name = "edition" + FieldOptions_EditionDefault_Value_field_name protoreflect.Name = "value" + + FieldOptions_EditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault.edition" + FieldOptions_EditionDefault_Value_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault.value" +) + +// Field numbers for google.protobuf.FieldOptions.EditionDefault. +const ( + FieldOptions_EditionDefault_Edition_field_number protoreflect.FieldNumber = 3 + FieldOptions_EditionDefault_Value_field_number protoreflect.FieldNumber = 2 +) + // Names for google.protobuf.OneofOptions. const ( OneofOptions_message_name protoreflect.Name = "OneofOptions" @@ -602,13 +775,16 @@ const ( // Field names for google.protobuf.OneofOptions. const ( + OneofOptions_Features_field_name protoreflect.Name = "features" OneofOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" + OneofOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.OneofOptions.features" OneofOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.OneofOptions.uninterpreted_option" ) // Field numbers for google.protobuf.OneofOptions. const ( + OneofOptions_Features_field_number protoreflect.FieldNumber = 1 OneofOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -623,11 +799,13 @@ const ( EnumOptions_AllowAlias_field_name protoreflect.Name = "allow_alias" EnumOptions_Deprecated_field_name protoreflect.Name = "deprecated" EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts" + EnumOptions_Features_field_name protoreflect.Name = "features" EnumOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" EnumOptions_AllowAlias_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.allow_alias" EnumOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated" EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated_legacy_json_field_conflicts" + EnumOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.features" EnumOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.uninterpreted_option" ) @@ -636,6 +814,7 @@ const ( EnumOptions_AllowAlias_field_number protoreflect.FieldNumber = 2 EnumOptions_Deprecated_field_number protoreflect.FieldNumber = 3 EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 6 + EnumOptions_Features_field_number protoreflect.FieldNumber = 7 EnumOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -648,15 +827,21 @@ const ( // Field names for google.protobuf.EnumValueOptions. const ( EnumValueOptions_Deprecated_field_name protoreflect.Name = "deprecated" + EnumValueOptions_Features_field_name protoreflect.Name = "features" + EnumValueOptions_DebugRedact_field_name protoreflect.Name = "debug_redact" EnumValueOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" EnumValueOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.deprecated" + EnumValueOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.features" + EnumValueOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.debug_redact" EnumValueOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.uninterpreted_option" ) // Field numbers for google.protobuf.EnumValueOptions. const ( EnumValueOptions_Deprecated_field_number protoreflect.FieldNumber = 1 + EnumValueOptions_Features_field_number protoreflect.FieldNumber = 2 + EnumValueOptions_DebugRedact_field_number protoreflect.FieldNumber = 3 EnumValueOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -668,15 +853,18 @@ const ( // Field names for google.protobuf.ServiceOptions. const ( + ServiceOptions_Features_field_name protoreflect.Name = "features" ServiceOptions_Deprecated_field_name protoreflect.Name = "deprecated" ServiceOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" + ServiceOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.features" ServiceOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.deprecated" ServiceOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.uninterpreted_option" ) // Field numbers for google.protobuf.ServiceOptions. const ( + ServiceOptions_Features_field_number protoreflect.FieldNumber = 34 ServiceOptions_Deprecated_field_number protoreflect.FieldNumber = 33 ServiceOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -691,10 +879,12 @@ const ( const ( MethodOptions_Deprecated_field_name protoreflect.Name = "deprecated" MethodOptions_IdempotencyLevel_field_name protoreflect.Name = "idempotency_level" + MethodOptions_Features_field_name protoreflect.Name = "features" MethodOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" MethodOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.deprecated" MethodOptions_IdempotencyLevel_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.idempotency_level" + MethodOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.features" MethodOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.uninterpreted_option" ) @@ -702,6 +892,7 @@ const ( const ( MethodOptions_Deprecated_field_number protoreflect.FieldNumber = 33 MethodOptions_IdempotencyLevel_field_number protoreflect.FieldNumber = 34 + MethodOptions_Features_field_number protoreflect.FieldNumber = 35 MethodOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -711,6 +902,13 @@ const ( MethodOptions_IdempotencyLevel_enum_name = "IdempotencyLevel" ) +// Enum values for google.protobuf.MethodOptions.IdempotencyLevel. +const ( + MethodOptions_IDEMPOTENCY_UNKNOWN_enum_value = 0 + MethodOptions_NO_SIDE_EFFECTS_enum_value = 1 + MethodOptions_IDEMPOTENT_enum_value = 2 +) + // Names for google.protobuf.UninterpretedOption. const ( UninterpretedOption_message_name protoreflect.Name = "UninterpretedOption" @@ -768,6 +966,163 @@ const ( UninterpretedOption_NamePart_IsExtension_field_number protoreflect.FieldNumber = 2 ) +// Names for google.protobuf.FeatureSet. +const ( + FeatureSet_message_name protoreflect.Name = "FeatureSet" + FeatureSet_message_fullname protoreflect.FullName = "google.protobuf.FeatureSet" +) + +// Field names for google.protobuf.FeatureSet. +const ( + FeatureSet_FieldPresence_field_name protoreflect.Name = "field_presence" + FeatureSet_EnumType_field_name protoreflect.Name = "enum_type" + FeatureSet_RepeatedFieldEncoding_field_name protoreflect.Name = "repeated_field_encoding" + FeatureSet_Utf8Validation_field_name protoreflect.Name = "utf8_validation" + FeatureSet_MessageEncoding_field_name protoreflect.Name = "message_encoding" + FeatureSet_JsonFormat_field_name protoreflect.Name = "json_format" + + FeatureSet_FieldPresence_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.field_presence" + FeatureSet_EnumType_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.enum_type" + FeatureSet_RepeatedFieldEncoding_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.repeated_field_encoding" + FeatureSet_Utf8Validation_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.utf8_validation" + FeatureSet_MessageEncoding_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.message_encoding" + FeatureSet_JsonFormat_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.json_format" +) + +// Field numbers for google.protobuf.FeatureSet. +const ( + FeatureSet_FieldPresence_field_number protoreflect.FieldNumber = 1 + FeatureSet_EnumType_field_number protoreflect.FieldNumber = 2 + FeatureSet_RepeatedFieldEncoding_field_number protoreflect.FieldNumber = 3 + FeatureSet_Utf8Validation_field_number protoreflect.FieldNumber = 4 + FeatureSet_MessageEncoding_field_number protoreflect.FieldNumber = 5 + FeatureSet_JsonFormat_field_number protoreflect.FieldNumber = 6 +) + +// Full and short names for google.protobuf.FeatureSet.FieldPresence. +const ( + FeatureSet_FieldPresence_enum_fullname = "google.protobuf.FeatureSet.FieldPresence" + FeatureSet_FieldPresence_enum_name = "FieldPresence" +) + +// Enum values for google.protobuf.FeatureSet.FieldPresence. +const ( + FeatureSet_FIELD_PRESENCE_UNKNOWN_enum_value = 0 + FeatureSet_EXPLICIT_enum_value = 1 + FeatureSet_IMPLICIT_enum_value = 2 + FeatureSet_LEGACY_REQUIRED_enum_value = 3 +) + +// Full and short names for google.protobuf.FeatureSet.EnumType. +const ( + FeatureSet_EnumType_enum_fullname = "google.protobuf.FeatureSet.EnumType" + FeatureSet_EnumType_enum_name = "EnumType" +) + +// Enum values for google.protobuf.FeatureSet.EnumType. +const ( + FeatureSet_ENUM_TYPE_UNKNOWN_enum_value = 0 + FeatureSet_OPEN_enum_value = 1 + FeatureSet_CLOSED_enum_value = 2 +) + +// Full and short names for google.protobuf.FeatureSet.RepeatedFieldEncoding. +const ( + FeatureSet_RepeatedFieldEncoding_enum_fullname = "google.protobuf.FeatureSet.RepeatedFieldEncoding" + FeatureSet_RepeatedFieldEncoding_enum_name = "RepeatedFieldEncoding" +) + +// Enum values for google.protobuf.FeatureSet.RepeatedFieldEncoding. +const ( + FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN_enum_value = 0 + FeatureSet_PACKED_enum_value = 1 + FeatureSet_EXPANDED_enum_value = 2 +) + +// Full and short names for google.protobuf.FeatureSet.Utf8Validation. +const ( + FeatureSet_Utf8Validation_enum_fullname = "google.protobuf.FeatureSet.Utf8Validation" + FeatureSet_Utf8Validation_enum_name = "Utf8Validation" +) + +// Enum values for google.protobuf.FeatureSet.Utf8Validation. +const ( + FeatureSet_UTF8_VALIDATION_UNKNOWN_enum_value = 0 + FeatureSet_VERIFY_enum_value = 2 + FeatureSet_NONE_enum_value = 3 +) + +// Full and short names for google.protobuf.FeatureSet.MessageEncoding. +const ( + FeatureSet_MessageEncoding_enum_fullname = "google.protobuf.FeatureSet.MessageEncoding" + FeatureSet_MessageEncoding_enum_name = "MessageEncoding" +) + +// Enum values for google.protobuf.FeatureSet.MessageEncoding. +const ( + FeatureSet_MESSAGE_ENCODING_UNKNOWN_enum_value = 0 + FeatureSet_LENGTH_PREFIXED_enum_value = 1 + FeatureSet_DELIMITED_enum_value = 2 +) + +// Full and short names for google.protobuf.FeatureSet.JsonFormat. +const ( + FeatureSet_JsonFormat_enum_fullname = "google.protobuf.FeatureSet.JsonFormat" + FeatureSet_JsonFormat_enum_name = "JsonFormat" +) + +// Enum values for google.protobuf.FeatureSet.JsonFormat. +const ( + FeatureSet_JSON_FORMAT_UNKNOWN_enum_value = 0 + FeatureSet_ALLOW_enum_value = 1 + FeatureSet_LEGACY_BEST_EFFORT_enum_value = 2 +) + +// Names for google.protobuf.FeatureSetDefaults. +const ( + FeatureSetDefaults_message_name protoreflect.Name = "FeatureSetDefaults" + FeatureSetDefaults_message_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults" +) + +// Field names for google.protobuf.FeatureSetDefaults. +const ( + FeatureSetDefaults_Defaults_field_name protoreflect.Name = "defaults" + FeatureSetDefaults_MinimumEdition_field_name protoreflect.Name = "minimum_edition" + FeatureSetDefaults_MaximumEdition_field_name protoreflect.Name = "maximum_edition" + + FeatureSetDefaults_Defaults_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.defaults" + FeatureSetDefaults_MinimumEdition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.minimum_edition" + FeatureSetDefaults_MaximumEdition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.maximum_edition" +) + +// Field numbers for google.protobuf.FeatureSetDefaults. +const ( + FeatureSetDefaults_Defaults_field_number protoreflect.FieldNumber = 1 + FeatureSetDefaults_MinimumEdition_field_number protoreflect.FieldNumber = 4 + FeatureSetDefaults_MaximumEdition_field_number protoreflect.FieldNumber = 5 +) + +// Names for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault. +const ( + FeatureSetDefaults_FeatureSetEditionDefault_message_name protoreflect.Name = "FeatureSetEditionDefault" + FeatureSetDefaults_FeatureSetEditionDefault_message_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault" +) + +// Field names for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault. +const ( + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_name protoreflect.Name = "edition" + FeatureSetDefaults_FeatureSetEditionDefault_Features_field_name protoreflect.Name = "features" + + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition" + FeatureSetDefaults_FeatureSetEditionDefault_Features_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.features" +) + +// Field numbers for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault. +const ( + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number protoreflect.FieldNumber = 3 + FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number protoreflect.FieldNumber = 2 +) + // Names for google.protobuf.SourceCodeInfo. const ( SourceCodeInfo_message_name protoreflect.Name = "SourceCodeInfo" @@ -869,3 +1224,10 @@ const ( GeneratedCodeInfo_Annotation_Semantic_enum_fullname = "google.protobuf.GeneratedCodeInfo.Annotation.Semantic" GeneratedCodeInfo_Annotation_Semantic_enum_name = "Semantic" ) + +// Enum values for google.protobuf.GeneratedCodeInfo.Annotation.Semantic. +const ( + GeneratedCodeInfo_Annotation_NONE_enum_value = 0 + GeneratedCodeInfo_Annotation_SET_enum_value = 1 + GeneratedCodeInfo_Annotation_ALIAS_enum_value = 2 +) diff --git a/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go b/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go new file mode 100644 index 000000000..fd9015e8e --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go @@ -0,0 +1,31 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by generate-protos. DO NOT EDIT. + +package genid + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" +) + +const File_reflect_protodesc_proto_go_features_proto = "reflect/protodesc/proto/go_features.proto" + +// Names for google.protobuf.GoFeatures. +const ( + GoFeatures_message_name protoreflect.Name = "GoFeatures" + GoFeatures_message_fullname protoreflect.FullName = "google.protobuf.GoFeatures" +) + +// Field names for google.protobuf.GoFeatures. +const ( + GoFeatures_LegacyUnmarshalJsonEnum_field_name protoreflect.Name = "legacy_unmarshal_json_enum" + + GoFeatures_LegacyUnmarshalJsonEnum_field_fullname protoreflect.FullName = "google.protobuf.GoFeatures.legacy_unmarshal_json_enum" +) + +// Field numbers for google.protobuf.GoFeatures. +const ( + GoFeatures_LegacyUnmarshalJsonEnum_field_number protoreflect.FieldNumber = 1 +) diff --git a/vendor/google.golang.org/protobuf/internal/genid/struct_gen.go b/vendor/google.golang.org/protobuf/internal/genid/struct_gen.go index 1a38944b2..ad6f80c46 100644 --- a/vendor/google.golang.org/protobuf/internal/genid/struct_gen.go +++ b/vendor/google.golang.org/protobuf/internal/genid/struct_gen.go @@ -18,6 +18,11 @@ const ( NullValue_enum_name = "NullValue" ) +// Enum values for google.protobuf.NullValue. +const ( + NullValue_NULL_VALUE_enum_value = 0 +) + // Names for google.protobuf.Struct. const ( Struct_message_name protoreflect.Name = "Struct" diff --git a/vendor/google.golang.org/protobuf/internal/genid/type_gen.go b/vendor/google.golang.org/protobuf/internal/genid/type_gen.go index 3bc710138..49bc73e25 100644 --- a/vendor/google.golang.org/protobuf/internal/genid/type_gen.go +++ b/vendor/google.golang.org/protobuf/internal/genid/type_gen.go @@ -18,6 +18,13 @@ const ( Syntax_enum_name = "Syntax" ) +// Enum values for google.protobuf.Syntax. +const ( + Syntax_SYNTAX_PROTO2_enum_value = 0 + Syntax_SYNTAX_PROTO3_enum_value = 1 + Syntax_SYNTAX_EDITIONS_enum_value = 2 +) + // Names for google.protobuf.Type. const ( Type_message_name protoreflect.Name = "Type" @@ -32,6 +39,7 @@ const ( Type_Options_field_name protoreflect.Name = "options" Type_SourceContext_field_name protoreflect.Name = "source_context" Type_Syntax_field_name protoreflect.Name = "syntax" + Type_Edition_field_name protoreflect.Name = "edition" Type_Name_field_fullname protoreflect.FullName = "google.protobuf.Type.name" Type_Fields_field_fullname protoreflect.FullName = "google.protobuf.Type.fields" @@ -39,6 +47,7 @@ const ( Type_Options_field_fullname protoreflect.FullName = "google.protobuf.Type.options" Type_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Type.source_context" Type_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Type.syntax" + Type_Edition_field_fullname protoreflect.FullName = "google.protobuf.Type.edition" ) // Field numbers for google.protobuf.Type. @@ -49,6 +58,7 @@ const ( Type_Options_field_number protoreflect.FieldNumber = 4 Type_SourceContext_field_number protoreflect.FieldNumber = 5 Type_Syntax_field_number protoreflect.FieldNumber = 6 + Type_Edition_field_number protoreflect.FieldNumber = 7 ) // Names for google.protobuf.Field. @@ -102,12 +112,43 @@ const ( Field_Kind_enum_name = "Kind" ) +// Enum values for google.protobuf.Field.Kind. +const ( + Field_TYPE_UNKNOWN_enum_value = 0 + Field_TYPE_DOUBLE_enum_value = 1 + Field_TYPE_FLOAT_enum_value = 2 + Field_TYPE_INT64_enum_value = 3 + Field_TYPE_UINT64_enum_value = 4 + Field_TYPE_INT32_enum_value = 5 + Field_TYPE_FIXED64_enum_value = 6 + Field_TYPE_FIXED32_enum_value = 7 + Field_TYPE_BOOL_enum_value = 8 + Field_TYPE_STRING_enum_value = 9 + Field_TYPE_GROUP_enum_value = 10 + Field_TYPE_MESSAGE_enum_value = 11 + Field_TYPE_BYTES_enum_value = 12 + Field_TYPE_UINT32_enum_value = 13 + Field_TYPE_ENUM_enum_value = 14 + Field_TYPE_SFIXED32_enum_value = 15 + Field_TYPE_SFIXED64_enum_value = 16 + Field_TYPE_SINT32_enum_value = 17 + Field_TYPE_SINT64_enum_value = 18 +) + // Full and short names for google.protobuf.Field.Cardinality. const ( Field_Cardinality_enum_fullname = "google.protobuf.Field.Cardinality" Field_Cardinality_enum_name = "Cardinality" ) +// Enum values for google.protobuf.Field.Cardinality. +const ( + Field_CARDINALITY_UNKNOWN_enum_value = 0 + Field_CARDINALITY_OPTIONAL_enum_value = 1 + Field_CARDINALITY_REQUIRED_enum_value = 2 + Field_CARDINALITY_REPEATED_enum_value = 3 +) + // Names for google.protobuf.Enum. const ( Enum_message_name protoreflect.Name = "Enum" @@ -121,12 +162,14 @@ const ( Enum_Options_field_name protoreflect.Name = "options" Enum_SourceContext_field_name protoreflect.Name = "source_context" Enum_Syntax_field_name protoreflect.Name = "syntax" + Enum_Edition_field_name protoreflect.Name = "edition" Enum_Name_field_fullname protoreflect.FullName = "google.protobuf.Enum.name" Enum_Enumvalue_field_fullname protoreflect.FullName = "google.protobuf.Enum.enumvalue" Enum_Options_field_fullname protoreflect.FullName = "google.protobuf.Enum.options" Enum_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Enum.source_context" Enum_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Enum.syntax" + Enum_Edition_field_fullname protoreflect.FullName = "google.protobuf.Enum.edition" ) // Field numbers for google.protobuf.Enum. @@ -136,6 +179,7 @@ const ( Enum_Options_field_number protoreflect.FieldNumber = 3 Enum_SourceContext_field_number protoreflect.FieldNumber = 4 Enum_Syntax_field_number protoreflect.FieldNumber = 5 + Enum_Edition_field_number protoreflect.FieldNumber = 6 ) // Names for google.protobuf.EnumValue. diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go b/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go index e74cefdc5..2b8f122c2 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go @@ -21,26 +21,18 @@ type extensionFieldInfo struct { validation validationInfo } -var legacyExtensionFieldInfoCache sync.Map // map[protoreflect.ExtensionType]*extensionFieldInfo - func getExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo { if xi, ok := xt.(*ExtensionInfo); ok { xi.lazyInit() return xi.info } - return legacyLoadExtensionFieldInfo(xt) -} - -// legacyLoadExtensionFieldInfo dynamically loads a *ExtensionInfo for xt. -func legacyLoadExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo { - if xi, ok := legacyExtensionFieldInfoCache.Load(xt); ok { - return xi.(*extensionFieldInfo) - } - e := makeExtensionFieldInfo(xt.TypeDescriptor()) - if e, ok := legacyMessageTypeCache.LoadOrStore(xt, e); ok { - return e.(*extensionFieldInfo) - } - return e + // Ideally we'd cache the resulting *extensionFieldInfo so we don't have to + // recompute this metadata repeatedly. But without support for something like + // weak references, such a cache would pin temporary values (like dynamic + // extension types, constructed for the duration of a user request) to the + // heap forever, causing memory usage of the cache to grow unbounded. + // See discussion in https://github.com/golang/protobuf/issues/1521. + return makeExtensionFieldInfo(xt.TypeDescriptor()) } func makeExtensionFieldInfo(xd protoreflect.ExtensionDescriptor) *extensionFieldInfo { diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_gen.go b/vendor/google.golang.org/protobuf/internal/impl/codec_gen.go index 1a509b63e..f55dc01e3 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_gen.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_gen.go @@ -162,11 +162,20 @@ func appendBoolSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions func consumeBoolSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.BoolSlice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growBoolSlice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -732,11 +741,20 @@ func appendInt32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption func consumeInt32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growInt32Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -1138,11 +1156,20 @@ func appendSint32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio func consumeSint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growInt32Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -1544,11 +1571,20 @@ func appendUint32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio func consumeUint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Uint32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growUint32Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -1950,11 +1986,20 @@ func appendInt64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption func consumeInt64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growInt64Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -2356,11 +2401,20 @@ func appendSint64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio func consumeSint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growInt64Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -2762,11 +2816,20 @@ func appendUint64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio func consumeUint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Uint64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := 0 + for _, v := range b { + if v < 0x80 { + count++ + } + } + if count > 0 { + p.growUint64Slice(count) + } + s := *sp for len(b) > 0 { var v uint64 var n int @@ -3145,11 +3208,15 @@ func appendSfixed32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpt func consumeSfixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed32() + if count > 0 { + p.growInt32Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed32(b) if n < 0 { @@ -3461,11 +3528,15 @@ func appendFixed32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpti func consumeFixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Uint32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed32() + if count > 0 { + p.growUint32Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed32(b) if n < 0 { @@ -3777,11 +3848,15 @@ func appendFloatSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption func consumeFloatSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Float32Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed32() + if count > 0 { + p.growFloat32Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed32(b) if n < 0 { @@ -4093,11 +4168,15 @@ func appendSfixed64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpt func consumeSfixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Int64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed64() + if count > 0 { + p.growInt64Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed64(b) if n < 0 { @@ -4409,11 +4488,15 @@ func appendFixed64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpti func consumeFixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Uint64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed64() + if count > 0 { + p.growUint64Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed64(b) if n < 0 { @@ -4725,11 +4808,15 @@ func appendDoubleSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio func consumeDoubleSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { sp := p.Float64Slice() if wtyp == protowire.BytesType { - s := *sp b, n := protowire.ConsumeBytes(b) if n < 0 { return out, errDecode } + count := len(b) / protowire.SizeFixed64() + if count > 0 { + p.growFloat64Slice(count) + } + s := *sp for len(b) > 0 { v, n := protowire.ConsumeFixed64(b) if n < 0 { diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_tables.go b/vendor/google.golang.org/protobuf/internal/impl/codec_tables.go index 576dcf3aa..13077751e 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_tables.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_tables.go @@ -197,7 +197,7 @@ func fieldCoder(fd protoreflect.FieldDescriptor, ft reflect.Type) (*MessageInfo, return getMessageInfo(ft), makeMessageFieldCoder(fd, ft) case fd.Kind() == protoreflect.GroupKind: return getMessageInfo(ft), makeGroupFieldCoder(fd, ft) - case fd.Syntax() == protoreflect.Proto3 && fd.ContainingOneof() == nil: + case !fd.HasPresence() && fd.ContainingOneof() == nil: // Populated oneof fields always encode even if set to the zero value, // which normally are not encoded in proto3. switch fd.Kind() { diff --git a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go index 61c483fac..2ab2c6297 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go +++ b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go @@ -206,13 +206,18 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName // Obtain a list of oneof wrapper types. var oneofWrappers []reflect.Type - for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} { - if fn, ok := t.MethodByName(method); ok { - for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { - if vs, ok := v.Interface().([]interface{}); ok { - for _, v := range vs { - oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) - } + methods := make([]reflect.Method, 0, 2) + if m, ok := t.MethodByName("XXX_OneofFuncs"); ok { + methods = append(methods, m) + } + if m, ok := t.MethodByName("XXX_OneofWrappers"); ok { + methods = append(methods, m) + } + for _, fn := range methods { + for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { + if vs, ok := v.Interface().([]interface{}); ok { + for _, v := range vs { + oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) } } } diff --git a/vendor/google.golang.org/protobuf/internal/impl/message.go b/vendor/google.golang.org/protobuf/internal/impl/message.go index 4f5fb67a0..629bacdce 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/message.go +++ b/vendor/google.golang.org/protobuf/internal/impl/message.go @@ -192,12 +192,17 @@ fieldLoop: // Derive a mapping of oneof wrappers to fields. oneofWrappers := mi.OneofWrappers - for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} { - if fn, ok := reflect.PtrTo(t).MethodByName(method); ok { - for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { - if vs, ok := v.Interface().([]interface{}); ok { - oneofWrappers = vs - } + methods := make([]reflect.Method, 0, 2) + if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok { + methods = append(methods, m) + } + if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok { + methods = append(methods, m) + } + for _, fn := range methods { + for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { + if vs, ok := v.Interface().([]interface{}); ok { + oneofWrappers = vs } } } diff --git a/vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go b/vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go index 5e736c60e..986322b19 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go +++ b/vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go @@ -538,6 +538,6 @@ func isZero(v reflect.Value) bool { } return true default: - panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()}) + panic(&reflect.ValueError{Method: "reflect.Value.IsZero", Kind: v.Kind()}) } } diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go index 4c491bdf4..517e94434 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go @@ -159,6 +159,42 @@ func (p pointer) SetPointer(v pointer) { p.v.Elem().Set(v.v) } +func growSlice(p pointer, addCap int) { + // TODO: Once we only support Go 1.20 and newer, use reflect.Grow. + in := p.v.Elem() + out := reflect.MakeSlice(in.Type(), in.Len(), in.Len()+addCap) + reflect.Copy(out, in) + p.v.Elem().Set(out) +} + +func (p pointer) growBoolSlice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growInt32Slice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growUint32Slice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growInt64Slice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growUint64Slice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growFloat64Slice(addCap int) { + growSlice(p, addCap) +} + +func (p pointer) growFloat32Slice(addCap int) { + growSlice(p, addCap) +} + func (Export) MessageStateOf(p Pointer) *messageState { panic("not supported") } func (ms *messageState) pointer() pointer { panic("not supported") } func (ms *messageState) messageInfo() *MessageInfo { panic("not supported") } diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go index ee0e0573e..4b020e311 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go @@ -138,6 +138,46 @@ func (p pointer) SetPointer(v pointer) { *(*unsafe.Pointer)(p.p) = (unsafe.Pointer)(v.p) } +func (p pointer) growBoolSlice(addCap int) { + sp := p.BoolSlice() + s := make([]bool, 0, addCap+len(*sp)) + s = s[:len(*sp)] + copy(s, *sp) + *sp = s +} + +func (p pointer) growInt32Slice(addCap int) { + sp := p.Int32Slice() + s := make([]int32, 0, addCap+len(*sp)) + s = s[:len(*sp)] + copy(s, *sp) + *sp = s +} + +func (p pointer) growUint32Slice(addCap int) { + p.growInt32Slice(addCap) +} + +func (p pointer) growFloat32Slice(addCap int) { + p.growInt32Slice(addCap) +} + +func (p pointer) growInt64Slice(addCap int) { + sp := p.Int64Slice() + s := make([]int64, 0, addCap+len(*sp)) + s = s[:len(*sp)] + copy(s, *sp) + *sp = s +} + +func (p pointer) growUint64Slice(addCap int) { + p.growInt64Slice(addCap) +} + +func (p pointer) growFloat64Slice(addCap int) { + p.growInt64Slice(addCap) +} + // Static check that MessageState does not exceed the size of a pointer. const _ = uint(unsafe.Sizeof(unsafe.Pointer(nil)) - unsafe.Sizeof(MessageState{})) diff --git a/vendor/google.golang.org/protobuf/internal/order/order.go b/vendor/google.golang.org/protobuf/internal/order/order.go index 33745ed06..dea522e12 100644 --- a/vendor/google.golang.org/protobuf/internal/order/order.go +++ b/vendor/google.golang.org/protobuf/internal/order/order.go @@ -33,7 +33,7 @@ var ( return !inOneof(ox) && inOneof(oy) } // Fields in disjoint oneof sets are sorted by declaration index. - if ox != nil && oy != nil && ox != oy { + if inOneof(ox) && inOneof(oy) && ox != oy { return ox.Index() < oy.Index() } // Fields sorted by field number. diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings.go b/vendor/google.golang.org/protobuf/internal/strs/strings.go index 0b74e7658..a6e7df244 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings.go @@ -17,7 +17,7 @@ import ( // EnforceUTF8 reports whether to enforce strict UTF-8 validation. func EnforceUTF8(fd protoreflect.FieldDescriptor) bool { - if flags.ProtoLegacy { + if flags.ProtoLegacy || fd.Syntax() == protoreflect.Editions { if fd, ok := fd.(interface{ EnforceUTF8() bool }); ok { return fd.EnforceUTF8() } diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go120.go similarity index 96% rename from vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go rename to vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go120.go index 61a84d341..a008acd09 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go120.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !purego && !appengine -// +build !purego,!appengine +//go:build !purego && !appengine && !go1.21 +// +build !purego,!appengine,!go1.21 package strs diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go121.go b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go121.go new file mode 100644 index 000000000..60166f2ba --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go121.go @@ -0,0 +1,74 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego && !appengine && go1.21 +// +build !purego,!appengine,go1.21 + +package strs + +import ( + "unsafe" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +// UnsafeString returns an unsafe string reference of b. +// The caller must treat the input slice as immutable. +// +// WARNING: Use carefully. The returned result must not leak to the end user +// unless the input slice is provably immutable. +func UnsafeString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} + +// UnsafeBytes returns an unsafe bytes slice reference of s. +// The caller must treat returned slice as immutable. +// +// WARNING: Use carefully. The returned result must not leak to the end user. +func UnsafeBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +// Builder builds a set of strings with shared lifetime. +// This differs from strings.Builder, which is for building a single string. +type Builder struct { + buf []byte +} + +// AppendFullName is equivalent to protoreflect.FullName.Append, +// but optimized for large batches where each name has a shared lifetime. +func (sb *Builder) AppendFullName(prefix protoreflect.FullName, name protoreflect.Name) protoreflect.FullName { + n := len(prefix) + len(".") + len(name) + if len(prefix) == 0 { + n -= len(".") + } + sb.grow(n) + sb.buf = append(sb.buf, prefix...) + sb.buf = append(sb.buf, '.') + sb.buf = append(sb.buf, name...) + return protoreflect.FullName(sb.last(n)) +} + +// MakeString is equivalent to string(b), but optimized for large batches +// with a shared lifetime. +func (sb *Builder) MakeString(b []byte) string { + sb.grow(len(b)) + sb.buf = append(sb.buf, b...) + return sb.last(len(b)) +} + +func (sb *Builder) grow(n int) { + if cap(sb.buf)-len(sb.buf) >= n { + return + } + + // Unlike strings.Builder, we do not need to copy over the contents + // of the old buffer since our builder provides no API for + // retrieving previously created strings. + sb.buf = make([]byte, 0, 2*(cap(sb.buf)+n)) +} + +func (sb *Builder) last(n int) string { + return UnsafeString(sb.buf[len(sb.buf)-n:]) +} diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index f7014cd51..a50fcfb49 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -51,7 +51,7 @@ import ( // 10. Send out the CL for review and submit it. const ( Major = 1 - Minor = 30 + Minor = 33 Patch = 0 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/proto/decode.go b/vendor/google.golang.org/protobuf/proto/decode.go index 48d47946b..e5b03b567 100644 --- a/vendor/google.golang.org/protobuf/proto/decode.go +++ b/vendor/google.golang.org/protobuf/proto/decode.go @@ -69,7 +69,7 @@ func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { // UnmarshalState parses a wire-format message and places the result in m. // // This method permits fine-grained control over the unmarshaler. -// Most users should use Unmarshal instead. +// Most users should use [Unmarshal] instead. func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { if o.RecursionLimit == 0 { o.RecursionLimit = protowire.DefaultRecursionLimit diff --git a/vendor/google.golang.org/protobuf/proto/doc.go b/vendor/google.golang.org/protobuf/proto/doc.go index ec71e717f..80ed16a0c 100644 --- a/vendor/google.golang.org/protobuf/proto/doc.go +++ b/vendor/google.golang.org/protobuf/proto/doc.go @@ -18,27 +18,27 @@ // This package contains functions to convert to and from the wire format, // an efficient binary serialization of protocol buffers. // -// • Size reports the size of a message in the wire format. +// - [Size] reports the size of a message in the wire format. // -// • Marshal converts a message to the wire format. -// The MarshalOptions type provides more control over wire marshaling. +// - [Marshal] converts a message to the wire format. +// The [MarshalOptions] type provides more control over wire marshaling. // -// • Unmarshal converts a message from the wire format. -// The UnmarshalOptions type provides more control over wire unmarshaling. +// - [Unmarshal] converts a message from the wire format. +// The [UnmarshalOptions] type provides more control over wire unmarshaling. // // # Basic message operations // -// • Clone makes a deep copy of a message. +// - [Clone] makes a deep copy of a message. // -// • Merge merges the content of a message into another. +// - [Merge] merges the content of a message into another. // -// • Equal compares two messages. For more control over comparisons -// and detailed reporting of differences, see package -// "google.golang.org/protobuf/testing/protocmp". +// - [Equal] compares two messages. For more control over comparisons +// and detailed reporting of differences, see package +// [google.golang.org/protobuf/testing/protocmp]. // -// • Reset clears the content of a message. +// - [Reset] clears the content of a message. // -// • CheckInitialized reports whether all required fields in a message are set. +// - [CheckInitialized] reports whether all required fields in a message are set. // // # Optional scalar constructors // @@ -46,9 +46,9 @@ // as pointers to a value. For example, an optional string field has the // Go type *string. // -// • Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, and String -// take a value and return a pointer to a new instance of it, -// to simplify construction of optional field values. +// - [Bool], [Int32], [Int64], [Uint32], [Uint64], [Float32], [Float64], and [String] +// take a value and return a pointer to a new instance of it, +// to simplify construction of optional field values. // // Generated enum types usually have an Enum method which performs the // same operation. @@ -57,29 +57,29 @@ // // # Extension accessors // -// • HasExtension, GetExtension, SetExtension, and ClearExtension -// access extension field values in a protocol buffer message. +// - [HasExtension], [GetExtension], [SetExtension], and [ClearExtension] +// access extension field values in a protocol buffer message. // // Extension fields are only supported in proto2. // // # Related packages // -// • Package "google.golang.org/protobuf/encoding/protojson" converts messages to -// and from JSON. +// - Package [google.golang.org/protobuf/encoding/protojson] converts messages to +// and from JSON. // -// • Package "google.golang.org/protobuf/encoding/prototext" converts messages to -// and from the text format. +// - Package [google.golang.org/protobuf/encoding/prototext] converts messages to +// and from the text format. // -// • Package "google.golang.org/protobuf/reflect/protoreflect" provides a -// reflection interface for protocol buffer data types. +// - Package [google.golang.org/protobuf/reflect/protoreflect] provides a +// reflection interface for protocol buffer data types. // -// • Package "google.golang.org/protobuf/testing/protocmp" provides features -// to compare protocol buffer messages with the "github.com/google/go-cmp/cmp" -// package. +// - Package [google.golang.org/protobuf/testing/protocmp] provides features +// to compare protocol buffer messages with the [github.com/google/go-cmp/cmp] +// package. // -// • Package "google.golang.org/protobuf/types/dynamicpb" provides a dynamic -// message type, suitable for working with messages where the protocol buffer -// type is only known at runtime. +// - Package [google.golang.org/protobuf/types/dynamicpb] provides a dynamic +// message type, suitable for working with messages where the protocol buffer +// type is only known at runtime. // // This module contains additional packages for more specialized use cases. // Consult the individual package documentation for details. diff --git a/vendor/google.golang.org/protobuf/proto/encode.go b/vendor/google.golang.org/protobuf/proto/encode.go index bf7f816d0..4fed202f9 100644 --- a/vendor/google.golang.org/protobuf/proto/encode.go +++ b/vendor/google.golang.org/protobuf/proto/encode.go @@ -129,7 +129,7 @@ func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) { // MarshalState returns the wire-format encoding of a message. // // This method permits fine-grained control over the marshaler. -// Most users should use Marshal instead. +// Most users should use [Marshal] instead. func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error) { return o.marshal(in.Buf, in.Message) } diff --git a/vendor/google.golang.org/protobuf/proto/extension.go b/vendor/google.golang.org/protobuf/proto/extension.go index 5f293cda8..17899a3a7 100644 --- a/vendor/google.golang.org/protobuf/proto/extension.go +++ b/vendor/google.golang.org/protobuf/proto/extension.go @@ -26,7 +26,7 @@ func HasExtension(m Message, xt protoreflect.ExtensionType) bool { } // ClearExtension clears an extension field such that subsequent -// HasExtension calls return false. +// [HasExtension] calls return false. // It panics if m is invalid or if xt does not extend m. func ClearExtension(m Message, xt protoreflect.ExtensionType) { m.ProtoReflect().Clear(xt.TypeDescriptor()) diff --git a/vendor/google.golang.org/protobuf/proto/merge.go b/vendor/google.golang.org/protobuf/proto/merge.go index d761ab331..3c6fe5780 100644 --- a/vendor/google.golang.org/protobuf/proto/merge.go +++ b/vendor/google.golang.org/protobuf/proto/merge.go @@ -21,7 +21,7 @@ import ( // The unknown fields of src are appended to the unknown fields of dst. // // It is semantically equivalent to unmarshaling the encoded form of src -// into dst with the UnmarshalOptions.Merge option specified. +// into dst with the [UnmarshalOptions.Merge] option specified. func Merge(dst, src Message) { // TODO: Should nil src be treated as semantically equivalent to a // untyped, read-only, empty message? What about a nil dst? diff --git a/vendor/google.golang.org/protobuf/proto/proto.go b/vendor/google.golang.org/protobuf/proto/proto.go index 1f0d183b1..7543ee6b2 100644 --- a/vendor/google.golang.org/protobuf/proto/proto.go +++ b/vendor/google.golang.org/protobuf/proto/proto.go @@ -15,18 +15,20 @@ import ( // protobuf module that accept a Message, except where otherwise specified. // // This is the v2 interface definition for protobuf messages. -// The v1 interface definition is "github.com/golang/protobuf/proto".Message. +// The v1 interface definition is [github.com/golang/protobuf/proto.Message]. // -// To convert a v1 message to a v2 message, -// use "github.com/golang/protobuf/proto".MessageV2. -// To convert a v2 message to a v1 message, -// use "github.com/golang/protobuf/proto".MessageV1. +// - To convert a v1 message to a v2 message, +// use [google.golang.org/protobuf/protoadapt.MessageV2Of]. +// - To convert a v2 message to a v1 message, +// use [google.golang.org/protobuf/protoadapt.MessageV1Of]. type Message = protoreflect.ProtoMessage -// Error matches all errors produced by packages in the protobuf module. +// Error matches all errors produced by packages in the protobuf module +// according to [errors.Is]. // -// That is, errors.Is(err, Error) reports whether an error is produced -// by this module. +// Example usage: +// +// if errors.Is(err, proto.Error) { ... } var Error error func init() { diff --git a/vendor/google.golang.org/protobuf/proto/size.go b/vendor/google.golang.org/protobuf/proto/size.go index 554b9c6c0..f1692b49b 100644 --- a/vendor/google.golang.org/protobuf/proto/size.go +++ b/vendor/google.golang.org/protobuf/proto/size.go @@ -73,23 +73,27 @@ func (o MarshalOptions) sizeField(fd protoreflect.FieldDescriptor, value protore } func (o MarshalOptions) sizeList(num protowire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) { + sizeTag := protowire.SizeTag(num) + if fd.IsPacked() && list.Len() > 0 { content := 0 for i, llen := 0, list.Len(); i < llen; i++ { content += o.sizeSingular(num, fd.Kind(), list.Get(i)) } - return protowire.SizeTag(num) + protowire.SizeBytes(content) + return sizeTag + protowire.SizeBytes(content) } for i, llen := 0, list.Len(); i < llen; i++ { - size += protowire.SizeTag(num) + o.sizeSingular(num, fd.Kind(), list.Get(i)) + size += sizeTag + o.sizeSingular(num, fd.Kind(), list.Get(i)) } return size } func (o MarshalOptions) sizeMap(num protowire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) { + sizeTag := protowire.SizeTag(num) + mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { - size += protowire.SizeTag(num) + size += sizeTag size += protowire.SizeBytes(o.sizeField(fd.MapKey(), key.Value()) + o.sizeField(fd.MapValue(), value)) return true }) diff --git a/vendor/google.golang.org/protobuf/protoadapt/convert.go b/vendor/google.golang.org/protobuf/protoadapt/convert.go new file mode 100644 index 000000000..ea276d15a --- /dev/null +++ b/vendor/google.golang.org/protobuf/protoadapt/convert.go @@ -0,0 +1,31 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protoadapt bridges the original and new proto APIs. +package protoadapt + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/runtime/protoimpl" +) + +// MessageV1 is the original [github.com/golang/protobuf/proto.Message] type. +type MessageV1 = protoiface.MessageV1 + +// MessageV2 is the [google.golang.org/protobuf/proto.Message] type used by the +// current [google.golang.org/protobuf] module, adding support for reflection. +type MessageV2 = proto.Message + +// MessageV1Of converts a v2 message to a v1 message. +// It returns nil if m is nil. +func MessageV1Of(m MessageV2) MessageV1 { + return protoimpl.X.ProtoMessageV1Of(m) +} + +// MessageV2Of converts a v1 message to a v2 message. +// It returns nil if m is nil. +func MessageV2Of(m MessageV1) MessageV2 { + return protoimpl.X.ProtoMessageV2Of(m) +} diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go index e4dfb1205..baa0cc621 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. // Package protodesc provides functionality for converting -// FileDescriptorProto messages to/from protoreflect.FileDescriptor values. +// FileDescriptorProto messages to/from [protoreflect.FileDescriptor] values. // // The google.protobuf.FileDescriptorProto is a protobuf message that describes // the type information for a .proto file in a form that is easily serializable. -// The protoreflect.FileDescriptor is a more structured representation of +// The [protoreflect.FileDescriptor] is a more structured representation of // the FileDescriptorProto message where references and remote dependencies // can be directly followed. package protodesc @@ -24,11 +24,11 @@ import ( "google.golang.org/protobuf/types/descriptorpb" ) -// Resolver is the resolver used by NewFile to resolve dependencies. +// Resolver is the resolver used by [NewFile] to resolve dependencies. // The enums and messages provided must belong to some parent file, // which is also registered. // -// It is implemented by protoregistry.Files. +// It is implemented by [protoregistry.Files]. type Resolver interface { FindFileByPath(string) (protoreflect.FileDescriptor, error) FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) @@ -61,19 +61,19 @@ type FileOptions struct { AllowUnresolvable bool } -// NewFile creates a new protoreflect.FileDescriptor from the provided -// file descriptor message. See FileOptions.New for more information. +// NewFile creates a new [protoreflect.FileDescriptor] from the provided +// file descriptor message. See [FileOptions.New] for more information. func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) { return FileOptions{}.New(fd, r) } -// NewFiles creates a new protoregistry.Files from the provided -// FileDescriptorSet message. See FileOptions.NewFiles for more information. +// NewFiles creates a new [protoregistry.Files] from the provided +// FileDescriptorSet message. See [FileOptions.NewFiles] for more information. func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) { return FileOptions{}.NewFiles(fd) } -// New creates a new protoreflect.FileDescriptor from the provided +// New creates a new [protoreflect.FileDescriptor] from the provided // file descriptor message. The file must represent a valid proto file according // to protobuf semantics. The returned descriptor is a deep copy of the input. // @@ -93,9 +93,15 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot f.L1.Syntax = protoreflect.Proto2 case "proto3": f.L1.Syntax = protoreflect.Proto3 + case "editions": + f.L1.Syntax = protoreflect.Editions + f.L1.Edition = fromEditionProto(fd.GetEdition()) default: return nil, errors.New("invalid syntax: %q", fd.GetSyntax()) } + if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < SupportedEditionsMinimum || fd.GetEdition() > SupportedEditionsMaximum) { + return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition()) + } f.L1.Path = fd.GetName() if f.L1.Path == "" { return nil, errors.New("file path must be populated") @@ -108,6 +114,9 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot opts = proto.Clone(opts).(*descriptorpb.FileOptions) f.L2.Options = func() protoreflect.ProtoMessage { return opts } } + if f.L1.Syntax == protoreflect.Editions { + initFileDescFromFeatureSet(f, fd.GetOptions().GetFeatures()) + } f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency())) for _, i := range fd.GetPublicDependency() { @@ -231,7 +240,7 @@ func (is importSet) importPublic(imps protoreflect.FileImports) { } } -// NewFiles creates a new protoregistry.Files from the provided +// NewFiles creates a new [protoregistry.Files] from the provided // FileDescriptorSet message. The descriptor set must include only // valid files according to protobuf semantics. The returned descriptors // are a deep copy of the input. diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go index 37efda1af..b3278163c 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go @@ -28,6 +28,7 @@ func (r descsByName) initEnumDeclarations(eds []*descriptorpb.EnumDescriptorProt opts = proto.Clone(opts).(*descriptorpb.EnumOptions) e.L2.Options = func() protoreflect.ProtoMessage { return opts } } + e.L1.EditionFeatures = mergeEditionFeatures(parent, ed.GetOptions().GetFeatures()) for _, s := range ed.GetReservedName() { e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s)) } @@ -68,6 +69,9 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil { return nil, err } + if m.Base.L0.ParentFile.Syntax() == protoreflect.Editions { + m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures()) + } if opts := md.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.MessageOptions) m.L2.Options = func() protoreflect.ProtoMessage { return opts } @@ -114,6 +118,27 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt return ms, nil } +// canBePacked returns whether the field can use packed encoding: +// https://protobuf.dev/programming-guides/encoding/#packed +func canBePacked(fd *descriptorpb.FieldDescriptorProto) bool { + if fd.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + return false // not a repeated field + } + + switch protoreflect.Kind(fd.GetType()) { + case protoreflect.MessageKind, protoreflect.GroupKind: + return false // not a scalar type field + + case protoreflect.StringKind, protoreflect.BytesKind: + // string and bytes can explicitly not be declared as packed, + // see https://protobuf.dev/programming-guides/encoding/#packed + return false + + default: + return true + } +} + func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (fs []filedesc.Field, err error) { fs = make([]filedesc.Field, len(fds)) // allocate up-front to ensure stable pointers for i, fd := range fds { @@ -137,6 +162,34 @@ func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDesc if fd.JsonName != nil { f.L1.StringName.InitJSON(fd.GetJsonName()) } + + if f.Base.L0.ParentFile.Syntax() == protoreflect.Editions { + f.L1.EditionFeatures = mergeEditionFeatures(parent, fd.GetOptions().GetFeatures()) + + if f.L1.EditionFeatures.IsLegacyRequired { + f.L1.Cardinality = protoreflect.Required + } + // We reuse the existing field because the old option `[packed = + // true]` is mutually exclusive with the editions feature. + if canBePacked(fd) { + f.L1.HasPacked = true + f.L1.IsPacked = f.L1.EditionFeatures.IsPacked + } + + // We pretend this option is always explicitly set because the only + // use of HasEnforceUTF8 is to determine whether to use EnforceUTF8 + // or to return the appropriate default. + // When using editions we either parse the option or resolve the + // appropriate default here (instead of later when this option is + // requested from the descriptor). + // In proto2/proto3 syntax HasEnforceUTF8 might be false. + f.L1.HasEnforceUTF8 = true + f.L1.EnforceUTF8 = f.L1.EditionFeatures.IsUTF8Validated + + if f.L1.Kind == protoreflect.MessageKind && f.L1.EditionFeatures.IsDelimitedEncoded { + f.L1.Kind = protoreflect.GroupKind + } + } } return fs, nil } @@ -151,6 +204,9 @@ func (r descsByName) initOneofsFromDescriptorProto(ods []*descriptorpb.OneofDesc if opts := od.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.OneofOptions) o.L1.Options = func() protoreflect.ProtoMessage { return opts } + if parent.Syntax() == protoreflect.Editions { + o.L1.EditionFeatures = mergeEditionFeatures(parent, opts.GetFeatures()) + } } } return os, nil diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go index 27d7e3501..254ca5854 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go @@ -276,8 +276,8 @@ func unmarshalDefault(s string, fd protoreflect.FieldDescriptor, allowUnresolvab } else if err != nil { return v, ev, err } - if fd.Syntax() == protoreflect.Proto3 { - return v, ev, errors.New("cannot be specified under proto3 semantics") + if !fd.HasPresence() { + return v, ev, errors.New("cannot be specified with implicit field presence") } if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind || fd.Cardinality() == protoreflect.Repeated { return v, ev, errors.New("cannot be specified on composite types") diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go index 9af1d5648..e4dcaf876 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go @@ -107,7 +107,7 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc if isMessageSet && !flags.ProtoLegacy { return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName()) } - if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { + if isMessageSet && (m.Syntax() == protoreflect.Proto3 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { return errors.New("message %q is an invalid proto1 MessageSet", m.FullName()) } if m.Syntax() == protoreflect.Proto3 { @@ -314,8 +314,8 @@ func checkValidGroup(fd protoreflect.FieldDescriptor) error { switch { case fd.Kind() != protoreflect.GroupKind: return nil - case fd.Syntax() != protoreflect.Proto2: - return errors.New("invalid under proto2 semantics") + case fd.Syntax() == protoreflect.Proto3: + return errors.New("invalid under proto3 semantics") case md == nil || md.IsPlaceholder(): return errors.New("message must be resolvable") case fd.FullName().Parent() != md.FullName().Parent(): diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go b/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go new file mode 100644 index 000000000..2a6b29d17 --- /dev/null +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go @@ -0,0 +1,148 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protodesc + +import ( + "fmt" + "os" + "sync" + + "google.golang.org/protobuf/internal/editiondefaults" + "google.golang.org/protobuf/internal/filedesc" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + gofeaturespb "google.golang.org/protobuf/types/gofeaturespb" +) + +const ( + SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2 + SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023 +) + +var defaults = &descriptorpb.FeatureSetDefaults{} +var defaultsCacheMu sync.Mutex +var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet) + +func init() { + err := proto.Unmarshal(editiondefaults.Defaults, defaults) + if err != nil { + fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err) + os.Exit(1) + } +} + +func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition { + return filedesc.Edition(epb) +} + +func toEditionProto(ed filedesc.Edition) descriptorpb.Edition { + switch ed { + case filedesc.EditionUnknown: + return descriptorpb.Edition_EDITION_UNKNOWN + case filedesc.EditionProto2: + return descriptorpb.Edition_EDITION_PROTO2 + case filedesc.EditionProto3: + return descriptorpb.Edition_EDITION_PROTO3 + case filedesc.Edition2023: + return descriptorpb.Edition_EDITION_2023 + default: + panic(fmt.Sprintf("unknown value for edition: %v", ed)) + } +} + +func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet { + defaultsCacheMu.Lock() + defer defaultsCacheMu.Unlock() + if def, ok := defaultsCache[ed]; ok { + return def + } + edpb := toEditionProto(ed) + if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb { + // This should never happen protodesc.(FileOptions).New would fail when + // initializing the file descriptor. + // This most likely means the embedded defaults were not updated. + fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb) + os.Exit(1) + } + fs := defaults.GetDefaults()[0].GetFeatures() + // Using a linear search for now. + // Editions are guaranteed to be sorted and thus we could use a binary search. + // Given that there are only a handful of editions (with one more per year) + // there is not much reason to use a binary search. + for _, def := range defaults.GetDefaults() { + if def.GetEdition() <= edpb { + fs = def.GetFeatures() + } else { + break + } + } + defaultsCache[ed] = fs + return fs +} + +// mergeEditionFeatures merges the parent and child feature sets. This function +// should be used when initializing Go descriptors from descriptor protos which +// is why the parent is a filedesc.EditionsFeatures (Go representation) while +// the child is a descriptorproto.FeatureSet (protoc representation). +// Any feature set by the child overwrites what is set by the parent. +func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorpb.FeatureSet) filedesc.EditionFeatures { + var parentFS filedesc.EditionFeatures + switch p := parentDesc.(type) { + case *filedesc.File: + parentFS = p.L1.EditionFeatures + case *filedesc.Message: + parentFS = p.L1.EditionFeatures + default: + panic(fmt.Sprintf("unknown parent type %T", parentDesc)) + } + if child == nil { + return parentFS + } + if fp := child.FieldPresence; fp != nil { + parentFS.IsFieldPresence = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED || + *fp == descriptorpb.FeatureSet_EXPLICIT + parentFS.IsLegacyRequired = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED + } + if et := child.EnumType; et != nil { + parentFS.IsOpenEnum = *et == descriptorpb.FeatureSet_OPEN + } + + if rfe := child.RepeatedFieldEncoding; rfe != nil { + parentFS.IsPacked = *rfe == descriptorpb.FeatureSet_PACKED + } + + if utf8val := child.Utf8Validation; utf8val != nil { + parentFS.IsUTF8Validated = *utf8val == descriptorpb.FeatureSet_VERIFY + } + + if me := child.MessageEncoding; me != nil { + parentFS.IsDelimitedEncoded = *me == descriptorpb.FeatureSet_DELIMITED + } + + if jf := child.JsonFormat; jf != nil { + parentFS.IsJSONCompliant = *jf == descriptorpb.FeatureSet_ALLOW + } + + if goFeatures, ok := proto.GetExtension(child, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures); ok && goFeatures != nil { + if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil { + parentFS.GenerateLegacyUnmarshalJSON = *luje + } + } + + return parentFS +} + +// initFileDescFromFeatureSet initializes editions related fields in fd based +// on fs. If fs is nil it is assumed to be an empty featureset and all fields +// will be initialized with the appropriate default. fd.L1.Edition must be set +// before calling this function. +func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) { + dfs := getFeatureSetFor(fd.L1.Edition) + // initialize the featureset with the defaults + fd.L1.EditionFeatures = mergeEditionFeatures(fd, dfs) + // overwrite any options explicitly specified + fd.L1.EditionFeatures = mergeEditionFeatures(fd, fs) +} diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go b/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go index a7c5ceffc..9d6e05420 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go @@ -16,7 +16,7 @@ import ( "google.golang.org/protobuf/types/descriptorpb" ) -// ToFileDescriptorProto copies a protoreflect.FileDescriptor into a +// ToFileDescriptorProto copies a [protoreflect.FileDescriptor] into a // google.protobuf.FileDescriptorProto message. func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto { p := &descriptorpb.FileDescriptorProto{ @@ -70,13 +70,13 @@ func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileD for i, exts := 0, file.Extensions(); i < exts.Len(); i++ { p.Extension = append(p.Extension, ToFieldDescriptorProto(exts.Get(i))) } - if syntax := file.Syntax(); syntax != protoreflect.Proto2 { + if syntax := file.Syntax(); syntax != protoreflect.Proto2 && syntax.IsValid() { p.Syntax = proto.String(file.Syntax().String()) } return p } -// ToDescriptorProto copies a protoreflect.MessageDescriptor into a +// ToDescriptorProto copies a [protoreflect.MessageDescriptor] into a // google.protobuf.DescriptorProto message. func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto { p := &descriptorpb.DescriptorProto{ @@ -119,7 +119,7 @@ func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.Des return p } -// ToFieldDescriptorProto copies a protoreflect.FieldDescriptor into a +// ToFieldDescriptorProto copies a [protoreflect.FieldDescriptor] into a // google.protobuf.FieldDescriptorProto message. func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto { p := &descriptorpb.FieldDescriptorProto{ @@ -168,7 +168,7 @@ func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.Fi return p } -// ToOneofDescriptorProto copies a protoreflect.OneofDescriptor into a +// ToOneofDescriptorProto copies a [protoreflect.OneofDescriptor] into a // google.protobuf.OneofDescriptorProto message. func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto { return &descriptorpb.OneofDescriptorProto{ @@ -177,7 +177,7 @@ func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.On } } -// ToEnumDescriptorProto copies a protoreflect.EnumDescriptor into a +// ToEnumDescriptorProto copies a [protoreflect.EnumDescriptor] into a // google.protobuf.EnumDescriptorProto message. func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto { p := &descriptorpb.EnumDescriptorProto{ @@ -200,7 +200,7 @@ func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumD return p } -// ToEnumValueDescriptorProto copies a protoreflect.EnumValueDescriptor into a +// ToEnumValueDescriptorProto copies a [protoreflect.EnumValueDescriptor] into a // google.protobuf.EnumValueDescriptorProto message. func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto { return &descriptorpb.EnumValueDescriptorProto{ @@ -210,7 +210,7 @@ func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descrip } } -// ToServiceDescriptorProto copies a protoreflect.ServiceDescriptor into a +// ToServiceDescriptorProto copies a [protoreflect.ServiceDescriptor] into a // google.protobuf.ServiceDescriptorProto message. func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto { p := &descriptorpb.ServiceDescriptorProto{ @@ -223,7 +223,7 @@ func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descripto return p } -// ToMethodDescriptorProto copies a protoreflect.MethodDescriptor into a +// ToMethodDescriptorProto copies a [protoreflect.MethodDescriptor] into a // google.protobuf.MethodDescriptorProto message. func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto { p := &descriptorpb.MethodDescriptorProto{ diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go index 55aa14922..00b01fbd8 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go @@ -10,46 +10,46 @@ // // # Protocol Buffer Descriptors // -// Protobuf descriptors (e.g., EnumDescriptor or MessageDescriptor) +// Protobuf descriptors (e.g., [EnumDescriptor] or [MessageDescriptor]) // are immutable objects that represent protobuf type information. // They are wrappers around the messages declared in descriptor.proto. // Protobuf descriptors alone lack any information regarding Go types. // -// Enums and messages generated by this module implement Enum and ProtoMessage, +// Enums and messages generated by this module implement [Enum] and [ProtoMessage], // where the Descriptor and ProtoReflect.Descriptor accessors respectively // return the protobuf descriptor for the values. // // The protobuf descriptor interfaces are not meant to be implemented by // user code since they might need to be extended in the future to support // additions to the protobuf language. -// The "google.golang.org/protobuf/reflect/protodesc" package converts between +// The [google.golang.org/protobuf/reflect/protodesc] package converts between // google.protobuf.DescriptorProto messages and protobuf descriptors. // // # Go Type Descriptors // -// A type descriptor (e.g., EnumType or MessageType) is a constructor for +// A type descriptor (e.g., [EnumType] or [MessageType]) is a constructor for // a concrete Go type that represents the associated protobuf descriptor. // There is commonly a one-to-one relationship between protobuf descriptors and // Go type descriptors, but it can potentially be a one-to-many relationship. // -// Enums and messages generated by this module implement Enum and ProtoMessage, +// Enums and messages generated by this module implement [Enum] and [ProtoMessage], // where the Type and ProtoReflect.Type accessors respectively // return the protobuf descriptor for the values. // -// The "google.golang.org/protobuf/types/dynamicpb" package can be used to +// The [google.golang.org/protobuf/types/dynamicpb] package can be used to // create Go type descriptors from protobuf descriptors. // // # Value Interfaces // -// The Enum and Message interfaces provide a reflective view over an +// The [Enum] and [Message] interfaces provide a reflective view over an // enum or message instance. For enums, it provides the ability to retrieve // the enum value number for any concrete enum type. For messages, it provides // the ability to access or manipulate fields of the message. // -// To convert a proto.Message to a protoreflect.Message, use the +// To convert a [google.golang.org/protobuf/proto.Message] to a [protoreflect.Message], use the // former's ProtoReflect method. Since the ProtoReflect method is new to the // v2 message interface, it may not be present on older message implementations. -// The "github.com/golang/protobuf/proto".MessageReflect function can be used +// The [github.com/golang/protobuf/proto.MessageReflect] function can be used // to obtain a reflective view on older messages. // // # Relationships @@ -71,12 +71,12 @@ // │ │ // └────────────────── Type() ───────┘ // -// • An EnumType describes a concrete Go enum type. +// • An [EnumType] describes a concrete Go enum type. // It has an EnumDescriptor and can construct an Enum instance. // -// • An EnumDescriptor describes an abstract protobuf enum type. +// • An [EnumDescriptor] describes an abstract protobuf enum type. // -// • An Enum is a concrete enum instance. Generated enums implement Enum. +// • An [Enum] is a concrete enum instance. Generated enums implement Enum. // // ┌──────────────── New() ─────────────────┠// │ │ @@ -90,24 +90,26 @@ // │ │ // └─────────────────── Type() ─────────┘ // -// • A MessageType describes a concrete Go message type. -// It has a MessageDescriptor and can construct a Message instance. -// Just as how Go's reflect.Type is a reflective description of a Go type, -// a MessageType is a reflective description of a Go type for a protobuf message. +// • A [MessageType] describes a concrete Go message type. +// It has a [MessageDescriptor] and can construct a [Message] instance. +// Just as how Go's [reflect.Type] is a reflective description of a Go type, +// a [MessageType] is a reflective description of a Go type for a protobuf message. // -// • A MessageDescriptor describes an abstract protobuf message type. -// It has no understanding of Go types. In order to construct a MessageType -// from just a MessageDescriptor, you can consider looking up the message type -// in the global registry using protoregistry.GlobalTypes.FindMessageByName -// or constructing a dynamic MessageType using dynamicpb.NewMessageType. +// • A [MessageDescriptor] describes an abstract protobuf message type. +// It has no understanding of Go types. In order to construct a [MessageType] +// from just a [MessageDescriptor], you can consider looking up the message type +// in the global registry using the FindMessageByName method on +// [google.golang.org/protobuf/reflect/protoregistry.GlobalTypes] +// or constructing a dynamic [MessageType] using +// [google.golang.org/protobuf/types/dynamicpb.NewMessageType]. // -// • A Message is a reflective view over a concrete message instance. -// Generated messages implement ProtoMessage, which can convert to a Message. -// Just as how Go's reflect.Value is a reflective view over a Go value, -// a Message is a reflective view over a concrete protobuf message instance. -// Using Go reflection as an analogy, the ProtoReflect method is similar to -// calling reflect.ValueOf, and the Message.Interface method is similar to -// calling reflect.Value.Interface. +// • A [Message] is a reflective view over a concrete message instance. +// Generated messages implement [ProtoMessage], which can convert to a [Message]. +// Just as how Go's [reflect.Value] is a reflective view over a Go value, +// a [Message] is a reflective view over a concrete protobuf message instance. +// Using Go reflection as an analogy, the [ProtoMessage.ProtoReflect] method is similar to +// calling [reflect.ValueOf], and the [Message.Interface] method is similar to +// calling [reflect.Value.Interface]. // // ┌── TypeDescriptor() ──┠┌───── Descriptor() ─────┠// │ V │ V @@ -119,15 +121,15 @@ // │ │ // └────── implements ────────┘ // -// • An ExtensionType describes a concrete Go implementation of an extension. -// It has an ExtensionTypeDescriptor and can convert to/from -// abstract Values and Go values. +// • An [ExtensionType] describes a concrete Go implementation of an extension. +// It has an [ExtensionTypeDescriptor] and can convert to/from +// an abstract [Value] and a Go value. // -// • An ExtensionTypeDescriptor is an ExtensionDescriptor -// which also has an ExtensionType. +// • An [ExtensionTypeDescriptor] is an [ExtensionDescriptor] +// which also has an [ExtensionType]. // -// • An ExtensionDescriptor describes an abstract protobuf extension field and -// may not always be an ExtensionTypeDescriptor. +// • An [ExtensionDescriptor] describes an abstract protobuf extension field and +// may not always be an [ExtensionTypeDescriptor]. package protoreflect import ( @@ -142,7 +144,7 @@ type doNotImplement pragma.DoNotImplement // ProtoMessage is the top-level interface that all proto messages implement. // This is declared in the protoreflect package to avoid a cyclic dependency; -// use the proto.Message type instead, which aliases this type. +// use the [google.golang.org/protobuf/proto.Message] type instead, which aliases this type. type ProtoMessage interface{ ProtoReflect() Message } // Syntax is the language version of the proto file. @@ -151,8 +153,9 @@ type Syntax syntax type syntax int8 // keep exact type opaque as the int type may change const ( - Proto2 Syntax = 2 - Proto3 Syntax = 3 + Proto2 Syntax = 2 + Proto3 Syntax = 3 + Editions Syntax = 4 ) // IsValid reports whether the syntax is valid. @@ -172,6 +175,8 @@ func (s Syntax) String() string { return "proto2" case Proto3: return "proto3" + case Editions: + return "editions" default: return fmt.Sprintf("", s) } @@ -436,7 +441,7 @@ type Names interface { // FullName is a qualified name that uniquely identifies a proto declaration. // A qualified name is the concatenation of the proto package along with the // fully-declared name (i.e., name of parent preceding the name of the child), -// with a '.' delimiter placed between each Name. +// with a '.' delimiter placed between each [Name]. // // This should not have any leading or trailing dots. type FullName string // e.g., "google.protobuf.Field.Kind" @@ -480,7 +485,7 @@ func isLetterDigit(c byte) bool { } // Name returns the short name, which is the last identifier segment. -// A single segment FullName is the Name itself. +// A single segment FullName is the [Name] itself. func (n FullName) Name() Name { if i := strings.LastIndexByte(string(n), '.'); i >= 0 { return Name(n[i+1:]) diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go index 54ce326df..7dcc2ff09 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go @@ -35,7 +35,7 @@ func (p *SourcePath) appendFileDescriptorProto(b []byte) []byte { b = p.appendSingularField(b, "source_code_info", (*SourcePath).appendSourceCodeInfo) case 12: b = p.appendSingularField(b, "syntax", nil) - case 13: + case 14: b = p.appendSingularField(b, "edition", nil) } return b @@ -160,8 +160,6 @@ func (p *SourcePath) appendFileOptions(b []byte) []byte { b = p.appendSingularField(b, "java_generic_services", nil) case 18: b = p.appendSingularField(b, "py_generic_services", nil) - case 42: - b = p.appendSingularField(b, "php_generic_services", nil) case 23: b = p.appendSingularField(b, "deprecated", nil) case 31: @@ -180,6 +178,8 @@ func (p *SourcePath) appendFileOptions(b []byte) []byte { b = p.appendSingularField(b, "php_metadata_namespace", nil) case 45: b = p.appendSingularField(b, "ruby_package", nil) + case 50: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -240,6 +240,8 @@ func (p *SourcePath) appendMessageOptions(b []byte) []byte { b = p.appendSingularField(b, "map_entry", nil) case 11: b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil) + case 12: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -285,6 +287,8 @@ func (p *SourcePath) appendEnumOptions(b []byte) []byte { b = p.appendSingularField(b, "deprecated", nil) case 6: b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil) + case 7: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -330,6 +334,8 @@ func (p *SourcePath) appendServiceOptions(b []byte) []byte { return b } switch (*p)[0] { + case 34: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 33: b = p.appendSingularField(b, "deprecated", nil) case 999: @@ -361,14 +367,39 @@ func (p *SourcePath) appendFieldOptions(b []byte) []byte { b = p.appendSingularField(b, "debug_redact", nil) case 17: b = p.appendSingularField(b, "retention", nil) - case 18: - b = p.appendSingularField(b, "target", nil) + case 19: + b = p.appendRepeatedField(b, "targets", nil) + case 20: + b = p.appendRepeatedField(b, "edition_defaults", (*SourcePath).appendFieldOptions_EditionDefault) + case 21: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } return b } +func (p *SourcePath) appendFeatureSet(b []byte) []byte { + if len(*p) == 0 { + return b + } + switch (*p)[0] { + case 1: + b = p.appendSingularField(b, "field_presence", nil) + case 2: + b = p.appendSingularField(b, "enum_type", nil) + case 3: + b = p.appendSingularField(b, "repeated_field_encoding", nil) + case 4: + b = p.appendSingularField(b, "utf8_validation", nil) + case 5: + b = p.appendSingularField(b, "message_encoding", nil) + case 6: + b = p.appendSingularField(b, "json_format", nil) + } + return b +} + func (p *SourcePath) appendUninterpretedOption(b []byte) []byte { if len(*p) == 0 { return b @@ -418,6 +449,12 @@ func (p *SourcePath) appendExtensionRangeOptions(b []byte) []byte { switch (*p)[0] { case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) + case 2: + b = p.appendRepeatedField(b, "declaration", (*SourcePath).appendExtensionRangeOptions_Declaration) + case 50: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) + case 3: + b = p.appendSingularField(b, "verification", nil) } return b } @@ -427,6 +464,8 @@ func (p *SourcePath) appendOneofOptions(b []byte) []byte { return b } switch (*p)[0] { + case 1: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -440,6 +479,10 @@ func (p *SourcePath) appendEnumValueOptions(b []byte) []byte { switch (*p)[0] { case 1: b = p.appendSingularField(b, "deprecated", nil) + case 2: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) + case 3: + b = p.appendSingularField(b, "debug_redact", nil) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -455,12 +498,27 @@ func (p *SourcePath) appendMethodOptions(b []byte) []byte { b = p.appendSingularField(b, "deprecated", nil) case 34: b = p.appendSingularField(b, "idempotency_level", nil) + case 35: + b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } return b } +func (p *SourcePath) appendFieldOptions_EditionDefault(b []byte) []byte { + if len(*p) == 0 { + return b + } + switch (*p)[0] { + case 3: + b = p.appendSingularField(b, "edition", nil) + case 2: + b = p.appendSingularField(b, "value", nil) + } + return b +} + func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte { if len(*p) == 0 { return b @@ -473,3 +531,22 @@ func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte { } return b } + +func (p *SourcePath) appendExtensionRangeOptions_Declaration(b []byte) []byte { + if len(*p) == 0 { + return b + } + switch (*p)[0] { + case 1: + b = p.appendSingularField(b, "number", nil) + case 2: + b = p.appendSingularField(b, "full_name", nil) + case 3: + b = p.appendSingularField(b, "type", nil) + case 5: + b = p.appendSingularField(b, "reserved", nil) + case 6: + b = p.appendSingularField(b, "repeated", nil) + } + return b +} diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go index 3867470d3..60ff62b4c 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go @@ -12,7 +12,7 @@ package protoreflect // exactly identical. However, it is possible for the same semantically // identical proto type to be represented by multiple type descriptors. // -// For example, suppose we have t1 and t2 which are both MessageDescriptors. +// For example, suppose we have t1 and t2 which are both an [MessageDescriptor]. // If t1 == t2, then the types are definitely equal and all accessors return // the same information. However, if t1 != t2, then it is still possible that // they still represent the same proto type (e.g., t1.FullName == t2.FullName). @@ -115,7 +115,7 @@ type Descriptor interface { // corresponds with the google.protobuf.FileDescriptorProto message. // // Top-level declarations: -// EnumDescriptor, MessageDescriptor, FieldDescriptor, and/or ServiceDescriptor. +// [EnumDescriptor], [MessageDescriptor], [FieldDescriptor], and/or [ServiceDescriptor]. type FileDescriptor interface { Descriptor // Descriptor.FullName is identical to Package @@ -180,8 +180,8 @@ type FileImport struct { // corresponds with the google.protobuf.DescriptorProto message. // // Nested declarations: -// FieldDescriptor, OneofDescriptor, FieldDescriptor, EnumDescriptor, -// and/or MessageDescriptor. +// [FieldDescriptor], [OneofDescriptor], [FieldDescriptor], [EnumDescriptor], +// and/or [MessageDescriptor]. type MessageDescriptor interface { Descriptor @@ -214,7 +214,7 @@ type MessageDescriptor interface { ExtensionRanges() FieldRanges // ExtensionRangeOptions returns the ith extension range options. // - // To avoid a dependency cycle, this method returns a proto.Message value, + // To avoid a dependency cycle, this method returns a proto.Message] value, // which always contains a google.protobuf.ExtensionRangeOptions message. // This method returns a typed nil-pointer if no options are present. // The caller must import the descriptorpb package to use this. @@ -231,9 +231,9 @@ type MessageDescriptor interface { } type isMessageDescriptor interface{ ProtoType(MessageDescriptor) } -// MessageType encapsulates a MessageDescriptor with a concrete Go implementation. +// MessageType encapsulates a [MessageDescriptor] with a concrete Go implementation. // It is recommended that implementations of this interface also implement the -// MessageFieldTypes interface. +// [MessageFieldTypes] interface. type MessageType interface { // New returns a newly allocated empty message. // It may return nil for synthetic messages representing a map entry. @@ -249,19 +249,19 @@ type MessageType interface { Descriptor() MessageDescriptor } -// MessageFieldTypes extends a MessageType by providing type information +// MessageFieldTypes extends a [MessageType] by providing type information // regarding enums and messages referenced by the message fields. type MessageFieldTypes interface { MessageType - // Enum returns the EnumType for the ith field in Descriptor.Fields. + // Enum returns the EnumType for the ith field in MessageDescriptor.Fields. // It returns nil if the ith field is not an enum kind. // It panics if out of bounds. // // Invariant: mt.Enum(i).Descriptor() == mt.Descriptor().Fields(i).Enum() Enum(i int) EnumType - // Message returns the MessageType for the ith field in Descriptor.Fields. + // Message returns the MessageType for the ith field in MessageDescriptor.Fields. // It returns nil if the ith field is not a message or group kind. // It panics if out of bounds. // @@ -286,8 +286,8 @@ type MessageDescriptors interface { // corresponds with the google.protobuf.FieldDescriptorProto message. // // It is used for both normal fields defined within the parent message -// (e.g., MessageDescriptor.Fields) and fields that extend some remote message -// (e.g., FileDescriptor.Extensions or MessageDescriptor.Extensions). +// (e.g., [MessageDescriptor.Fields]) and fields that extend some remote message +// (e.g., [FileDescriptor.Extensions] or [MessageDescriptor.Extensions]). type FieldDescriptor interface { Descriptor @@ -344,7 +344,7 @@ type FieldDescriptor interface { // IsMap reports whether this field represents a map, // where the value type for the associated field is a Map. // It is equivalent to checking whether Cardinality is Repeated, - // that the Kind is MessageKind, and that Message.IsMapEntry reports true. + // that the Kind is MessageKind, and that MessageDescriptor.IsMapEntry reports true. IsMap() bool // MapKey returns the field descriptor for the key in the map entry. @@ -419,7 +419,7 @@ type OneofDescriptor interface { // IsSynthetic reports whether this is a synthetic oneof created to support // proto3 optional semantics. If true, Fields contains exactly one field - // with HasOptionalKeyword specified. + // with FieldDescriptor.HasOptionalKeyword specified. IsSynthetic() bool // Fields is a list of fields belonging to this oneof. @@ -442,10 +442,10 @@ type OneofDescriptors interface { doNotImplement } -// ExtensionDescriptor is an alias of FieldDescriptor for documentation. +// ExtensionDescriptor is an alias of [FieldDescriptor] for documentation. type ExtensionDescriptor = FieldDescriptor -// ExtensionTypeDescriptor is an ExtensionDescriptor with an associated ExtensionType. +// ExtensionTypeDescriptor is an [ExtensionDescriptor] with an associated [ExtensionType]. type ExtensionTypeDescriptor interface { ExtensionDescriptor @@ -470,12 +470,12 @@ type ExtensionDescriptors interface { doNotImplement } -// ExtensionType encapsulates an ExtensionDescriptor with a concrete +// ExtensionType encapsulates an [ExtensionDescriptor] with a concrete // Go implementation. The nested field descriptor must be for a extension field. // // While a normal field is a member of the parent message that it is declared -// within (see Descriptor.Parent), an extension field is a member of some other -// target message (see ExtensionDescriptor.Extendee) and may have no +// within (see [Descriptor.Parent]), an extension field is a member of some other +// target message (see [FieldDescriptor.ContainingMessage]) and may have no // relationship with the parent. However, the full name of an extension field is // relative to the parent that it is declared within. // @@ -532,7 +532,7 @@ type ExtensionType interface { // corresponds with the google.protobuf.EnumDescriptorProto message. // // Nested declarations: -// EnumValueDescriptor. +// [EnumValueDescriptor]. type EnumDescriptor interface { Descriptor @@ -548,7 +548,7 @@ type EnumDescriptor interface { } type isEnumDescriptor interface{ ProtoType(EnumDescriptor) } -// EnumType encapsulates an EnumDescriptor with a concrete Go implementation. +// EnumType encapsulates an [EnumDescriptor] with a concrete Go implementation. type EnumType interface { // New returns an instance of this enum type with its value set to n. New(n EnumNumber) Enum @@ -610,7 +610,7 @@ type EnumValueDescriptors interface { // ServiceDescriptor describes a service and // corresponds with the google.protobuf.ServiceDescriptorProto message. // -// Nested declarations: MethodDescriptor. +// Nested declarations: [MethodDescriptor]. type ServiceDescriptor interface { Descriptor diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value.go index 37601b781..a7b0d06ff 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value.go @@ -27,16 +27,16 @@ type Enum interface { // Message is a reflective interface for a concrete message value, // encapsulating both type and value information for the message. // -// Accessor/mutators for individual fields are keyed by FieldDescriptor. +// Accessor/mutators for individual fields are keyed by [FieldDescriptor]. // For non-extension fields, the descriptor must exactly match the // field known by the parent message. -// For extension fields, the descriptor must implement ExtensionTypeDescriptor, -// extend the parent message (i.e., have the same message FullName), and +// For extension fields, the descriptor must implement [ExtensionTypeDescriptor], +// extend the parent message (i.e., have the same message [FullName]), and // be within the parent's extension range. // -// Each field Value can be a scalar or a composite type (Message, List, or Map). -// See Value for the Go types associated with a FieldDescriptor. -// Providing a Value that is invalid or of an incorrect type panics. +// Each field [Value] can be a scalar or a composite type ([Message], [List], or [Map]). +// See [Value] for the Go types associated with a [FieldDescriptor]. +// Providing a [Value] that is invalid or of an incorrect type panics. type Message interface { // Descriptor returns message descriptor, which contains only the protobuf // type information for the message. @@ -152,7 +152,7 @@ type Message interface { // This method may return nil. // // The returned methods type is identical to - // "google.golang.org/protobuf/runtime/protoiface".Methods. + // google.golang.org/protobuf/runtime/protoiface.Methods. // Consult the protoiface package documentation for details. ProtoMethods() *methods } @@ -175,8 +175,8 @@ func (b RawFields) IsValid() bool { } // List is a zero-indexed, ordered list. -// The element Value type is determined by FieldDescriptor.Kind. -// Providing a Value that is invalid or of an incorrect type panics. +// The element [Value] type is determined by [FieldDescriptor.Kind]. +// Providing a [Value] that is invalid or of an incorrect type panics. type List interface { // Len reports the number of entries in the List. // Get, Set, and Truncate panic with out of bound indexes. @@ -226,9 +226,9 @@ type List interface { } // Map is an unordered, associative map. -// The entry MapKey type is determined by FieldDescriptor.MapKey.Kind. -// The entry Value type is determined by FieldDescriptor.MapValue.Kind. -// Providing a MapKey or Value that is invalid or of an incorrect type panics. +// The entry [MapKey] type is determined by [FieldDescriptor.MapKey].Kind. +// The entry [Value] type is determined by [FieldDescriptor.MapValue].Kind. +// Providing a [MapKey] or [Value] that is invalid or of an incorrect type panics. type Map interface { // Len reports the number of elements in the map. Len() int diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go index 591652541..654599d44 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go @@ -24,19 +24,19 @@ import ( // Unlike the == operator, a NaN is equal to another NaN. // // - Enums are equal if they contain the same number. -// Since Value does not contain an enum descriptor, +// Since [Value] does not contain an enum descriptor, // enum values do not consider the type of the enum. // // - Other scalar values are equal if they contain the same value. // -// - Message values are equal if they belong to the same message descriptor, +// - [Message] values are equal if they belong to the same message descriptor, // have the same set of populated known and extension field values, // and the same set of unknown fields values. // -// - Lists are equal if they are the same length and +// - [List] values are equal if they are the same length and // each corresponding element is equal. // -// - Maps are equal if they have the same set of keys and +// - [Map] values are equal if they have the same set of keys and // the corresponding value for each key is equal. func (v1 Value) Equal(v2 Value) bool { return equalValue(v1, v2) diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go index 08e5ef73f..160309731 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go @@ -11,7 +11,7 @@ import ( // Value is a union where only one Go type may be set at a time. // The Value is used to represent all possible values a field may take. -// The following shows which Go type is used to represent each proto Kind: +// The following shows which Go type is used to represent each proto [Kind]: // // â•”â•â•â•â•â•â•â•â•â•â•â•â•â•¤â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•— // â•‘ Go type │ Protobuf kind â•‘ @@ -31,22 +31,22 @@ import ( // // Multiple protobuf Kinds may be represented by a single Go type if the type // can losslessly represent the information for the proto kind. For example, -// Int64Kind, Sint64Kind, and Sfixed64Kind are all represented by int64, +// [Int64Kind], [Sint64Kind], and [Sfixed64Kind] are all represented by int64, // but use different integer encoding methods. // -// The List or Map types are used if the field cardinality is repeated. -// A field is a List if FieldDescriptor.IsList reports true. -// A field is a Map if FieldDescriptor.IsMap reports true. +// The [List] or [Map] types are used if the field cardinality is repeated. +// A field is a [List] if [FieldDescriptor.IsList] reports true. +// A field is a [Map] if [FieldDescriptor.IsMap] reports true. // // Converting to/from a Value and a concrete Go value panics on type mismatch. -// For example, ValueOf("hello").Int() panics because this attempts to +// For example, [ValueOf]("hello").Int() panics because this attempts to // retrieve an int64 from a string. // -// List, Map, and Message Values are called "composite" values. +// [List], [Map], and [Message] Values are called "composite" values. // // A composite Value may alias (reference) memory at some location, // such that changes to the Value updates the that location. -// A composite value acquired with a Mutable method, such as Message.Mutable, +// A composite value acquired with a Mutable method, such as [Message.Mutable], // always references the source object. // // For example: @@ -65,7 +65,7 @@ import ( // // appending to the List here may or may not modify the message. // list.Append(protoreflect.ValueOfInt32(0)) // -// Some operations, such as Message.Get, may return an "empty, read-only" +// Some operations, such as [Message.Get], may return an "empty, read-only" // composite Value. Modifying an empty, read-only value panics. type Value value @@ -306,7 +306,7 @@ func (v Value) Float() float64 { } } -// String returns v as a string. Since this method implements fmt.Stringer, +// String returns v as a string. Since this method implements [fmt.Stringer], // this returns the formatted string value for any non-string type. func (v Value) String() string { switch v.typ { @@ -327,7 +327,7 @@ func (v Value) Bytes() []byte { } } -// Enum returns v as a EnumNumber and panics if the type is not a EnumNumber. +// Enum returns v as a [EnumNumber] and panics if the type is not a [EnumNumber]. func (v Value) Enum() EnumNumber { switch v.typ { case enumType: @@ -337,7 +337,7 @@ func (v Value) Enum() EnumNumber { } } -// Message returns v as a Message and panics if the type is not a Message. +// Message returns v as a [Message] and panics if the type is not a [Message]. func (v Value) Message() Message { switch vi := v.getIface().(type) { case Message: @@ -347,7 +347,7 @@ func (v Value) Message() Message { } } -// List returns v as a List and panics if the type is not a List. +// List returns v as a [List] and panics if the type is not a [List]. func (v Value) List() List { switch vi := v.getIface().(type) { case List: @@ -357,7 +357,7 @@ func (v Value) List() List { } } -// Map returns v as a Map and panics if the type is not a Map. +// Map returns v as a [Map] and panics if the type is not a [Map]. func (v Value) Map() Map { switch vi := v.getIface().(type) { case Map: @@ -367,7 +367,7 @@ func (v Value) Map() Map { } } -// MapKey returns v as a MapKey and panics for invalid MapKey types. +// MapKey returns v as a [MapKey] and panics for invalid [MapKey] types. func (v Value) MapKey() MapKey { switch v.typ { case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType: @@ -378,8 +378,8 @@ func (v Value) MapKey() MapKey { } // MapKey is used to index maps, where the Go type of the MapKey must match -// the specified key Kind (see MessageDescriptor.IsMapEntry). -// The following shows what Go type is used to represent each proto Kind: +// the specified key [Kind] (see [MessageDescriptor.IsMapEntry]). +// The following shows what Go type is used to represent each proto [Kind]: // // â•”â•â•â•â•â•â•â•â•â•â•¤â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•— // â•‘ Go type │ Protobuf kind â•‘ @@ -392,13 +392,13 @@ func (v Value) MapKey() MapKey { // â•‘ string │ StringKind â•‘ // â•šâ•â•â•â•â•â•â•â•â•â•§â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• // -// A MapKey is constructed and accessed through a Value: +// A MapKey is constructed and accessed through a [Value]: // // k := ValueOf("hash").MapKey() // convert string to MapKey // s := k.String() // convert MapKey to string // -// The MapKey is a strict subset of valid types used in Value; -// converting a Value to a MapKey with an invalid type panics. +// The MapKey is a strict subset of valid types used in [Value]; +// converting a [Value] to a MapKey with an invalid type panics. type MapKey value // IsValid reports whether k is populated with a value. @@ -426,13 +426,13 @@ func (k MapKey) Uint() uint64 { return Value(k).Uint() } -// String returns k as a string. Since this method implements fmt.Stringer, +// String returns k as a string. Since this method implements [fmt.Stringer], // this returns the formatted string value for any non-string type. func (k MapKey) String() string { return Value(k).String() } -// Value returns k as a Value. +// Value returns k as a [Value]. func (k MapKey) Value() Value { return Value(k) } diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go similarity index 97% rename from vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go rename to vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go index 702ddf22a..b1fdbe3e8 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !purego && !appengine -// +build !purego,!appengine +//go:build !purego && !appengine && !go1.21 +// +build !purego,!appengine,!go1.21 package protoreflect diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go new file mode 100644 index 000000000..435470111 --- /dev/null +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go @@ -0,0 +1,87 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego && !appengine && go1.21 +// +build !purego,!appengine,go1.21 + +package protoreflect + +import ( + "unsafe" + + "google.golang.org/protobuf/internal/pragma" +) + +type ( + ifaceHeader struct { + _ [0]interface{} // if interfaces have greater alignment than unsafe.Pointer, this will enforce it. + Type unsafe.Pointer + Data unsafe.Pointer + } +) + +var ( + nilType = typeOf(nil) + boolType = typeOf(*new(bool)) + int32Type = typeOf(*new(int32)) + int64Type = typeOf(*new(int64)) + uint32Type = typeOf(*new(uint32)) + uint64Type = typeOf(*new(uint64)) + float32Type = typeOf(*new(float32)) + float64Type = typeOf(*new(float64)) + stringType = typeOf(*new(string)) + bytesType = typeOf(*new([]byte)) + enumType = typeOf(*new(EnumNumber)) +) + +// typeOf returns a pointer to the Go type information. +// The pointer is comparable and equal if and only if the types are identical. +func typeOf(t interface{}) unsafe.Pointer { + return (*ifaceHeader)(unsafe.Pointer(&t)).Type +} + +// value is a union where only one type can be represented at a time. +// The struct is 24B large on 64-bit systems and requires the minimum storage +// necessary to represent each possible type. +// +// The Go GC needs to be able to scan variables containing pointers. +// As such, pointers and non-pointers cannot be intermixed. +type value struct { + pragma.DoNotCompare // 0B + + // typ stores the type of the value as a pointer to the Go type. + typ unsafe.Pointer // 8B + + // ptr stores the data pointer for a String, Bytes, or interface value. + ptr unsafe.Pointer // 8B + + // num stores a Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, or + // Enum value as a raw uint64. + // + // It is also used to store the length of a String or Bytes value; + // the capacity is ignored. + num uint64 // 8B +} + +func valueOfString(v string) Value { + return Value{typ: stringType, ptr: unsafe.Pointer(unsafe.StringData(v)), num: uint64(len(v))} +} +func valueOfBytes(v []byte) Value { + return Value{typ: bytesType, ptr: unsafe.Pointer(unsafe.SliceData(v)), num: uint64(len(v))} +} +func valueOfIface(v interface{}) Value { + p := (*ifaceHeader)(unsafe.Pointer(&v)) + return Value{typ: p.Type, ptr: p.Data} +} + +func (v Value) getString() string { + return unsafe.String((*byte)(v.ptr), v.num) +} +func (v Value) getBytes() []byte { + return unsafe.Slice((*byte)(v.ptr), v.num) +} +func (v Value) getIface() (x interface{}) { + *(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr} + return x +} diff --git a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go index aeb559774..6267dc52a 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go +++ b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go @@ -5,12 +5,12 @@ // Package protoregistry provides data structures to register and lookup // protobuf descriptor types. // -// The Files registry contains file descriptors and provides the ability +// The [Files] registry contains file descriptors and provides the ability // to iterate over the files or lookup a specific descriptor within the files. -// Files only contains protobuf descriptors and has no understanding of Go +// [Files] only contains protobuf descriptors and has no understanding of Go // type information that may be associated with each descriptor. // -// The Types registry contains descriptor types for which there is a known +// The [Types] registry contains descriptor types for which there is a known // Go type associated with that descriptor. It provides the ability to iterate // over the registered types or lookup a type by name. package protoregistry @@ -218,7 +218,7 @@ func (r *Files) checkGenProtoConflict(path string) { // FindDescriptorByName looks up a descriptor by the full name. // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { if r == nil { return nil, NotFound @@ -310,7 +310,7 @@ func (s *nameSuffix) Pop() (name protoreflect.Name) { // FindFileByPath looks up a file by the path. // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. // This returns an error if multiple files have the same path. func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { if r == nil { @@ -431,7 +431,7 @@ func rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflec // A compliant implementation must deterministically return the same type // if no error is encountered. // -// The Types type implements this interface. +// The [Types] type implements this interface. type MessageTypeResolver interface { // FindMessageByName looks up a message by its full name. // E.g., "google.protobuf.Any" @@ -451,7 +451,7 @@ type MessageTypeResolver interface { // A compliant implementation must deterministically return the same type // if no error is encountered. // -// The Types type implements this interface. +// The [Types] type implements this interface. type ExtensionTypeResolver interface { // FindExtensionByName looks up a extension field by the field's full name. // Note that this is the full name of the field as determined by @@ -590,7 +590,7 @@ func (r *Types) register(kind string, desc protoreflect.Descriptor, typ interfac // FindEnumByName looks up an enum by its full name. // E.g., "google.protobuf.Field.Kind". // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) { if r == nil { return nil, NotFound @@ -611,7 +611,7 @@ func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumTyp // FindMessageByName looks up a message by its full name, // e.g. "google.protobuf.Any". // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { if r == nil { return nil, NotFound @@ -632,7 +632,7 @@ func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.M // FindMessageByURL looks up a message by a URL identifier. // See documentation on google.protobuf.Any.type_url for the URL format. // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) { // This function is similar to FindMessageByName but // truncates anything before and including '/' in the URL. @@ -662,7 +662,7 @@ func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) { // where the extension is declared and is unrelated to the full name of the // message being extended. // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { if r == nil { return nil, NotFound @@ -703,7 +703,7 @@ func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.E // FindExtensionByNumber looks up a extension field by the field number // within some parent message, identified by full name. // -// This returns (nil, NotFound) if not found. +// This returns (nil, [NotFound]) if not found. func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { if r == nil { return nil, NotFound diff --git a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index dac5671db..78624cf60 100644 --- a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -48,6 +48,161 @@ import ( sync "sync" ) +// The full set of known editions. +type Edition int32 + +const ( + // A placeholder for an unknown edition value. + Edition_EDITION_UNKNOWN Edition = 0 + // Legacy syntax "editions". These pre-date editions, but behave much like + // distinct editions. These can't be used to specify the edition of proto + // files, but feature definitions must supply proto2/proto3 defaults for + // backwards compatibility. + Edition_EDITION_PROTO2 Edition = 998 + Edition_EDITION_PROTO3 Edition = 999 + // Editions that have been released. The specific values are arbitrary and + // should not be depended on, but they will always be time-ordered for easy + // comparison. + Edition_EDITION_2023 Edition = 1000 + Edition_EDITION_2024 Edition = 1001 + // Placeholder editions for testing feature resolution. These should not be + // used or relyed on outside of tests. + Edition_EDITION_1_TEST_ONLY Edition = 1 + Edition_EDITION_2_TEST_ONLY Edition = 2 + Edition_EDITION_99997_TEST_ONLY Edition = 99997 + Edition_EDITION_99998_TEST_ONLY Edition = 99998 + Edition_EDITION_99999_TEST_ONLY Edition = 99999 + // Placeholder for specifying unbounded edition support. This should only + // ever be used by plugins that can expect to never require any changes to + // support a new edition. + Edition_EDITION_MAX Edition = 2147483647 +) + +// Enum value maps for Edition. +var ( + Edition_name = map[int32]string{ + 0: "EDITION_UNKNOWN", + 998: "EDITION_PROTO2", + 999: "EDITION_PROTO3", + 1000: "EDITION_2023", + 1001: "EDITION_2024", + 1: "EDITION_1_TEST_ONLY", + 2: "EDITION_2_TEST_ONLY", + 99997: "EDITION_99997_TEST_ONLY", + 99998: "EDITION_99998_TEST_ONLY", + 99999: "EDITION_99999_TEST_ONLY", + 2147483647: "EDITION_MAX", + } + Edition_value = map[string]int32{ + "EDITION_UNKNOWN": 0, + "EDITION_PROTO2": 998, + "EDITION_PROTO3": 999, + "EDITION_2023": 1000, + "EDITION_2024": 1001, + "EDITION_1_TEST_ONLY": 1, + "EDITION_2_TEST_ONLY": 2, + "EDITION_99997_TEST_ONLY": 99997, + "EDITION_99998_TEST_ONLY": 99998, + "EDITION_99999_TEST_ONLY": 99999, + "EDITION_MAX": 2147483647, + } +) + +func (x Edition) Enum() *Edition { + p := new(Edition) + *p = x + return p +} + +func (x Edition) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Edition) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[0].Descriptor() +} + +func (Edition) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[0] +} + +func (x Edition) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *Edition) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = Edition(num) + return nil +} + +// Deprecated: Use Edition.Descriptor instead. +func (Edition) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{0} +} + +// The verification state of the extension range. +type ExtensionRangeOptions_VerificationState int32 + +const ( + // All the extensions of the range must be declared. + ExtensionRangeOptions_DECLARATION ExtensionRangeOptions_VerificationState = 0 + ExtensionRangeOptions_UNVERIFIED ExtensionRangeOptions_VerificationState = 1 +) + +// Enum value maps for ExtensionRangeOptions_VerificationState. +var ( + ExtensionRangeOptions_VerificationState_name = map[int32]string{ + 0: "DECLARATION", + 1: "UNVERIFIED", + } + ExtensionRangeOptions_VerificationState_value = map[string]int32{ + "DECLARATION": 0, + "UNVERIFIED": 1, + } +) + +func (x ExtensionRangeOptions_VerificationState) Enum() *ExtensionRangeOptions_VerificationState { + p := new(ExtensionRangeOptions_VerificationState) + *p = x + return p +} + +func (x ExtensionRangeOptions_VerificationState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ExtensionRangeOptions_VerificationState) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[1].Descriptor() +} + +func (ExtensionRangeOptions_VerificationState) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[1] +} + +func (x ExtensionRangeOptions_VerificationState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *ExtensionRangeOptions_VerificationState) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = ExtensionRangeOptions_VerificationState(num) + return nil +} + +// Deprecated: Use ExtensionRangeOptions_VerificationState.Descriptor instead. +func (ExtensionRangeOptions_VerificationState) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{3, 0} +} + type FieldDescriptorProto_Type int32 const ( @@ -67,9 +222,10 @@ const ( FieldDescriptorProto_TYPE_BOOL FieldDescriptorProto_Type = 8 FieldDescriptorProto_TYPE_STRING FieldDescriptorProto_Type = 9 // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 + // Group type is deprecated and not supported after google.protobuf. However, Proto3 // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. + // treat group fields as unknown fields. In Editions, the group wire format + // can be enabled via the `message_encoding` feature. FieldDescriptorProto_TYPE_GROUP FieldDescriptorProto_Type = 10 FieldDescriptorProto_TYPE_MESSAGE FieldDescriptorProto_Type = 11 // Length-delimited aggregate. // New in version 2. @@ -137,11 +293,11 @@ func (x FieldDescriptorProto_Type) String() string { } func (FieldDescriptorProto_Type) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[0].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[2].Descriptor() } func (FieldDescriptorProto_Type) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[0] + return &file_google_protobuf_descriptor_proto_enumTypes[2] } func (x FieldDescriptorProto_Type) Number() protoreflect.EnumNumber { @@ -168,21 +324,24 @@ type FieldDescriptorProto_Label int32 const ( // 0 is reserved for errors FieldDescriptorProto_LABEL_OPTIONAL FieldDescriptorProto_Label = 1 - FieldDescriptorProto_LABEL_REQUIRED FieldDescriptorProto_Label = 2 FieldDescriptorProto_LABEL_REPEATED FieldDescriptorProto_Label = 3 + // The required label is only allowed in google.protobuf. In proto3 and Editions + // it's explicitly prohibited. In Editions, the `field_presence` feature + // can be used to get this behavior. + FieldDescriptorProto_LABEL_REQUIRED FieldDescriptorProto_Label = 2 ) // Enum value maps for FieldDescriptorProto_Label. var ( FieldDescriptorProto_Label_name = map[int32]string{ 1: "LABEL_OPTIONAL", - 2: "LABEL_REQUIRED", 3: "LABEL_REPEATED", + 2: "LABEL_REQUIRED", } FieldDescriptorProto_Label_value = map[string]int32{ "LABEL_OPTIONAL": 1, - "LABEL_REQUIRED": 2, "LABEL_REPEATED": 3, + "LABEL_REQUIRED": 2, } ) @@ -197,11 +356,11 @@ func (x FieldDescriptorProto_Label) String() string { } func (FieldDescriptorProto_Label) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[1].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[3].Descriptor() } func (FieldDescriptorProto_Label) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[1] + return &file_google_protobuf_descriptor_proto_enumTypes[3] } func (x FieldDescriptorProto_Label) Number() protoreflect.EnumNumber { @@ -258,11 +417,11 @@ func (x FileOptions_OptimizeMode) String() string { } func (FileOptions_OptimizeMode) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[2].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[4].Descriptor() } func (FileOptions_OptimizeMode) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[2] + return &file_google_protobuf_descriptor_proto_enumTypes[4] } func (x FileOptions_OptimizeMode) Number() protoreflect.EnumNumber { @@ -288,7 +447,13 @@ type FieldOptions_CType int32 const ( // Default mode. - FieldOptions_STRING FieldOptions_CType = 0 + FieldOptions_STRING FieldOptions_CType = 0 + // The option [ctype=CORD] may be applied to a non-repeated field of type + // "bytes". It indicates that in C++, the data should be stored in a Cord + // instead of a string. For very large strings, this may reduce memory + // fragmentation. It may also allow better performance when parsing from a + // Cord, or when parsing with aliasing enabled, as the parsed Cord may then + // alias the original buffer. FieldOptions_CORD FieldOptions_CType = 1 FieldOptions_STRING_PIECE FieldOptions_CType = 2 ) @@ -318,11 +483,11 @@ func (x FieldOptions_CType) String() string { } func (FieldOptions_CType) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[3].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[5].Descriptor() } func (FieldOptions_CType) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[3] + return &file_google_protobuf_descriptor_proto_enumTypes[5] } func (x FieldOptions_CType) Number() protoreflect.EnumNumber { @@ -380,11 +545,11 @@ func (x FieldOptions_JSType) String() string { } func (FieldOptions_JSType) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[4].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[6].Descriptor() } func (FieldOptions_JSType) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[4] + return &file_google_protobuf_descriptor_proto_enumTypes[6] } func (x FieldOptions_JSType) Number() protoreflect.EnumNumber { @@ -442,11 +607,11 @@ func (x FieldOptions_OptionRetention) String() string { } func (FieldOptions_OptionRetention) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[5].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[7].Descriptor() } func (FieldOptions_OptionRetention) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[5] + return &file_google_protobuf_descriptor_proto_enumTypes[7] } func (x FieldOptions_OptionRetention) Number() protoreflect.EnumNumber { @@ -526,11 +691,11 @@ func (x FieldOptions_OptionTargetType) String() string { } func (FieldOptions_OptionTargetType) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[6].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[8].Descriptor() } func (FieldOptions_OptionTargetType) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[6] + return &file_google_protobuf_descriptor_proto_enumTypes[8] } func (x FieldOptions_OptionTargetType) Number() protoreflect.EnumNumber { @@ -587,31 +752,388 @@ func (x MethodOptions_IdempotencyLevel) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (MethodOptions_IdempotencyLevel) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[7].Descriptor() +func (MethodOptions_IdempotencyLevel) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[9].Descriptor() +} + +func (MethodOptions_IdempotencyLevel) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[9] +} + +func (x MethodOptions_IdempotencyLevel) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = MethodOptions_IdempotencyLevel(num) + return nil +} + +// Deprecated: Use MethodOptions_IdempotencyLevel.Descriptor instead. +func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{17, 0} +} + +type FeatureSet_FieldPresence int32 + +const ( + FeatureSet_FIELD_PRESENCE_UNKNOWN FeatureSet_FieldPresence = 0 + FeatureSet_EXPLICIT FeatureSet_FieldPresence = 1 + FeatureSet_IMPLICIT FeatureSet_FieldPresence = 2 + FeatureSet_LEGACY_REQUIRED FeatureSet_FieldPresence = 3 +) + +// Enum value maps for FeatureSet_FieldPresence. +var ( + FeatureSet_FieldPresence_name = map[int32]string{ + 0: "FIELD_PRESENCE_UNKNOWN", + 1: "EXPLICIT", + 2: "IMPLICIT", + 3: "LEGACY_REQUIRED", + } + FeatureSet_FieldPresence_value = map[string]int32{ + "FIELD_PRESENCE_UNKNOWN": 0, + "EXPLICIT": 1, + "IMPLICIT": 2, + "LEGACY_REQUIRED": 3, + } +) + +func (x FeatureSet_FieldPresence) Enum() *FeatureSet_FieldPresence { + p := new(FeatureSet_FieldPresence) + *p = x + return p +} + +func (x FeatureSet_FieldPresence) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_FieldPresence) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[10].Descriptor() +} + +func (FeatureSet_FieldPresence) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[10] +} + +func (x FeatureSet_FieldPresence) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *FeatureSet_FieldPresence) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = FeatureSet_FieldPresence(num) + return nil +} + +// Deprecated: Use FeatureSet_FieldPresence.Descriptor instead. +func (FeatureSet_FieldPresence) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 0} +} + +type FeatureSet_EnumType int32 + +const ( + FeatureSet_ENUM_TYPE_UNKNOWN FeatureSet_EnumType = 0 + FeatureSet_OPEN FeatureSet_EnumType = 1 + FeatureSet_CLOSED FeatureSet_EnumType = 2 +) + +// Enum value maps for FeatureSet_EnumType. +var ( + FeatureSet_EnumType_name = map[int32]string{ + 0: "ENUM_TYPE_UNKNOWN", + 1: "OPEN", + 2: "CLOSED", + } + FeatureSet_EnumType_value = map[string]int32{ + "ENUM_TYPE_UNKNOWN": 0, + "OPEN": 1, + "CLOSED": 2, + } +) + +func (x FeatureSet_EnumType) Enum() *FeatureSet_EnumType { + p := new(FeatureSet_EnumType) + *p = x + return p +} + +func (x FeatureSet_EnumType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_EnumType) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[11].Descriptor() +} + +func (FeatureSet_EnumType) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[11] +} + +func (x FeatureSet_EnumType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *FeatureSet_EnumType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = FeatureSet_EnumType(num) + return nil +} + +// Deprecated: Use FeatureSet_EnumType.Descriptor instead. +func (FeatureSet_EnumType) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 1} +} + +type FeatureSet_RepeatedFieldEncoding int32 + +const ( + FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN FeatureSet_RepeatedFieldEncoding = 0 + FeatureSet_PACKED FeatureSet_RepeatedFieldEncoding = 1 + FeatureSet_EXPANDED FeatureSet_RepeatedFieldEncoding = 2 +) + +// Enum value maps for FeatureSet_RepeatedFieldEncoding. +var ( + FeatureSet_RepeatedFieldEncoding_name = map[int32]string{ + 0: "REPEATED_FIELD_ENCODING_UNKNOWN", + 1: "PACKED", + 2: "EXPANDED", + } + FeatureSet_RepeatedFieldEncoding_value = map[string]int32{ + "REPEATED_FIELD_ENCODING_UNKNOWN": 0, + "PACKED": 1, + "EXPANDED": 2, + } +) + +func (x FeatureSet_RepeatedFieldEncoding) Enum() *FeatureSet_RepeatedFieldEncoding { + p := new(FeatureSet_RepeatedFieldEncoding) + *p = x + return p +} + +func (x FeatureSet_RepeatedFieldEncoding) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_RepeatedFieldEncoding) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[12].Descriptor() +} + +func (FeatureSet_RepeatedFieldEncoding) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[12] +} + +func (x FeatureSet_RepeatedFieldEncoding) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *FeatureSet_RepeatedFieldEncoding) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = FeatureSet_RepeatedFieldEncoding(num) + return nil +} + +// Deprecated: Use FeatureSet_RepeatedFieldEncoding.Descriptor instead. +func (FeatureSet_RepeatedFieldEncoding) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 2} +} + +type FeatureSet_Utf8Validation int32 + +const ( + FeatureSet_UTF8_VALIDATION_UNKNOWN FeatureSet_Utf8Validation = 0 + FeatureSet_VERIFY FeatureSet_Utf8Validation = 2 + FeatureSet_NONE FeatureSet_Utf8Validation = 3 +) + +// Enum value maps for FeatureSet_Utf8Validation. +var ( + FeatureSet_Utf8Validation_name = map[int32]string{ + 0: "UTF8_VALIDATION_UNKNOWN", + 2: "VERIFY", + 3: "NONE", + } + FeatureSet_Utf8Validation_value = map[string]int32{ + "UTF8_VALIDATION_UNKNOWN": 0, + "VERIFY": 2, + "NONE": 3, + } +) + +func (x FeatureSet_Utf8Validation) Enum() *FeatureSet_Utf8Validation { + p := new(FeatureSet_Utf8Validation) + *p = x + return p +} + +func (x FeatureSet_Utf8Validation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_Utf8Validation) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[13].Descriptor() +} + +func (FeatureSet_Utf8Validation) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[13] +} + +func (x FeatureSet_Utf8Validation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *FeatureSet_Utf8Validation) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = FeatureSet_Utf8Validation(num) + return nil +} + +// Deprecated: Use FeatureSet_Utf8Validation.Descriptor instead. +func (FeatureSet_Utf8Validation) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 3} +} + +type FeatureSet_MessageEncoding int32 + +const ( + FeatureSet_MESSAGE_ENCODING_UNKNOWN FeatureSet_MessageEncoding = 0 + FeatureSet_LENGTH_PREFIXED FeatureSet_MessageEncoding = 1 + FeatureSet_DELIMITED FeatureSet_MessageEncoding = 2 +) + +// Enum value maps for FeatureSet_MessageEncoding. +var ( + FeatureSet_MessageEncoding_name = map[int32]string{ + 0: "MESSAGE_ENCODING_UNKNOWN", + 1: "LENGTH_PREFIXED", + 2: "DELIMITED", + } + FeatureSet_MessageEncoding_value = map[string]int32{ + "MESSAGE_ENCODING_UNKNOWN": 0, + "LENGTH_PREFIXED": 1, + "DELIMITED": 2, + } +) + +func (x FeatureSet_MessageEncoding) Enum() *FeatureSet_MessageEncoding { + p := new(FeatureSet_MessageEncoding) + *p = x + return p +} + +func (x FeatureSet_MessageEncoding) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_MessageEncoding) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[14].Descriptor() +} + +func (FeatureSet_MessageEncoding) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[14] +} + +func (x FeatureSet_MessageEncoding) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *FeatureSet_MessageEncoding) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = FeatureSet_MessageEncoding(num) + return nil +} + +// Deprecated: Use FeatureSet_MessageEncoding.Descriptor instead. +func (FeatureSet_MessageEncoding) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 4} +} + +type FeatureSet_JsonFormat int32 + +const ( + FeatureSet_JSON_FORMAT_UNKNOWN FeatureSet_JsonFormat = 0 + FeatureSet_ALLOW FeatureSet_JsonFormat = 1 + FeatureSet_LEGACY_BEST_EFFORT FeatureSet_JsonFormat = 2 +) + +// Enum value maps for FeatureSet_JsonFormat. +var ( + FeatureSet_JsonFormat_name = map[int32]string{ + 0: "JSON_FORMAT_UNKNOWN", + 1: "ALLOW", + 2: "LEGACY_BEST_EFFORT", + } + FeatureSet_JsonFormat_value = map[string]int32{ + "JSON_FORMAT_UNKNOWN": 0, + "ALLOW": 1, + "LEGACY_BEST_EFFORT": 2, + } +) + +func (x FeatureSet_JsonFormat) Enum() *FeatureSet_JsonFormat { + p := new(FeatureSet_JsonFormat) + *p = x + return p +} + +func (x FeatureSet_JsonFormat) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet_JsonFormat) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_descriptor_proto_enumTypes[15].Descriptor() } -func (MethodOptions_IdempotencyLevel) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[7] +func (FeatureSet_JsonFormat) Type() protoreflect.EnumType { + return &file_google_protobuf_descriptor_proto_enumTypes[15] } -func (x MethodOptions_IdempotencyLevel) Number() protoreflect.EnumNumber { +func (x FeatureSet_JsonFormat) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. -func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(b []byte) error { +func (x *FeatureSet_JsonFormat) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = MethodOptions_IdempotencyLevel(num) + *x = FeatureSet_JsonFormat(num) return nil } -// Deprecated: Use MethodOptions_IdempotencyLevel.Descriptor instead. -func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{17, 0} +// Deprecated: Use FeatureSet_JsonFormat.Descriptor instead. +func (FeatureSet_JsonFormat) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 5} } // Represents the identified object's effect on the element in the original @@ -652,11 +1174,11 @@ func (x GeneratedCodeInfo_Annotation_Semantic) String() string { } func (GeneratedCodeInfo_Annotation_Semantic) Descriptor() protoreflect.EnumDescriptor { - return file_google_protobuf_descriptor_proto_enumTypes[8].Descriptor() + return file_google_protobuf_descriptor_proto_enumTypes[16].Descriptor() } func (GeneratedCodeInfo_Annotation_Semantic) Type() protoreflect.EnumType { - return &file_google_protobuf_descriptor_proto_enumTypes[8] + return &file_google_protobuf_descriptor_proto_enumTypes[16] } func (x GeneratedCodeInfo_Annotation_Semantic) Number() protoreflect.EnumNumber { @@ -675,7 +1197,7 @@ func (x *GeneratedCodeInfo_Annotation_Semantic) UnmarshalJSON(b []byte) error { // Deprecated: Use GeneratedCodeInfo_Annotation_Semantic.Descriptor instead. func (GeneratedCodeInfo_Annotation_Semantic) EnumDescriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{20, 0, 0} + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{22, 0, 0} } // The protocol compiler can output a FileDescriptorSet containing the .proto @@ -758,8 +1280,8 @@ type FileDescriptorProto struct { // // If `edition` is present, this value must be "editions". Syntax *string `protobuf:"bytes,12,opt,name=syntax" json:"syntax,omitempty"` - // The edition of the proto file, which is an opaque string. - Edition *string `protobuf:"bytes,13,opt,name=edition" json:"edition,omitempty"` + // The edition of the proto file. + Edition *Edition `protobuf:"varint,14,opt,name=edition,enum=google.protobuf.Edition" json:"edition,omitempty"` } func (x *FileDescriptorProto) Reset() { @@ -878,11 +1400,11 @@ func (x *FileDescriptorProto) GetSyntax() string { return "" } -func (x *FileDescriptorProto) GetEdition() string { +func (x *FileDescriptorProto) GetEdition() Edition { if x != nil && x.Edition != nil { return *x.Edition } - return "" + return Edition_EDITION_UNKNOWN } // Describes a message type. @@ -1015,7 +1537,22 @@ type ExtensionRangeOptions struct { // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` -} + // For external users: DO NOT USE. We are in the process of open sourcing + // extension declaration and executing internal cleanups before it can be + // used externally. + Declaration []*ExtensionRangeOptions_Declaration `protobuf:"bytes,2,rep,name=declaration" json:"declaration,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,50,opt,name=features" json:"features,omitempty"` + // The verification state of the range. + // TODO: flip the default to DECLARATION once all empty ranges + // are marked as UNVERIFIED. + Verification *ExtensionRangeOptions_VerificationState `protobuf:"varint,3,opt,name=verification,enum=google.protobuf.ExtensionRangeOptions_VerificationState,def=1" json:"verification,omitempty"` +} + +// Default values for ExtensionRangeOptions fields. +const ( + Default_ExtensionRangeOptions_Verification = ExtensionRangeOptions_UNVERIFIED +) func (x *ExtensionRangeOptions) Reset() { *x = ExtensionRangeOptions{} @@ -1056,6 +1593,27 @@ func (x *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption return nil } +func (x *ExtensionRangeOptions) GetDeclaration() []*ExtensionRangeOptions_Declaration { + if x != nil { + return x.Declaration + } + return nil +} + +func (x *ExtensionRangeOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + +func (x *ExtensionRangeOptions) GetVerification() ExtensionRangeOptions_VerificationState { + if x != nil && x.Verification != nil { + return *x.Verification + } + return Default_ExtensionRangeOptions_Verification +} + // Describes a field within a message. type FieldDescriptorProto struct { state protoimpl.MessageState @@ -1094,12 +1652,12 @@ type FieldDescriptorProto struct { // If true, this is a proto3 "optional". When a proto3 field is optional, it // tracks presence regardless of field type. // - // When proto3_optional is true, this field must be belong to a oneof to - // signal to old proto3 clients that presence is tracked for this field. This - // oneof is known as a "synthetic" oneof, and this field must be its sole - // member (each proto3 optional field gets its own synthetic oneof). Synthetic - // oneofs exist in the descriptor only, and do not generate any API. Synthetic - // oneofs must be ordered after all "real" oneofs. + // When proto3_optional is true, this field must belong to a oneof to signal + // to old proto3 clients that presence is tracked for this field. This oneof + // is known as a "synthetic" oneof, and this field must be its sole member + // (each proto3 optional field gets its own synthetic oneof). Synthetic oneofs + // exist in the descriptor only, and do not generate any API. Synthetic oneofs + // must be ordered after all "real" oneofs. // // For message fields, proto3_optional doesn't create any semantic change, // since non-repeated message fields always track presence. However it still @@ -1646,7 +2204,6 @@ type FileOptions struct { CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,json=ccGenericServices,def=0" json:"cc_generic_services,omitempty"` JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,json=javaGenericServices,def=0" json:"java_generic_services,omitempty"` PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,json=pyGenericServices,def=0" json:"py_generic_services,omitempty"` - PhpGenericServices *bool `protobuf:"varint,42,opt,name=php_generic_services,json=phpGenericServices,def=0" json:"php_generic_services,omitempty"` // Is this file deprecated? // Depending on the target platform, this can emit Deprecated annotations // for everything in the file, or it will be completely ignored; in the very @@ -1680,6 +2237,8 @@ type FileOptions struct { // is empty. When this option is not set, the package name will be used for // determining the ruby package. RubyPackage *string `protobuf:"bytes,45,opt,name=ruby_package,json=rubyPackage" json:"ruby_package,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,50,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. // See the documentation for the "Options" section above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` @@ -1693,7 +2252,6 @@ const ( Default_FileOptions_CcGenericServices = bool(false) Default_FileOptions_JavaGenericServices = bool(false) Default_FileOptions_PyGenericServices = bool(false) - Default_FileOptions_PhpGenericServices = bool(false) Default_FileOptions_Deprecated = bool(false) Default_FileOptions_CcEnableArenas = bool(true) ) @@ -1801,13 +2359,6 @@ func (x *FileOptions) GetPyGenericServices() bool { return Default_FileOptions_PyGenericServices } -func (x *FileOptions) GetPhpGenericServices() bool { - if x != nil && x.PhpGenericServices != nil { - return *x.PhpGenericServices - } - return Default_FileOptions_PhpGenericServices -} - func (x *FileOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -1871,6 +2422,13 @@ func (x *FileOptions) GetRubyPackage() string { return "" } +func (x *FileOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *FileOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -1914,10 +2472,6 @@ type MessageOptions struct { // for the message, or it will be completely ignored; in the very least, // this is a formalization for deprecating messages. Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - // // Whether the message is an automatically generated map entry type for the // maps field. // @@ -1938,6 +2492,10 @@ type MessageOptions struct { // use a native map in the target language to hold the keys and values. // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"` // Enable the legacy handling of JSON field name conflicts. This lowercases // and strips underscored from the fields before comparison in proto3 only. @@ -1947,11 +2505,13 @@ type MessageOptions struct { // This should only be used as a temporary measure against broken builds due // to the change in behavior for JSON field name conflicts. // - // TODO(b/261750190) This is legacy behavior we plan to remove once downstream + // TODO This is legacy behavior we plan to remove once downstream // teams have had time to migrate. // // Deprecated: Marked as deprecated in google/protobuf/descriptor.proto. DeprecatedLegacyJsonFieldConflicts *bool `protobuf:"varint,11,opt,name=deprecated_legacy_json_field_conflicts,json=deprecatedLegacyJsonFieldConflicts" json:"deprecated_legacy_json_field_conflicts,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,12,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2031,6 +2591,13 @@ func (x *MessageOptions) GetDeprecatedLegacyJsonFieldConflicts() bool { return false } +func (x *MessageOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *MessageOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2046,14 +2613,18 @@ type FieldOptions struct { // The ctype option instructs the C++ code generator to use a different // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! + // options below. This option is only implemented to support use of + // [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of + // type "bytes" in the open source release -- sorry, we'll try to include + // other types in a future version! Ctype *FieldOptions_CType `protobuf:"varint,1,opt,name=ctype,enum=google.protobuf.FieldOptions_CType,def=0" json:"ctype,omitempty"` // The packed option can be enabled for repeated primitive fields to enable // a more efficient representation on the wire. Rather than repeatedly // writing the tag and type for each element, the entire array is encoded as // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. + // false will avoid using packed encoding. This option is prohibited in + // Editions, but the `repeated_field_encoding` feature can be used to control + // the behavior. Packed *bool `protobuf:"varint,2,opt,name=packed" json:"packed,omitempty"` // The jstype option determines the JavaScript type used for values of the // field. The option is permitted only for 64 bit integral and fixed types @@ -2084,19 +2655,11 @@ type FieldOptions struct { // call from multiple threads concurrently, while non-const methods continue // to require exclusive access. // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - // - // As of May 2022, lazy verifies the contents of the byte stream during - // parsing. An invalid byte stream will cause the overall parsing to fail. + // Note that lazy message fields are still eagerly verified to check + // ill-formed wireformat or missing required fields. Calling IsInitialized() + // on the outer message would fail if the inner message has missing required + // fields. Failed verification would result in parsing failure (except when + // uninitialized messages are acceptable). Lazy *bool `protobuf:"varint,5,opt,name=lazy,def=0" json:"lazy,omitempty"` // unverified_lazy does no correctness checks on the byte stream. This should // only be used where lazy with verification is prohibitive for performance @@ -2111,9 +2674,12 @@ type FieldOptions struct { Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"` // Indicate that the field value should not be printed out when using debug // formats, e.g. when the field contains sensitive credentials. - DebugRedact *bool `protobuf:"varint,16,opt,name=debug_redact,json=debugRedact,def=0" json:"debug_redact,omitempty"` - Retention *FieldOptions_OptionRetention `protobuf:"varint,17,opt,name=retention,enum=google.protobuf.FieldOptions_OptionRetention" json:"retention,omitempty"` - Target *FieldOptions_OptionTargetType `protobuf:"varint,18,opt,name=target,enum=google.protobuf.FieldOptions_OptionTargetType" json:"target,omitempty"` + DebugRedact *bool `protobuf:"varint,16,opt,name=debug_redact,json=debugRedact,def=0" json:"debug_redact,omitempty"` + Retention *FieldOptions_OptionRetention `protobuf:"varint,17,opt,name=retention,enum=google.protobuf.FieldOptions_OptionRetention" json:"retention,omitempty"` + Targets []FieldOptions_OptionTargetType `protobuf:"varint,19,rep,name=targets,enum=google.protobuf.FieldOptions_OptionTargetType" json:"targets,omitempty"` + EditionDefaults []*FieldOptions_EditionDefault `protobuf:"bytes,20,rep,name=edition_defaults,json=editionDefaults" json:"edition_defaults,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,21,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2224,11 +2790,25 @@ func (x *FieldOptions) GetRetention() FieldOptions_OptionRetention { return FieldOptions_RETENTION_UNKNOWN } -func (x *FieldOptions) GetTarget() FieldOptions_OptionTargetType { - if x != nil && x.Target != nil { - return *x.Target +func (x *FieldOptions) GetTargets() []FieldOptions_OptionTargetType { + if x != nil { + return x.Targets + } + return nil +} + +func (x *FieldOptions) GetEditionDefaults() []*FieldOptions_EditionDefault { + if x != nil { + return x.EditionDefaults + } + return nil +} + +func (x *FieldOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features } - return FieldOptions_TARGET_TYPE_UNKNOWN + return nil } func (x *FieldOptions) GetUninterpretedOption() []*UninterpretedOption { @@ -2244,6 +2824,8 @@ type OneofOptions struct { unknownFields protoimpl.UnknownFields extensionFields protoimpl.ExtensionFields + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,1,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2280,6 +2862,13 @@ func (*OneofOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{13} } +func (x *OneofOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2305,11 +2894,13 @@ type EnumOptions struct { // and strips underscored from the fields before comparison in proto3 only. // The new behavior takes `json_name` into account and applies to proto2 as // well. - // TODO(b/261750190) Remove this legacy behavior once downstream teams have + // TODO Remove this legacy behavior once downstream teams have // had time to migrate. // // Deprecated: Marked as deprecated in google/protobuf/descriptor.proto. DeprecatedLegacyJsonFieldConflicts *bool `protobuf:"varint,6,opt,name=deprecated_legacy_json_field_conflicts,json=deprecatedLegacyJsonFieldConflicts" json:"deprecated_legacy_json_field_conflicts,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,7,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2373,6 +2964,13 @@ func (x *EnumOptions) GetDeprecatedLegacyJsonFieldConflicts() bool { return false } +func (x *EnumOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *EnumOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2391,13 +2989,20 @@ type EnumValueOptions struct { // for the enum value, or it will be completely ignored; in the very least, // this is a formalization for deprecating enum values. Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,2,opt,name=features" json:"features,omitempty"` + // Indicate that fields annotated with this enum value should not be printed + // out when using debug formats, e.g. when the field contains sensitive + // credentials. + DebugRedact *bool `protobuf:"varint,3,opt,name=debug_redact,json=debugRedact,def=0" json:"debug_redact,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } // Default values for EnumValueOptions fields. const ( - Default_EnumValueOptions_Deprecated = bool(false) + Default_EnumValueOptions_Deprecated = bool(false) + Default_EnumValueOptions_DebugRedact = bool(false) ) func (x *EnumValueOptions) Reset() { @@ -2439,6 +3044,20 @@ func (x *EnumValueOptions) GetDeprecated() bool { return Default_EnumValueOptions_Deprecated } +func (x *EnumValueOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + +func (x *EnumValueOptions) GetDebugRedact() bool { + if x != nil && x.DebugRedact != nil { + return *x.DebugRedact + } + return Default_EnumValueOptions_DebugRedact +} + func (x *EnumValueOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2452,6 +3071,8 @@ type ServiceOptions struct { unknownFields protoimpl.UnknownFields extensionFields protoimpl.ExtensionFields + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,34,opt,name=features" json:"features,omitempty"` // Is this service deprecated? // Depending on the target platform, this can emit Deprecated annotations // for the service, or it will be completely ignored; in the very least, @@ -2498,6 +3119,13 @@ func (*ServiceOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{16} } +func (x *ServiceOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *ServiceOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -2524,6 +3152,8 @@ type MethodOptions struct { // this is a formalization for deprecating methods. Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` IdempotencyLevel *MethodOptions_IdempotencyLevel `protobuf:"varint,34,opt,name=idempotency_level,json=idempotencyLevel,enum=google.protobuf.MethodOptions_IdempotencyLevel,def=0" json:"idempotency_level,omitempty"` + // Any features defined in the specific edition. + Features *FeatureSet `protobuf:"bytes,35,opt,name=features" json:"features,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2580,6 +3210,13 @@ func (x *MethodOptions) GetIdempotencyLevel() MethodOptions_IdempotencyLevel { return Default_MethodOptions_IdempotencyLevel } +func (x *MethodOptions) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + func (x *MethodOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2690,6 +3327,171 @@ func (x *UninterpretedOption) GetAggregateValue() string { return "" } +// TODO Enums in C++ gencode (and potentially other languages) are +// not well scoped. This means that each of the feature enums below can clash +// with each other. The short names we've chosen maximize call-site +// readability, but leave us very open to this scenario. A future feature will +// be designed and implemented to handle this, hopefully before we ever hit a +// conflict here. +type FeatureSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + extensionFields protoimpl.ExtensionFields + + FieldPresence *FeatureSet_FieldPresence `protobuf:"varint,1,opt,name=field_presence,json=fieldPresence,enum=google.protobuf.FeatureSet_FieldPresence" json:"field_presence,omitempty"` + EnumType *FeatureSet_EnumType `protobuf:"varint,2,opt,name=enum_type,json=enumType,enum=google.protobuf.FeatureSet_EnumType" json:"enum_type,omitempty"` + RepeatedFieldEncoding *FeatureSet_RepeatedFieldEncoding `protobuf:"varint,3,opt,name=repeated_field_encoding,json=repeatedFieldEncoding,enum=google.protobuf.FeatureSet_RepeatedFieldEncoding" json:"repeated_field_encoding,omitempty"` + Utf8Validation *FeatureSet_Utf8Validation `protobuf:"varint,4,opt,name=utf8_validation,json=utf8Validation,enum=google.protobuf.FeatureSet_Utf8Validation" json:"utf8_validation,omitempty"` + MessageEncoding *FeatureSet_MessageEncoding `protobuf:"varint,5,opt,name=message_encoding,json=messageEncoding,enum=google.protobuf.FeatureSet_MessageEncoding" json:"message_encoding,omitempty"` + JsonFormat *FeatureSet_JsonFormat `protobuf:"varint,6,opt,name=json_format,json=jsonFormat,enum=google.protobuf.FeatureSet_JsonFormat" json:"json_format,omitempty"` +} + +func (x *FeatureSet) Reset() { + *x = FeatureSet{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FeatureSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FeatureSet) ProtoMessage() {} + +func (x *FeatureSet) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FeatureSet.ProtoReflect.Descriptor instead. +func (*FeatureSet) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19} +} + +func (x *FeatureSet) GetFieldPresence() FeatureSet_FieldPresence { + if x != nil && x.FieldPresence != nil { + return *x.FieldPresence + } + return FeatureSet_FIELD_PRESENCE_UNKNOWN +} + +func (x *FeatureSet) GetEnumType() FeatureSet_EnumType { + if x != nil && x.EnumType != nil { + return *x.EnumType + } + return FeatureSet_ENUM_TYPE_UNKNOWN +} + +func (x *FeatureSet) GetRepeatedFieldEncoding() FeatureSet_RepeatedFieldEncoding { + if x != nil && x.RepeatedFieldEncoding != nil { + return *x.RepeatedFieldEncoding + } + return FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN +} + +func (x *FeatureSet) GetUtf8Validation() FeatureSet_Utf8Validation { + if x != nil && x.Utf8Validation != nil { + return *x.Utf8Validation + } + return FeatureSet_UTF8_VALIDATION_UNKNOWN +} + +func (x *FeatureSet) GetMessageEncoding() FeatureSet_MessageEncoding { + if x != nil && x.MessageEncoding != nil { + return *x.MessageEncoding + } + return FeatureSet_MESSAGE_ENCODING_UNKNOWN +} + +func (x *FeatureSet) GetJsonFormat() FeatureSet_JsonFormat { + if x != nil && x.JsonFormat != nil { + return *x.JsonFormat + } + return FeatureSet_JSON_FORMAT_UNKNOWN +} + +// A compiled specification for the defaults of a set of features. These +// messages are generated from FeatureSet extensions and can be used to seed +// feature resolution. The resolution with this object becomes a simple search +// for the closest matching edition, followed by proto merges. +type FeatureSetDefaults struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Defaults []*FeatureSetDefaults_FeatureSetEditionDefault `protobuf:"bytes,1,rep,name=defaults" json:"defaults,omitempty"` + // The minimum supported edition (inclusive) when this was constructed. + // Editions before this will not have defaults. + MinimumEdition *Edition `protobuf:"varint,4,opt,name=minimum_edition,json=minimumEdition,enum=google.protobuf.Edition" json:"minimum_edition,omitempty"` + // The maximum known edition (inclusive) when this was constructed. Editions + // after this will not have reliable defaults. + MaximumEdition *Edition `protobuf:"varint,5,opt,name=maximum_edition,json=maximumEdition,enum=google.protobuf.Edition" json:"maximum_edition,omitempty"` +} + +func (x *FeatureSetDefaults) Reset() { + *x = FeatureSetDefaults{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FeatureSetDefaults) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FeatureSetDefaults) ProtoMessage() {} + +func (x *FeatureSetDefaults) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FeatureSetDefaults.ProtoReflect.Descriptor instead. +func (*FeatureSetDefaults) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{20} +} + +func (x *FeatureSetDefaults) GetDefaults() []*FeatureSetDefaults_FeatureSetEditionDefault { + if x != nil { + return x.Defaults + } + return nil +} + +func (x *FeatureSetDefaults) GetMinimumEdition() Edition { + if x != nil && x.MinimumEdition != nil { + return *x.MinimumEdition + } + return Edition_EDITION_UNKNOWN +} + +func (x *FeatureSetDefaults) GetMaximumEdition() Edition { + if x != nil && x.MaximumEdition != nil { + return *x.MaximumEdition + } + return Edition_EDITION_UNKNOWN +} + // Encapsulates information about the original source file from which a // FileDescriptorProto was generated. type SourceCodeInfo struct { @@ -2751,7 +3553,7 @@ type SourceCodeInfo struct { func (x *SourceCodeInfo) Reset() { *x = SourceCodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[19] + mi := &file_google_protobuf_descriptor_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2764,7 +3566,7 @@ func (x *SourceCodeInfo) String() string { func (*SourceCodeInfo) ProtoMessage() {} func (x *SourceCodeInfo) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[19] + mi := &file_google_protobuf_descriptor_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2777,7 +3579,7 @@ func (x *SourceCodeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceCodeInfo.ProtoReflect.Descriptor instead. func (*SourceCodeInfo) Descriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19} + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{21} } func (x *SourceCodeInfo) GetLocation() []*SourceCodeInfo_Location { @@ -2803,7 +3605,7 @@ type GeneratedCodeInfo struct { func (x *GeneratedCodeInfo) Reset() { *x = GeneratedCodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[20] + mi := &file_google_protobuf_descriptor_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2816,7 +3618,7 @@ func (x *GeneratedCodeInfo) String() string { func (*GeneratedCodeInfo) ProtoMessage() {} func (x *GeneratedCodeInfo) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[20] + mi := &file_google_protobuf_descriptor_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2829,7 +3631,7 @@ func (x *GeneratedCodeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use GeneratedCodeInfo.ProtoReflect.Descriptor instead. func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{20} + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{22} } func (x *GeneratedCodeInfo) GetAnnotation() []*GeneratedCodeInfo_Annotation { @@ -2852,7 +3654,7 @@ type DescriptorProto_ExtensionRange struct { func (x *DescriptorProto_ExtensionRange) Reset() { *x = DescriptorProto_ExtensionRange{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[21] + mi := &file_google_protobuf_descriptor_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2865,7 +3667,7 @@ func (x *DescriptorProto_ExtensionRange) String() string { func (*DescriptorProto_ExtensionRange) ProtoMessage() {} func (x *DescriptorProto_ExtensionRange) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[21] + mi := &file_google_protobuf_descriptor_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2902,35 +3704,104 @@ func (x *DescriptorProto_ExtensionRange) GetOptions() *ExtensionRangeOptions { return nil } -// Range of reserved tag numbers. Reserved tag numbers may not be used by -// fields or extension ranges in the same message. Reserved ranges may -// not overlap. -type DescriptorProto_ReservedRange struct { +// Range of reserved tag numbers. Reserved tag numbers may not be used by +// fields or extension ranges in the same message. Reserved ranges may +// not overlap. +type DescriptorProto_ReservedRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` // Inclusive. + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` // Exclusive. +} + +func (x *DescriptorProto_ReservedRange) Reset() { + *x = DescriptorProto_ReservedRange{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DescriptorProto_ReservedRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DescriptorProto_ReservedRange) ProtoMessage() {} + +func (x *DescriptorProto_ReservedRange) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DescriptorProto_ReservedRange.ProtoReflect.Descriptor instead. +func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *DescriptorProto_ReservedRange) GetStart() int32 { + if x != nil && x.Start != nil { + return *x.Start + } + return 0 +} + +func (x *DescriptorProto_ReservedRange) GetEnd() int32 { + if x != nil && x.End != nil { + return *x.End + } + return 0 +} + +type ExtensionRangeOptions_Declaration struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` // Inclusive. - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` // Exclusive. -} - -func (x *DescriptorProto_ReservedRange) Reset() { - *x = DescriptorProto_ReservedRange{} + // The extension number declared within the extension range. + Number *int32 `protobuf:"varint,1,opt,name=number" json:"number,omitempty"` + // The fully-qualified name of the extension field. There must be a leading + // dot in front of the full name. + FullName *string `protobuf:"bytes,2,opt,name=full_name,json=fullName" json:"full_name,omitempty"` + // The fully-qualified type name of the extension field. Unlike + // Metadata.type, Declaration.type must have a leading dot for messages + // and enums. + Type *string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"` + // If true, indicates that the number is reserved in the extension range, + // and any extension field with the number will fail to compile. Set this + // when a declared extension field is deleted. + Reserved *bool `protobuf:"varint,5,opt,name=reserved" json:"reserved,omitempty"` + // If true, indicates that the extension must be defined as repeated. + // Otherwise the extension must be defined as optional. + Repeated *bool `protobuf:"varint,6,opt,name=repeated" json:"repeated,omitempty"` +} + +func (x *ExtensionRangeOptions_Declaration) Reset() { + *x = ExtensionRangeOptions_Declaration{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[22] + mi := &file_google_protobuf_descriptor_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DescriptorProto_ReservedRange) String() string { +func (x *ExtensionRangeOptions_Declaration) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DescriptorProto_ReservedRange) ProtoMessage() {} +func (*ExtensionRangeOptions_Declaration) ProtoMessage() {} -func (x *DescriptorProto_ReservedRange) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[22] +func (x *ExtensionRangeOptions_Declaration) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2941,23 +3812,44 @@ func (x *DescriptorProto_ReservedRange) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DescriptorProto_ReservedRange.ProtoReflect.Descriptor instead. -func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{2, 1} +// Deprecated: Use ExtensionRangeOptions_Declaration.ProtoReflect.Descriptor instead. +func (*ExtensionRangeOptions_Declaration) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{3, 0} } -func (x *DescriptorProto_ReservedRange) GetStart() int32 { - if x != nil && x.Start != nil { - return *x.Start +func (x *ExtensionRangeOptions_Declaration) GetNumber() int32 { + if x != nil && x.Number != nil { + return *x.Number } return 0 } -func (x *DescriptorProto_ReservedRange) GetEnd() int32 { - if x != nil && x.End != nil { - return *x.End +func (x *ExtensionRangeOptions_Declaration) GetFullName() string { + if x != nil && x.FullName != nil { + return *x.FullName } - return 0 + return "" +} + +func (x *ExtensionRangeOptions_Declaration) GetType() string { + if x != nil && x.Type != nil { + return *x.Type + } + return "" +} + +func (x *ExtensionRangeOptions_Declaration) GetReserved() bool { + if x != nil && x.Reserved != nil { + return *x.Reserved + } + return false +} + +func (x *ExtensionRangeOptions_Declaration) GetRepeated() bool { + if x != nil && x.Repeated != nil { + return *x.Repeated + } + return false } // Range of reserved numeric values. Reserved values may not be used by @@ -2978,7 +3870,7 @@ type EnumDescriptorProto_EnumReservedRange struct { func (x *EnumDescriptorProto_EnumReservedRange) Reset() { *x = EnumDescriptorProto_EnumReservedRange{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[23] + mi := &file_google_protobuf_descriptor_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2991,7 +3883,7 @@ func (x *EnumDescriptorProto_EnumReservedRange) String() string { func (*EnumDescriptorProto_EnumReservedRange) ProtoMessage() {} func (x *EnumDescriptorProto_EnumReservedRange) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[23] + mi := &file_google_protobuf_descriptor_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3021,6 +3913,61 @@ func (x *EnumDescriptorProto_EnumReservedRange) GetEnd() int32 { return 0 } +type FieldOptions_EditionDefault struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Edition *Edition `protobuf:"varint,3,opt,name=edition,enum=google.protobuf.Edition" json:"edition,omitempty"` + Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` // Textproto value. +} + +func (x *FieldOptions_EditionDefault) Reset() { + *x = FieldOptions_EditionDefault{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldOptions_EditionDefault) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldOptions_EditionDefault) ProtoMessage() {} + +func (x *FieldOptions_EditionDefault) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldOptions_EditionDefault.ProtoReflect.Descriptor instead. +func (*FieldOptions_EditionDefault) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{12, 0} +} + +func (x *FieldOptions_EditionDefault) GetEdition() Edition { + if x != nil && x.Edition != nil { + return *x.Edition + } + return Edition_EDITION_UNKNOWN +} + +func (x *FieldOptions_EditionDefault) GetValue() string { + if x != nil && x.Value != nil { + return *x.Value + } + return "" +} + // The name of the uninterpreted option. Each string represents a segment in // a dot-separated name. is_extension is true iff a segment represents an // extension (denoted with parentheses in options specs in .proto files). @@ -3038,7 +3985,7 @@ type UninterpretedOption_NamePart struct { func (x *UninterpretedOption_NamePart) Reset() { *x = UninterpretedOption_NamePart{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[24] + mi := &file_google_protobuf_descriptor_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3051,7 +3998,7 @@ func (x *UninterpretedOption_NamePart) String() string { func (*UninterpretedOption_NamePart) ProtoMessage() {} func (x *UninterpretedOption_NamePart) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[24] + mi := &file_google_protobuf_descriptor_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3081,6 +4028,65 @@ func (x *UninterpretedOption_NamePart) GetIsExtension() bool { return false } +// A map from every known edition with a unique set of defaults to its +// defaults. Not all editions may be contained here. For a given edition, +// the defaults at the closest matching edition ordered at or before it should +// be used. This field must be in strict ascending order by edition. +type FeatureSetDefaults_FeatureSetEditionDefault struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Edition *Edition `protobuf:"varint,3,opt,name=edition,enum=google.protobuf.Edition" json:"edition,omitempty"` + Features *FeatureSet `protobuf:"bytes,2,opt,name=features" json:"features,omitempty"` +} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) Reset() { + *x = FeatureSetDefaults_FeatureSetEditionDefault{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FeatureSetDefaults_FeatureSetEditionDefault) ProtoMessage() {} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FeatureSetDefaults_FeatureSetEditionDefault.ProtoReflect.Descriptor instead. +func (*FeatureSetDefaults_FeatureSetEditionDefault) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{20, 0} +} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetEdition() Edition { + if x != nil && x.Edition != nil { + return *x.Edition + } + return Edition_EDITION_UNKNOWN +} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetFeatures() *FeatureSet { + if x != nil { + return x.Features + } + return nil +} + type SourceCodeInfo_Location struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3090,7 +4096,7 @@ type SourceCodeInfo_Location struct { // location. // // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition occurs. + // the root FileDescriptorProto to the place where the definition appears. // For example, this path: // // [ 4, 3, 2, 7, 1 ] @@ -3182,7 +4188,7 @@ type SourceCodeInfo_Location struct { func (x *SourceCodeInfo_Location) Reset() { *x = SourceCodeInfo_Location{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[25] + mi := &file_google_protobuf_descriptor_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3195,7 +4201,7 @@ func (x *SourceCodeInfo_Location) String() string { func (*SourceCodeInfo_Location) ProtoMessage() {} func (x *SourceCodeInfo_Location) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[25] + mi := &file_google_protobuf_descriptor_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3208,7 +4214,7 @@ func (x *SourceCodeInfo_Location) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceCodeInfo_Location.ProtoReflect.Descriptor instead. func (*SourceCodeInfo_Location) Descriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{19, 0} + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{21, 0} } func (x *SourceCodeInfo_Location) GetPath() []int32 { @@ -3269,7 +4275,7 @@ type GeneratedCodeInfo_Annotation struct { func (x *GeneratedCodeInfo_Annotation) Reset() { *x = GeneratedCodeInfo_Annotation{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[26] + mi := &file_google_protobuf_descriptor_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3282,7 +4288,7 @@ func (x *GeneratedCodeInfo_Annotation) String() string { func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} func (x *GeneratedCodeInfo_Annotation) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[26] + mi := &file_google_protobuf_descriptor_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3295,7 +4301,7 @@ func (x *GeneratedCodeInfo_Annotation) ProtoReflect() protoreflect.Message { // Deprecated: Use GeneratedCodeInfo_Annotation.ProtoReflect.Descriptor instead. func (*GeneratedCodeInfo_Annotation) Descriptor() ([]byte, []int) { - return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{20, 0} + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{22, 0} } func (x *GeneratedCodeInfo_Annotation) GetPath() []int32 { @@ -3344,7 +4350,7 @@ var file_google_protobuf_descriptor_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x04, 0x66, 0x69, - 0x6c, 0x65, 0x22, 0xfe, 0x04, 0x0a, 0x13, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, + 0x6c, 0x65, 0x22, 0x98, 0x05, 0x0a, 0x13, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, @@ -3382,495 +4388,687 @@ var file_google_protobuf_descriptor_proto_rawDesc = []byte{ 0x75, 0x66, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xb9, 0x06, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x43, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, - 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x0a, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x41, 0x0a, 0x09, 0x65, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, + 0x09, 0x52, 0x06, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x12, 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb9, 0x06, + 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x12, 0x43, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x09, 0x65, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x65, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x44, 0x0a, - 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x18, 0x08, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x09, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x44, - 0x65, 0x63, 0x6c, 0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, - 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x0a, + 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x65, 0x6e, + 0x75, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x58, 0x0a, + 0x0f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x7a, 0x0a, 0x0e, 0x45, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x65, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x37, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, - 0x7c, 0x0a, 0x15, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, - 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xc1, 0x06, - 0x0a, 0x14, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, - 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x12, 0x41, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x05, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x65, 0x12, 0x23, - 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x22, 0xb6, 0x02, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, - 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, - 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x03, 0x12, 0x0f, 0x0a, - 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x04, 0x12, 0x0e, - 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x05, 0x12, 0x10, - 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x58, 0x45, 0x44, 0x36, 0x34, 0x10, 0x06, - 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, - 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10, - 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, - 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, - 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, - 0x47, 0x45, 0x10, 0x0b, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x59, 0x54, - 0x45, 0x53, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, - 0x54, 0x33, 0x32, 0x10, 0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, - 0x55, 0x4d, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, - 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x0f, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x36, 0x34, 0x10, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x11, 0x12, 0x0f, 0x0a, 0x0b, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x12, 0x22, 0x43, 0x0a, 0x05, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x4f, - 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, - 0x45, 0x4c, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x02, 0x12, 0x12, 0x0a, - 0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, - 0x03, 0x22, 0x63, 0x0a, 0x14, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, - 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, + 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, + 0x65, 0x6f, 0x66, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x52, 0x09, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65, 0x63, 0x6c, 0x12, 0x39, 0x0a, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x13, 0x45, 0x6e, 0x75, 0x6d, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5d, 0x0a, 0x0e, 0x72, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x72, 0x65, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x1a, - 0x3b, 0x0a, 0x11, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x83, 0x01, 0x0a, - 0x18, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x3e, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x89, 0x02, 0x0a, - 0x15, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, - 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x22, 0x91, 0x09, 0x0a, 0x0b, 0x46, 0x69, 0x6c, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x61, 0x76, 0x61, - 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x6a, 0x61, 0x76, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6a, - 0x61, 0x76, 0x61, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6a, 0x61, 0x76, 0x61, 0x4f, - 0x75, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, - 0x13, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x52, 0x11, 0x6a, 0x61, 0x76, 0x61, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x1d, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x5f, 0x61, 0x6e, 0x64, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x19, 0x6a, 0x61, 0x76, 0x61, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x71, 0x75, - 0x61, 0x6c, 0x73, 0x41, 0x6e, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x16, 0x6a, 0x61, - 0x76, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, - 0x75, 0x74, 0x66, 0x38, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x52, 0x13, 0x6a, 0x61, 0x76, 0x61, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x55, 0x74, 0x66, 0x38, 0x12, 0x53, 0x0a, 0x0c, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, - 0x7a, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x7a, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x40, + 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x1a, 0x37, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xcc, 0x04, 0x0a, 0x15, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x59, 0x0a, + 0x0b, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x61, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x03, 0x88, 0x01, 0x02, 0x52, 0x0b, 0x64, 0x65, 0x63, + 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x12, 0x6d, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x3a, 0x0a, 0x55, 0x4e, 0x56, 0x45, 0x52, 0x49, 0x46, 0x49, 0x45, 0x44, 0x42, 0x03, 0x88, + 0x01, 0x02, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x1a, 0x94, 0x01, 0x0a, 0x0b, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x75, 0x6c, 0x6c, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, 0x6c, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x34, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x0a, 0x0b, + 0x44, 0x45, 0x43, 0x4c, 0x41, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, + 0x0a, 0x55, 0x4e, 0x56, 0x45, 0x52, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x2a, 0x09, 0x08, + 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xc1, 0x06, 0x0a, 0x14, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x41, 0x0a, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, - 0x69, 0x7a, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x05, 0x53, 0x50, 0x45, 0x45, 0x44, 0x52, 0x0b, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x67, - 0x6f, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x67, 0x6f, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x63, 0x63, - 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, - 0x63, 0x63, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x12, 0x39, 0x0a, 0x15, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, - 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x13, 0x6a, 0x61, 0x76, 0x61, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x13, - 0x70, 0x79, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, - 0x52, 0x11, 0x70, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x14, 0x70, 0x68, 0x70, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x2a, 0x20, 0x01, 0x28, - 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x12, 0x70, 0x68, 0x70, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0a, - 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, + 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0xb6, + 0x02, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x49, 0x58, 0x45, 0x44, 0x36, 0x34, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x07, 0x12, 0x0d, 0x0a, + 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x09, 0x12, 0x0e, 0x0a, + 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x0a, 0x12, 0x10, 0x0a, + 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0b, 0x12, + 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x59, 0x54, 0x45, 0x53, 0x10, 0x0c, 0x12, + 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x0d, + 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x0e, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, + 0x10, 0x0f, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, + 0x44, 0x36, 0x34, 0x10, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, + 0x4e, 0x54, 0x33, 0x32, 0x10, 0x11, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x12, 0x22, 0x43, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, + 0x41, 0x4c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x52, 0x45, + 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x42, 0x45, + 0x4c, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x02, 0x22, 0x63, 0x0a, 0x14, + 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, + 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x13, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x36, + 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5d, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x3b, 0x0a, 0x11, 0x45, 0x6e, + 0x75, 0x6d, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x75, 0x6d, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa7, 0x01, + 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x06, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x39, 0x0a, 0x07, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x89, 0x02, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x30, 0x0a, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, + 0x67, 0x12, 0x30, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x69, 0x6e, 0x67, 0x22, 0x97, 0x09, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6a, 0x61, 0x76, 0x61, 0x50, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6a, 0x61, 0x76, 0x61, 0x4f, 0x75, 0x74, 0x65, 0x72, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x6a, 0x61, 0x76, 0x61, + 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, 0x6a, 0x61, + 0x76, 0x61, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, + 0x44, 0x0a, 0x1d, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x19, 0x6a, 0x61, 0x76, 0x61, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x41, 0x6e, + 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x16, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x74, 0x66, 0x38, 0x18, + 0x1b, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x13, 0x6a, 0x61, + 0x76, 0x61, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x74, 0x66, + 0x38, 0x12, 0x53, 0x0a, 0x0c, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x66, 0x6f, + 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x4d, 0x6f, + 0x64, 0x65, 0x3a, 0x05, 0x53, 0x50, 0x45, 0x45, 0x44, 0x52, 0x0b, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x6f, 0x50, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x63, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, 0x63, 0x63, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x15, + 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x52, 0x13, 0x6a, 0x61, 0x76, 0x61, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x13, 0x70, 0x79, 0x5f, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x11, 0x70, 0x79, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x25, + 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x10, 0x63, 0x63, 0x5f, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x3a, + 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x0e, 0x63, 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x63, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x6f, 0x62, 0x6a, 0x63, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, + 0x78, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x73, 0x68, 0x61, 0x72, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x73, 0x68, + 0x61, 0x72, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x77, 0x69, 0x66, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x27, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x77, 0x69, 0x66, 0x74, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, + 0x28, 0x0a, 0x10, 0x70, 0x68, 0x70, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x68, 0x70, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x68, 0x70, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x70, 0x68, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x34, + 0x0a, 0x16, 0x70, 0x68, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, + 0x70, 0x68, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x62, 0x79, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x62, 0x79, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, + 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x0a, 0x0c, 0x4f, 0x70, + 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x50, + 0x45, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x49, + 0x5a, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x54, 0x45, 0x5f, 0x52, 0x55, 0x4e, + 0x54, 0x49, 0x4d, 0x45, 0x10, 0x03, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, + 0x02, 0x4a, 0x04, 0x08, 0x2a, 0x10, 0x2b, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x22, 0xf4, 0x03, + 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x3c, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, + 0x77, 0x69, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x14, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x53, 0x65, 0x74, 0x57, 0x69, 0x72, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4c, + 0x0a, 0x1f, 0x6e, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x1c, + 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0a, + 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x10, 0x63, 0x63, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, - 0x72, 0x75, 0x65, 0x52, 0x0e, 0x63, 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x72, 0x65, - 0x6e, 0x61, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x63, 0x5f, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x6f, 0x62, 0x6a, 0x63, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, - 0x29, 0x0a, 0x10, 0x63, 0x73, 0x68, 0x61, 0x72, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x73, 0x68, 0x61, 0x72, - 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x77, - 0x69, 0x66, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x73, 0x77, 0x69, 0x66, 0x74, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x28, 0x0a, - 0x10, 0x70, 0x68, 0x70, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x68, 0x70, 0x43, 0x6c, 0x61, 0x73, - 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x68, 0x70, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x70, 0x68, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, - 0x70, 0x68, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x68, - 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x62, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x62, 0x79, 0x50, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, - 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x3a, 0x0a, 0x0c, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x09, 0x0a, 0x05, 0x53, 0x50, 0x45, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, - 0x44, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x54, - 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x03, 0x2a, 0x09, 0x08, 0xe8, 0x07, - 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x22, 0xbb, 0x03, 0x0a, - 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x3c, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x77, - 0x69, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x14, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x53, 0x65, 0x74, 0x57, 0x69, 0x72, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4c, 0x0a, - 0x1f, 0x6e, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x1c, 0x6e, - 0x6f, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0a, 0x64, - 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, - 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x70, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, 0x65, - 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x4c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, - 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, - 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0xb7, 0x08, 0x0a, 0x0c, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x63, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x54, 0x79, 0x70, 0x65, 0x3a, - 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x52, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x09, 0x4a, 0x53, - 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x52, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x19, 0x0a, 0x04, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, 0x6c, 0x61, 0x7a, 0x79, 0x12, 0x2e, 0x0a, 0x0f, 0x75, 0x6e, - 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0e, 0x75, 0x6e, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, - 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x19, 0x0a, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, - 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x12, 0x28, 0x0a, 0x0c, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, - 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, 0x12, 0x4b, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x70, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, + 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, + 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, + 0x08, 0x09, 0x10, 0x0a, 0x22, 0xad, 0x0a, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, + 0x47, 0x52, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, + 0x12, 0x47, 0x0a, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, + 0x4c, 0x52, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x04, 0x6c, 0x61, 0x7a, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, + 0x6c, 0x61, 0x7a, 0x79, 0x12, 0x2e, 0x0a, 0x0f, 0x75, 0x6e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0e, 0x75, 0x6e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, + 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x04, 0x77, + 0x65, 0x61, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x12, 0x28, 0x0a, 0x0c, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, + 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, + 0x6c, 0x73, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, + 0x12, 0x4b, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x58, 0x0a, 0x14, 0x75, - 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x05, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, - 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, - 0x52, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x50, - 0x49, 0x45, 0x43, 0x45, 0x10, 0x02, 0x22, 0x35, 0x0a, 0x06, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0d, - 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x22, 0x55, 0x0a, - 0x0f, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x14, - 0x0a, 0x10, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x10, 0x02, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, - 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x41, 0x52, 0x47, - 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, - 0x4e, 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, - 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, - 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, - 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x45, 0x4f, 0x46, 0x10, 0x05, - 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, - 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x54, - 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, - 0x44, 0x10, 0x09, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, - 0x08, 0x04, 0x10, 0x05, 0x22, 0x73, 0x0a, 0x0c, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, - 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, - 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, - 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x98, 0x02, 0x0a, 0x0b, 0x45, 0x6e, - 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, - 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, + 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, + 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2e, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x57, 0x0a, 0x10, 0x65, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, + 0x0f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, + 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, - 0x08, 0x05, 0x10, 0x06, 0x22, 0x9e, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, - 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, - 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, - 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, - 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, - 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, - 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, - 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, - 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, - 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, - 0x80, 0x80, 0x80, 0x02, 0x22, 0xe0, 0x02, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, - 0x11, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, - 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, - 0x4f, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x10, - 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, - 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, - 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x10, 0x49, 0x64, - 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x17, - 0x0a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4e, 0x4f, 0x5f, 0x53, 0x49, - 0x44, 0x45, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, - 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x2a, 0x09, 0x08, 0xe8, - 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x9a, 0x03, 0x0a, 0x13, 0x55, 0x6e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x69, 0x6f, 0x6e, 0x1a, 0x5a, 0x0a, 0x0e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x2f, 0x0a, 0x05, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, + 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x10, + 0x0a, 0x0c, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x49, 0x45, 0x43, 0x45, 0x10, 0x02, + 0x22, 0x35, 0x0a, 0x06, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, + 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, + 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, + 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x22, 0x55, 0x0a, 0x0f, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, + 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, + 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x54, 0x45, + 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x22, 0x8c, + 0x02, 0x0a, 0x10, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, + 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, + 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x41, 0x4e, 0x47, + 0x45, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, + 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x45, 0x4c, + 0x44, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x45, 0x4f, 0x46, 0x10, 0x05, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, + 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x06, + 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, + 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, + 0x49, 0x43, 0x45, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x10, 0x09, 0x2a, 0x09, 0x08, + 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, + 0x08, 0x12, 0x10, 0x13, 0x22, 0xac, 0x01, 0x0a, 0x0c, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, + 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, - 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6e, - 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x75, - 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, - 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x4a, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, - 0x50, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x72, - 0x74, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa7, 0x02, 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xce, 0x01, - 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x12, 0x16, 0x0a, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, - 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6c, 0x65, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, - 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, - 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, - 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xd0, - 0x02, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x01, 0x0a, 0x0a, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, + 0x80, 0x80, 0x02, 0x22, 0xd1, 0x02, 0x0a, 0x0b, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, + 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x56, 0x0a, 0x26, 0x64, + 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, + 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, + 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, + 0x63, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, + 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, + 0x02, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x81, 0x02, 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, + 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0c, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, + 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xd5, 0x01, 0x0a, 0x0e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, + 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, + 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, + 0x80, 0x80, 0x02, 0x22, 0x99, 0x03, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, 0x11, + 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, + 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x10, 0x69, + 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x23, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, + 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x10, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, + 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, + 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x13, 0x0a, 0x0f, 0x4e, 0x4f, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, + 0x54, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, + 0x4e, 0x54, 0x10, 0x02, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, + 0x9a, 0x03, 0x0a, 0x13, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, + 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, + 0x50, 0x61, 0x72, 0x74, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x10, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x1a, 0x4a, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, + 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, + 0x0b, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8c, 0x0a, 0x0a, + 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x12, 0x8b, 0x01, 0x0a, 0x0e, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, + 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x42, + 0x39, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, + 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x49, + 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe7, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, + 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe8, 0x07, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x65, 0x6e, 0x75, + 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x54, 0x79, + 0x70, 0x65, 0x42, 0x23, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0b, + 0x12, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x09, 0x12, 0x04, + 0x4f, 0x50, 0x45, 0x4e, 0x18, 0xe7, 0x07, 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x92, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, + 0x2e, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x27, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, + 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, 0x58, 0x50, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x18, 0xe6, + 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x44, 0x18, 0xe7, 0x07, 0x52, + 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x78, 0x0a, 0x0f, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x2a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x55, 0x74, 0x66, + 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x88, 0x01, 0x01, + 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x18, + 0xe6, 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x18, 0xe7, 0x07, + 0x52, 0x0e, 0x75, 0x74, 0x66, 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x78, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x20, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, + 0x01, 0x01, 0xa2, 0x01, 0x14, 0x12, 0x0f, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48, 0x5f, 0x50, 0x52, + 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, 0x18, 0xe6, 0x07, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x7c, 0x0a, 0x0b, 0x6a, 0x73, + 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4a, 0x73, 0x6f, + 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, 0x33, 0x88, 0x01, 0x01, 0x98, 0x01, 0x03, 0x98, + 0x01, 0x06, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x17, 0x12, 0x12, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, + 0x5f, 0x42, 0x45, 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x18, 0xe6, 0x07, 0xa2, + 0x01, 0x0a, 0x12, 0x05, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x18, 0xe7, 0x07, 0x52, 0x0a, 0x6a, 0x73, + 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x5c, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x45, + 0x4c, 0x44, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, + 0x54, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, + 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, + 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x22, 0x37, 0x0a, 0x08, 0x45, 0x6e, 0x75, 0x6d, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, + 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x02, 0x22, + 0x56, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x50, 0x45, + 0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, + 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, + 0x41, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x02, 0x22, 0x43, 0x0a, 0x0e, 0x55, 0x74, 0x66, 0x38, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x54, 0x46, + 0x38, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, + 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x53, 0x0a, 0x0f, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, + 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, + 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x10, + 0x02, 0x22, 0x48, 0x0a, 0x0a, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, + 0x17, 0x0a, 0x13, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x4c, 0x4f, + 0x57, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x42, 0x45, + 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x10, 0x02, 0x2a, 0x06, 0x08, 0xe8, 0x07, + 0x10, 0xe9, 0x07, 0x2a, 0x06, 0x08, 0xe9, 0x07, 0x10, 0xea, 0x07, 0x2a, 0x06, 0x08, 0xea, 0x07, + 0x10, 0xeb, 0x07, 0x2a, 0x06, 0x08, 0x8b, 0x4e, 0x10, 0x90, 0x4e, 0x2a, 0x06, 0x08, 0x90, 0x4e, + 0x10, 0x91, 0x4e, 0x4a, 0x06, 0x08, 0xe7, 0x07, 0x10, 0xe8, 0x07, 0x22, 0xfe, 0x02, 0x0a, 0x12, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x73, 0x12, 0x58, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, + 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x53, 0x65, 0x74, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0f, + 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x45, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x1a, 0x87, 0x01, 0x0a, 0x18, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, + 0x74, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, + 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0xa7, 0x02, 0x0a, + 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x44, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xce, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, - 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, - 0x65, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x62, 0x65, 0x67, 0x69, - 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, - 0x65, 0x6e, 0x64, 0x12, 0x52, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x52, 0x08, 0x73, - 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x22, 0x28, 0x0a, 0x08, 0x53, 0x65, 0x6d, 0x61, 0x6e, - 0x74, 0x69, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, - 0x03, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x49, 0x41, 0x53, 0x10, - 0x02, 0x42, 0x7e, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x48, 0x01, 0x5a, 0x2d, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, - 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, + 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x04, 0x73, 0x70, + 0x61, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x70, + 0x61, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x65, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, + 0x11, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, + 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x6c, 0x65, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x6c, + 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d, 0x0a, 0x0a, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x01, 0x0a, 0x0a, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x52, 0x0a, 0x08, 0x73, + 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6d, + 0x61, 0x6e, 0x74, 0x69, 0x63, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x22, + 0x28, 0x0a, 0x08, 0x53, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x09, + 0x0a, 0x05, 0x41, 0x4c, 0x49, 0x41, 0x53, 0x10, 0x02, 0x2a, 0x92, 0x02, 0x0a, 0x07, 0x45, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x44, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x32, 0x10, 0xe6, 0x07, 0x12, + 0x13, 0x0a, 0x0e, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, + 0x33, 0x10, 0xe7, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x32, 0x30, 0x32, 0x33, 0x10, 0xe8, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x32, 0x30, 0x32, 0x34, 0x10, 0xe9, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, + 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x32, + 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x17, + 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x37, 0x5f, 0x54, 0x45, + 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9d, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, + 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5f, 0x54, 0x45, 0x53, + 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9e, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, 0x44, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x39, 0x5f, 0x54, 0x45, 0x53, 0x54, + 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9f, 0x8d, 0x06, 0x12, 0x13, 0x0a, 0x0b, 0x45, 0x44, 0x49, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x41, 0x58, 0x10, 0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x7e, + 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, + 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x48, 0x01, 0x5a, 0x2d, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, + 0x42, 0xaa, 0x02, 0x1a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, } var ( @@ -3885,98 +5083,136 @@ func file_google_protobuf_descriptor_proto_rawDescGZIP() []byte { return file_google_protobuf_descriptor_proto_rawDescData } -var file_google_protobuf_descriptor_proto_enumTypes = make([]protoimpl.EnumInfo, 9) -var file_google_protobuf_descriptor_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_google_protobuf_descriptor_proto_enumTypes = make([]protoimpl.EnumInfo, 17) +var file_google_protobuf_descriptor_proto_msgTypes = make([]protoimpl.MessageInfo, 32) var file_google_protobuf_descriptor_proto_goTypes = []interface{}{ - (FieldDescriptorProto_Type)(0), // 0: google.protobuf.FieldDescriptorProto.Type - (FieldDescriptorProto_Label)(0), // 1: google.protobuf.FieldDescriptorProto.Label - (FileOptions_OptimizeMode)(0), // 2: google.protobuf.FileOptions.OptimizeMode - (FieldOptions_CType)(0), // 3: google.protobuf.FieldOptions.CType - (FieldOptions_JSType)(0), // 4: google.protobuf.FieldOptions.JSType - (FieldOptions_OptionRetention)(0), // 5: google.protobuf.FieldOptions.OptionRetention - (FieldOptions_OptionTargetType)(0), // 6: google.protobuf.FieldOptions.OptionTargetType - (MethodOptions_IdempotencyLevel)(0), // 7: google.protobuf.MethodOptions.IdempotencyLevel - (GeneratedCodeInfo_Annotation_Semantic)(0), // 8: google.protobuf.GeneratedCodeInfo.Annotation.Semantic - (*FileDescriptorSet)(nil), // 9: google.protobuf.FileDescriptorSet - (*FileDescriptorProto)(nil), // 10: google.protobuf.FileDescriptorProto - (*DescriptorProto)(nil), // 11: google.protobuf.DescriptorProto - (*ExtensionRangeOptions)(nil), // 12: google.protobuf.ExtensionRangeOptions - (*FieldDescriptorProto)(nil), // 13: google.protobuf.FieldDescriptorProto - (*OneofDescriptorProto)(nil), // 14: google.protobuf.OneofDescriptorProto - (*EnumDescriptorProto)(nil), // 15: google.protobuf.EnumDescriptorProto - (*EnumValueDescriptorProto)(nil), // 16: google.protobuf.EnumValueDescriptorProto - (*ServiceDescriptorProto)(nil), // 17: google.protobuf.ServiceDescriptorProto - (*MethodDescriptorProto)(nil), // 18: google.protobuf.MethodDescriptorProto - (*FileOptions)(nil), // 19: google.protobuf.FileOptions - (*MessageOptions)(nil), // 20: google.protobuf.MessageOptions - (*FieldOptions)(nil), // 21: google.protobuf.FieldOptions - (*OneofOptions)(nil), // 22: google.protobuf.OneofOptions - (*EnumOptions)(nil), // 23: google.protobuf.EnumOptions - (*EnumValueOptions)(nil), // 24: google.protobuf.EnumValueOptions - (*ServiceOptions)(nil), // 25: google.protobuf.ServiceOptions - (*MethodOptions)(nil), // 26: google.protobuf.MethodOptions - (*UninterpretedOption)(nil), // 27: google.protobuf.UninterpretedOption - (*SourceCodeInfo)(nil), // 28: google.protobuf.SourceCodeInfo - (*GeneratedCodeInfo)(nil), // 29: google.protobuf.GeneratedCodeInfo - (*DescriptorProto_ExtensionRange)(nil), // 30: google.protobuf.DescriptorProto.ExtensionRange - (*DescriptorProto_ReservedRange)(nil), // 31: google.protobuf.DescriptorProto.ReservedRange - (*EnumDescriptorProto_EnumReservedRange)(nil), // 32: google.protobuf.EnumDescriptorProto.EnumReservedRange - (*UninterpretedOption_NamePart)(nil), // 33: google.protobuf.UninterpretedOption.NamePart - (*SourceCodeInfo_Location)(nil), // 34: google.protobuf.SourceCodeInfo.Location - (*GeneratedCodeInfo_Annotation)(nil), // 35: google.protobuf.GeneratedCodeInfo.Annotation + (Edition)(0), // 0: google.protobuf.Edition + (ExtensionRangeOptions_VerificationState)(0), // 1: google.protobuf.ExtensionRangeOptions.VerificationState + (FieldDescriptorProto_Type)(0), // 2: google.protobuf.FieldDescriptorProto.Type + (FieldDescriptorProto_Label)(0), // 3: google.protobuf.FieldDescriptorProto.Label + (FileOptions_OptimizeMode)(0), // 4: google.protobuf.FileOptions.OptimizeMode + (FieldOptions_CType)(0), // 5: google.protobuf.FieldOptions.CType + (FieldOptions_JSType)(0), // 6: google.protobuf.FieldOptions.JSType + (FieldOptions_OptionRetention)(0), // 7: google.protobuf.FieldOptions.OptionRetention + (FieldOptions_OptionTargetType)(0), // 8: google.protobuf.FieldOptions.OptionTargetType + (MethodOptions_IdempotencyLevel)(0), // 9: google.protobuf.MethodOptions.IdempotencyLevel + (FeatureSet_FieldPresence)(0), // 10: google.protobuf.FeatureSet.FieldPresence + (FeatureSet_EnumType)(0), // 11: google.protobuf.FeatureSet.EnumType + (FeatureSet_RepeatedFieldEncoding)(0), // 12: google.protobuf.FeatureSet.RepeatedFieldEncoding + (FeatureSet_Utf8Validation)(0), // 13: google.protobuf.FeatureSet.Utf8Validation + (FeatureSet_MessageEncoding)(0), // 14: google.protobuf.FeatureSet.MessageEncoding + (FeatureSet_JsonFormat)(0), // 15: google.protobuf.FeatureSet.JsonFormat + (GeneratedCodeInfo_Annotation_Semantic)(0), // 16: google.protobuf.GeneratedCodeInfo.Annotation.Semantic + (*FileDescriptorSet)(nil), // 17: google.protobuf.FileDescriptorSet + (*FileDescriptorProto)(nil), // 18: google.protobuf.FileDescriptorProto + (*DescriptorProto)(nil), // 19: google.protobuf.DescriptorProto + (*ExtensionRangeOptions)(nil), // 20: google.protobuf.ExtensionRangeOptions + (*FieldDescriptorProto)(nil), // 21: google.protobuf.FieldDescriptorProto + (*OneofDescriptorProto)(nil), // 22: google.protobuf.OneofDescriptorProto + (*EnumDescriptorProto)(nil), // 23: google.protobuf.EnumDescriptorProto + (*EnumValueDescriptorProto)(nil), // 24: google.protobuf.EnumValueDescriptorProto + (*ServiceDescriptorProto)(nil), // 25: google.protobuf.ServiceDescriptorProto + (*MethodDescriptorProto)(nil), // 26: google.protobuf.MethodDescriptorProto + (*FileOptions)(nil), // 27: google.protobuf.FileOptions + (*MessageOptions)(nil), // 28: google.protobuf.MessageOptions + (*FieldOptions)(nil), // 29: google.protobuf.FieldOptions + (*OneofOptions)(nil), // 30: google.protobuf.OneofOptions + (*EnumOptions)(nil), // 31: google.protobuf.EnumOptions + (*EnumValueOptions)(nil), // 32: google.protobuf.EnumValueOptions + (*ServiceOptions)(nil), // 33: google.protobuf.ServiceOptions + (*MethodOptions)(nil), // 34: google.protobuf.MethodOptions + (*UninterpretedOption)(nil), // 35: google.protobuf.UninterpretedOption + (*FeatureSet)(nil), // 36: google.protobuf.FeatureSet + (*FeatureSetDefaults)(nil), // 37: google.protobuf.FeatureSetDefaults + (*SourceCodeInfo)(nil), // 38: google.protobuf.SourceCodeInfo + (*GeneratedCodeInfo)(nil), // 39: google.protobuf.GeneratedCodeInfo + (*DescriptorProto_ExtensionRange)(nil), // 40: google.protobuf.DescriptorProto.ExtensionRange + (*DescriptorProto_ReservedRange)(nil), // 41: google.protobuf.DescriptorProto.ReservedRange + (*ExtensionRangeOptions_Declaration)(nil), // 42: google.protobuf.ExtensionRangeOptions.Declaration + (*EnumDescriptorProto_EnumReservedRange)(nil), // 43: google.protobuf.EnumDescriptorProto.EnumReservedRange + (*FieldOptions_EditionDefault)(nil), // 44: google.protobuf.FieldOptions.EditionDefault + (*UninterpretedOption_NamePart)(nil), // 45: google.protobuf.UninterpretedOption.NamePart + (*FeatureSetDefaults_FeatureSetEditionDefault)(nil), // 46: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault + (*SourceCodeInfo_Location)(nil), // 47: google.protobuf.SourceCodeInfo.Location + (*GeneratedCodeInfo_Annotation)(nil), // 48: google.protobuf.GeneratedCodeInfo.Annotation } var file_google_protobuf_descriptor_proto_depIdxs = []int32{ - 10, // 0: google.protobuf.FileDescriptorSet.file:type_name -> google.protobuf.FileDescriptorProto - 11, // 1: google.protobuf.FileDescriptorProto.message_type:type_name -> google.protobuf.DescriptorProto - 15, // 2: google.protobuf.FileDescriptorProto.enum_type:type_name -> google.protobuf.EnumDescriptorProto - 17, // 3: google.protobuf.FileDescriptorProto.service:type_name -> google.protobuf.ServiceDescriptorProto - 13, // 4: google.protobuf.FileDescriptorProto.extension:type_name -> google.protobuf.FieldDescriptorProto - 19, // 5: google.protobuf.FileDescriptorProto.options:type_name -> google.protobuf.FileOptions - 28, // 6: google.protobuf.FileDescriptorProto.source_code_info:type_name -> google.protobuf.SourceCodeInfo - 13, // 7: google.protobuf.DescriptorProto.field:type_name -> google.protobuf.FieldDescriptorProto - 13, // 8: google.protobuf.DescriptorProto.extension:type_name -> google.protobuf.FieldDescriptorProto - 11, // 9: google.protobuf.DescriptorProto.nested_type:type_name -> google.protobuf.DescriptorProto - 15, // 10: google.protobuf.DescriptorProto.enum_type:type_name -> google.protobuf.EnumDescriptorProto - 30, // 11: google.protobuf.DescriptorProto.extension_range:type_name -> google.protobuf.DescriptorProto.ExtensionRange - 14, // 12: google.protobuf.DescriptorProto.oneof_decl:type_name -> google.protobuf.OneofDescriptorProto - 20, // 13: google.protobuf.DescriptorProto.options:type_name -> google.protobuf.MessageOptions - 31, // 14: google.protobuf.DescriptorProto.reserved_range:type_name -> google.protobuf.DescriptorProto.ReservedRange - 27, // 15: google.protobuf.ExtensionRangeOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 1, // 16: google.protobuf.FieldDescriptorProto.label:type_name -> google.protobuf.FieldDescriptorProto.Label - 0, // 17: google.protobuf.FieldDescriptorProto.type:type_name -> google.protobuf.FieldDescriptorProto.Type - 21, // 18: google.protobuf.FieldDescriptorProto.options:type_name -> google.protobuf.FieldOptions - 22, // 19: google.protobuf.OneofDescriptorProto.options:type_name -> google.protobuf.OneofOptions - 16, // 20: google.protobuf.EnumDescriptorProto.value:type_name -> google.protobuf.EnumValueDescriptorProto - 23, // 21: google.protobuf.EnumDescriptorProto.options:type_name -> google.protobuf.EnumOptions - 32, // 22: google.protobuf.EnumDescriptorProto.reserved_range:type_name -> google.protobuf.EnumDescriptorProto.EnumReservedRange - 24, // 23: google.protobuf.EnumValueDescriptorProto.options:type_name -> google.protobuf.EnumValueOptions - 18, // 24: google.protobuf.ServiceDescriptorProto.method:type_name -> google.protobuf.MethodDescriptorProto - 25, // 25: google.protobuf.ServiceDescriptorProto.options:type_name -> google.protobuf.ServiceOptions - 26, // 26: google.protobuf.MethodDescriptorProto.options:type_name -> google.protobuf.MethodOptions - 2, // 27: google.protobuf.FileOptions.optimize_for:type_name -> google.protobuf.FileOptions.OptimizeMode - 27, // 28: google.protobuf.FileOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 27, // 29: google.protobuf.MessageOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 3, // 30: google.protobuf.FieldOptions.ctype:type_name -> google.protobuf.FieldOptions.CType - 4, // 31: google.protobuf.FieldOptions.jstype:type_name -> google.protobuf.FieldOptions.JSType - 5, // 32: google.protobuf.FieldOptions.retention:type_name -> google.protobuf.FieldOptions.OptionRetention - 6, // 33: google.protobuf.FieldOptions.target:type_name -> google.protobuf.FieldOptions.OptionTargetType - 27, // 34: google.protobuf.FieldOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 27, // 35: google.protobuf.OneofOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 27, // 36: google.protobuf.EnumOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 27, // 37: google.protobuf.EnumValueOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 27, // 38: google.protobuf.ServiceOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 7, // 39: google.protobuf.MethodOptions.idempotency_level:type_name -> google.protobuf.MethodOptions.IdempotencyLevel - 27, // 40: google.protobuf.MethodOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 33, // 41: google.protobuf.UninterpretedOption.name:type_name -> google.protobuf.UninterpretedOption.NamePart - 34, // 42: google.protobuf.SourceCodeInfo.location:type_name -> google.protobuf.SourceCodeInfo.Location - 35, // 43: google.protobuf.GeneratedCodeInfo.annotation:type_name -> google.protobuf.GeneratedCodeInfo.Annotation - 12, // 44: google.protobuf.DescriptorProto.ExtensionRange.options:type_name -> google.protobuf.ExtensionRangeOptions - 8, // 45: google.protobuf.GeneratedCodeInfo.Annotation.semantic:type_name -> google.protobuf.GeneratedCodeInfo.Annotation.Semantic - 46, // [46:46] is the sub-list for method output_type - 46, // [46:46] is the sub-list for method input_type - 46, // [46:46] is the sub-list for extension type_name - 46, // [46:46] is the sub-list for extension extendee - 0, // [0:46] is the sub-list for field type_name + 18, // 0: google.protobuf.FileDescriptorSet.file:type_name -> google.protobuf.FileDescriptorProto + 19, // 1: google.protobuf.FileDescriptorProto.message_type:type_name -> google.protobuf.DescriptorProto + 23, // 2: google.protobuf.FileDescriptorProto.enum_type:type_name -> google.protobuf.EnumDescriptorProto + 25, // 3: google.protobuf.FileDescriptorProto.service:type_name -> google.protobuf.ServiceDescriptorProto + 21, // 4: google.protobuf.FileDescriptorProto.extension:type_name -> google.protobuf.FieldDescriptorProto + 27, // 5: google.protobuf.FileDescriptorProto.options:type_name -> google.protobuf.FileOptions + 38, // 6: google.protobuf.FileDescriptorProto.source_code_info:type_name -> google.protobuf.SourceCodeInfo + 0, // 7: google.protobuf.FileDescriptorProto.edition:type_name -> google.protobuf.Edition + 21, // 8: google.protobuf.DescriptorProto.field:type_name -> google.protobuf.FieldDescriptorProto + 21, // 9: google.protobuf.DescriptorProto.extension:type_name -> google.protobuf.FieldDescriptorProto + 19, // 10: google.protobuf.DescriptorProto.nested_type:type_name -> google.protobuf.DescriptorProto + 23, // 11: google.protobuf.DescriptorProto.enum_type:type_name -> google.protobuf.EnumDescriptorProto + 40, // 12: google.protobuf.DescriptorProto.extension_range:type_name -> google.protobuf.DescriptorProto.ExtensionRange + 22, // 13: google.protobuf.DescriptorProto.oneof_decl:type_name -> google.protobuf.OneofDescriptorProto + 28, // 14: google.protobuf.DescriptorProto.options:type_name -> google.protobuf.MessageOptions + 41, // 15: google.protobuf.DescriptorProto.reserved_range:type_name -> google.protobuf.DescriptorProto.ReservedRange + 35, // 16: google.protobuf.ExtensionRangeOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 42, // 17: google.protobuf.ExtensionRangeOptions.declaration:type_name -> google.protobuf.ExtensionRangeOptions.Declaration + 36, // 18: google.protobuf.ExtensionRangeOptions.features:type_name -> google.protobuf.FeatureSet + 1, // 19: google.protobuf.ExtensionRangeOptions.verification:type_name -> google.protobuf.ExtensionRangeOptions.VerificationState + 3, // 20: google.protobuf.FieldDescriptorProto.label:type_name -> google.protobuf.FieldDescriptorProto.Label + 2, // 21: google.protobuf.FieldDescriptorProto.type:type_name -> google.protobuf.FieldDescriptorProto.Type + 29, // 22: google.protobuf.FieldDescriptorProto.options:type_name -> google.protobuf.FieldOptions + 30, // 23: google.protobuf.OneofDescriptorProto.options:type_name -> google.protobuf.OneofOptions + 24, // 24: google.protobuf.EnumDescriptorProto.value:type_name -> google.protobuf.EnumValueDescriptorProto + 31, // 25: google.protobuf.EnumDescriptorProto.options:type_name -> google.protobuf.EnumOptions + 43, // 26: google.protobuf.EnumDescriptorProto.reserved_range:type_name -> google.protobuf.EnumDescriptorProto.EnumReservedRange + 32, // 27: google.protobuf.EnumValueDescriptorProto.options:type_name -> google.protobuf.EnumValueOptions + 26, // 28: google.protobuf.ServiceDescriptorProto.method:type_name -> google.protobuf.MethodDescriptorProto + 33, // 29: google.protobuf.ServiceDescriptorProto.options:type_name -> google.protobuf.ServiceOptions + 34, // 30: google.protobuf.MethodDescriptorProto.options:type_name -> google.protobuf.MethodOptions + 4, // 31: google.protobuf.FileOptions.optimize_for:type_name -> google.protobuf.FileOptions.OptimizeMode + 36, // 32: google.protobuf.FileOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 33: google.protobuf.FileOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 34: google.protobuf.MessageOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 35: google.protobuf.MessageOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 5, // 36: google.protobuf.FieldOptions.ctype:type_name -> google.protobuf.FieldOptions.CType + 6, // 37: google.protobuf.FieldOptions.jstype:type_name -> google.protobuf.FieldOptions.JSType + 7, // 38: google.protobuf.FieldOptions.retention:type_name -> google.protobuf.FieldOptions.OptionRetention + 8, // 39: google.protobuf.FieldOptions.targets:type_name -> google.protobuf.FieldOptions.OptionTargetType + 44, // 40: google.protobuf.FieldOptions.edition_defaults:type_name -> google.protobuf.FieldOptions.EditionDefault + 36, // 41: google.protobuf.FieldOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 42: google.protobuf.FieldOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 43: google.protobuf.OneofOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 44: google.protobuf.OneofOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 45: google.protobuf.EnumOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 46: google.protobuf.EnumOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 47: google.protobuf.EnumValueOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 48: google.protobuf.EnumValueOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 49: google.protobuf.ServiceOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 50: google.protobuf.ServiceOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 9, // 51: google.protobuf.MethodOptions.idempotency_level:type_name -> google.protobuf.MethodOptions.IdempotencyLevel + 36, // 52: google.protobuf.MethodOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 53: google.protobuf.MethodOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 45, // 54: google.protobuf.UninterpretedOption.name:type_name -> google.protobuf.UninterpretedOption.NamePart + 10, // 55: google.protobuf.FeatureSet.field_presence:type_name -> google.protobuf.FeatureSet.FieldPresence + 11, // 56: google.protobuf.FeatureSet.enum_type:type_name -> google.protobuf.FeatureSet.EnumType + 12, // 57: google.protobuf.FeatureSet.repeated_field_encoding:type_name -> google.protobuf.FeatureSet.RepeatedFieldEncoding + 13, // 58: google.protobuf.FeatureSet.utf8_validation:type_name -> google.protobuf.FeatureSet.Utf8Validation + 14, // 59: google.protobuf.FeatureSet.message_encoding:type_name -> google.protobuf.FeatureSet.MessageEncoding + 15, // 60: google.protobuf.FeatureSet.json_format:type_name -> google.protobuf.FeatureSet.JsonFormat + 46, // 61: google.protobuf.FeatureSetDefaults.defaults:type_name -> google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault + 0, // 62: google.protobuf.FeatureSetDefaults.minimum_edition:type_name -> google.protobuf.Edition + 0, // 63: google.protobuf.FeatureSetDefaults.maximum_edition:type_name -> google.protobuf.Edition + 47, // 64: google.protobuf.SourceCodeInfo.location:type_name -> google.protobuf.SourceCodeInfo.Location + 48, // 65: google.protobuf.GeneratedCodeInfo.annotation:type_name -> google.protobuf.GeneratedCodeInfo.Annotation + 20, // 66: google.protobuf.DescriptorProto.ExtensionRange.options:type_name -> google.protobuf.ExtensionRangeOptions + 0, // 67: google.protobuf.FieldOptions.EditionDefault.edition:type_name -> google.protobuf.Edition + 0, // 68: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition:type_name -> google.protobuf.Edition + 36, // 69: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.features:type_name -> google.protobuf.FeatureSet + 16, // 70: google.protobuf.GeneratedCodeInfo.Annotation.semantic:type_name -> google.protobuf.GeneratedCodeInfo.Annotation.Semantic + 71, // [71:71] is the sub-list for method output_type + 71, // [71:71] is the sub-list for method input_type + 71, // [71:71] is the sub-list for extension type_name + 71, // [71:71] is the sub-list for extension extendee + 0, // [0:71] is the sub-list for field type_name } func init() { file_google_protobuf_descriptor_proto_init() } @@ -4232,19 +5468,21 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SourceCodeInfo); i { + switch v := v.(*FeatureSet); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields + case 3: + return &v.extensionFields default: return nil } } file_google_protobuf_descriptor_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GeneratedCodeInfo); i { + switch v := v.(*FeatureSetDefaults); i { case 0: return &v.state case 1: @@ -4256,7 +5494,7 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DescriptorProto_ExtensionRange); i { + switch v := v.(*SourceCodeInfo); i { case 0: return &v.state case 1: @@ -4268,7 +5506,7 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DescriptorProto_ReservedRange); i { + switch v := v.(*GeneratedCodeInfo); i { case 0: return &v.state case 1: @@ -4280,7 +5518,7 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EnumDescriptorProto_EnumReservedRange); i { + switch v := v.(*DescriptorProto_ExtensionRange); i { case 0: return &v.state case 1: @@ -4292,7 +5530,7 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UninterpretedOption_NamePart); i { + switch v := v.(*DescriptorProto_ReservedRange); i { case 0: return &v.state case 1: @@ -4304,7 +5542,7 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SourceCodeInfo_Location); i { + switch v := v.(*ExtensionRangeOptions_Declaration); i { case 0: return &v.state case 1: @@ -4316,6 +5554,66 @@ func file_google_protobuf_descriptor_proto_init() { } } file_google_protobuf_descriptor_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnumDescriptorProto_EnumReservedRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FieldOptions_EditionDefault); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UninterpretedOption_NamePart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FeatureSetDefaults_FeatureSetEditionDefault); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SourceCodeInfo_Location); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GeneratedCodeInfo_Annotation); i { case 0: return &v.state @@ -4333,8 +5631,8 @@ func file_google_protobuf_descriptor_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_google_protobuf_descriptor_proto_rawDesc, - NumEnums: 9, - NumMessages: 27, + NumEnums: 17, + NumMessages: 32, NumExtensions: 0, NumServices: 0, }, diff --git a/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go b/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go new file mode 100644 index 000000000..25de5ae00 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go @@ -0,0 +1,177 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: reflect/protodesc/proto/go_features.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" +) + +type GoFeatures struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Whether or not to generate the deprecated UnmarshalJSON method for enums. + LegacyUnmarshalJsonEnum *bool `protobuf:"varint,1,opt,name=legacy_unmarshal_json_enum,json=legacyUnmarshalJsonEnum" json:"legacy_unmarshal_json_enum,omitempty"` +} + +func (x *GoFeatures) Reset() { + *x = GoFeatures{} + if protoimpl.UnsafeEnabled { + mi := &file_reflect_protodesc_proto_go_features_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GoFeatures) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoFeatures) ProtoMessage() {} + +func (x *GoFeatures) ProtoReflect() protoreflect.Message { + mi := &file_reflect_protodesc_proto_go_features_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoFeatures.ProtoReflect.Descriptor instead. +func (*GoFeatures) Descriptor() ([]byte, []int) { + return file_reflect_protodesc_proto_go_features_proto_rawDescGZIP(), []int{0} +} + +func (x *GoFeatures) GetLegacyUnmarshalJsonEnum() bool { + if x != nil && x.LegacyUnmarshalJsonEnum != nil { + return *x.LegacyUnmarshalJsonEnum + } + return false +} + +var file_reflect_protodesc_proto_go_features_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.FeatureSet)(nil), + ExtensionType: (*GoFeatures)(nil), + Field: 1002, + Name: "google.protobuf.go", + Tag: "bytes,1002,opt,name=go", + Filename: "reflect/protodesc/proto/go_features.proto", + }, +} + +// Extension fields to descriptorpb.FeatureSet. +var ( + // optional google.protobuf.GoFeatures go = 1002; + E_Go = &file_reflect_protodesc_proto_go_features_proto_extTypes[0] +) + +var File_reflect_protodesc_proto_go_features_proto protoreflect.FileDescriptor + +var file_reflect_protodesc_proto_go_features_proto_rawDesc = []byte{ + 0x0a, 0x29, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x64, + 0x65, 0x73, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x20, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6a, + 0x0a, 0x0a, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x5c, 0x0a, 0x1a, + 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, + 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x42, 0x1f, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x74, 0x72, 0x75, + 0x65, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0a, 0x12, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x18, 0xe7, + 0x07, 0x52, 0x17, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, + 0x61, 0x6c, 0x4a, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x3a, 0x49, 0x0a, 0x02, 0x67, 0x6f, + 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x18, 0xea, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x52, 0x02, 0x67, 0x6f, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x64, 0x65, 0x73, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, +} + +var ( + file_reflect_protodesc_proto_go_features_proto_rawDescOnce sync.Once + file_reflect_protodesc_proto_go_features_proto_rawDescData = file_reflect_protodesc_proto_go_features_proto_rawDesc +) + +func file_reflect_protodesc_proto_go_features_proto_rawDescGZIP() []byte { + file_reflect_protodesc_proto_go_features_proto_rawDescOnce.Do(func() { + file_reflect_protodesc_proto_go_features_proto_rawDescData = protoimpl.X.CompressGZIP(file_reflect_protodesc_proto_go_features_proto_rawDescData) + }) + return file_reflect_protodesc_proto_go_features_proto_rawDescData +} + +var file_reflect_protodesc_proto_go_features_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_reflect_protodesc_proto_go_features_proto_goTypes = []interface{}{ + (*GoFeatures)(nil), // 0: google.protobuf.GoFeatures + (*descriptorpb.FeatureSet)(nil), // 1: google.protobuf.FeatureSet +} +var file_reflect_protodesc_proto_go_features_proto_depIdxs = []int32{ + 1, // 0: google.protobuf.go:extendee -> google.protobuf.FeatureSet + 0, // 1: google.protobuf.go:type_name -> google.protobuf.GoFeatures + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 1, // [1:2] is the sub-list for extension type_name + 0, // [0:1] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_reflect_protodesc_proto_go_features_proto_init() } +func file_reflect_protodesc_proto_go_features_proto_init() { + if File_reflect_protodesc_proto_go_features_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_reflect_protodesc_proto_go_features_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GoFeatures); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_reflect_protodesc_proto_go_features_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 1, + NumServices: 0, + }, + GoTypes: file_reflect_protodesc_proto_go_features_proto_goTypes, + DependencyIndexes: file_reflect_protodesc_proto_go_features_proto_depIdxs, + MessageInfos: file_reflect_protodesc_proto_go_features_proto_msgTypes, + ExtensionInfos: file_reflect_protodesc_proto_go_features_proto_extTypes, + }.Build() + File_reflect_protodesc_proto_go_features_proto = out.File + file_reflect_protodesc_proto_go_features_proto_rawDesc = nil + file_reflect_protodesc_proto_go_features_proto_goTypes = nil + file_reflect_protodesc_proto_go_features_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto b/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto new file mode 100644 index 000000000..d24657129 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto @@ -0,0 +1,28 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +syntax = "proto2"; + +package google.protobuf; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/protobuf/types/gofeaturespb"; + +extend google.protobuf.FeatureSet { + optional GoFeatures go = 1002; +} + +message GoFeatures { + // Whether or not to generate the deprecated UnmarshalJSON method for enums. + optional bool legacy_unmarshal_json_enum = 1 [ + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + edition_defaults = { edition: EDITION_PROTO2, value: "true" }, + edition_defaults = { edition: EDITION_PROTO3, value: "false" } + ]; +} diff --git a/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go b/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go index a6c7a33f3..9de51be54 100644 --- a/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go +++ b/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go @@ -142,39 +142,39 @@ import ( // // Example 2: Pack and unpack a message in Java. // -// Foo foo = ...; -// Any any = Any.pack(foo); -// ... -// if (any.is(Foo.class)) { -// foo = any.unpack(Foo.class); -// } -// // or ... -// if (any.isSameTypeAs(Foo.getDefaultInstance())) { -// foo = any.unpack(Foo.getDefaultInstance()); -// } -// -// Example 3: Pack and unpack a message in Python. -// -// foo = Foo(...) -// any = Any() -// any.Pack(foo) -// ... -// if any.Is(Foo.DESCRIPTOR): -// any.Unpack(foo) -// ... -// -// Example 4: Pack and unpack a message in Go -// -// foo := &pb.Foo{...} -// any, err := anypb.New(foo) -// if err != nil { -// ... -// } -// ... -// foo := &pb.Foo{} -// if err := any.UnmarshalTo(foo); err != nil { -// ... -// } +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } // // The pack methods provided by protobuf library will by default use // 'type.googleapis.com/full.type.name' as the type URL and the unpack @@ -182,8 +182,8 @@ import ( // in the type URL, for example "foo.bar.com/x/y.z" will yield type // name "y.z". // -// # JSON -// +// JSON +// ==== // The JSON representation of an `Any` value uses the regular // representation of the deserialized, embedded message, with an // additional field `@type` which contains the type URL. Example: @@ -237,7 +237,8 @@ type Any struct { // // Note: this functionality is not currently available in the official // protobuf release, and it is not used for type URLs beginning with - // type.googleapis.com. + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. diff --git a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go index 61f69fc11..81511a336 100644 --- a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go +++ b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go @@ -167,7 +167,7 @@ import ( // [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with // the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use // the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() // ) to obtain a formatter capable of generating timestamps in this format. type Timestamp struct { state protoimpl.MessageState diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-amd64/vendor.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/datastreams/options/options.go similarity index 61% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-amd64/vendor.go rename to vendor/gopkg.in/DataDog/dd-trace-go.v1/datastreams/options/options.go index 8ef856933..7b2d626ce 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-amd64/vendor.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/datastreams/options/options.go @@ -1,8 +1,10 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. +// Copyright 2016-present Datadog, Inc. -// Package vendor is required to help go tools support vendoring. -// DO NOT REMOVE -package vendor +package options + +type CheckpointParams struct { + PayloadSize int64 +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go index 06a1732fc..e311b5ff2 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go @@ -16,8 +16,23 @@ package ddtrace // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" import ( "context" "time" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" ) +// SpanContextW3C represents a SpanContext with an additional method to allow +// access of the 128-bit trace id of the span, if present. +type SpanContextW3C interface { + SpanContext + + // TraceID128 returns the hex-encoded 128-bit trace ID that this context is carrying. + // The string will be exactly 32 bytes and may include leading zeroes. + TraceID128() string + + // TraceID128 returns the raw bytes of the 128-bit trace ID that this context is carrying. + TraceID128Bytes() [16]byte +} + // Tracer specifies an implementation of the Datadog tracer which allows starting // and propagating spans. The official implementation if exposed as functions // within the "tracer" package. @@ -79,6 +94,25 @@ type SpanContext interface { ForeachBaggageItem(handler func(k, v string) bool) } +// SpanLink represents a reference to a span that exists outside of the trace. +// +//go:generate msgp -unexported -marshal=false -o=span_link_msgp.go -tests=false + +type SpanLink struct { + // TraceID represents the low 64 bits of the linked span's trace id. This field is required. + TraceID uint64 `msg:"trace_id" json:"trace_id"` + // TraceIDHigh represents the high 64 bits of the linked span's trace id. This field is only set if the linked span's trace id is 128 bits. + TraceIDHigh uint64 `msg:"trace_id_high,omitempty" json:"trace_id_high"` + // SpanID represents the linked span's span id. + SpanID uint64 `msg:"span_id" json:"span_id"` + // Attributes is a mapping of keys to string values. These values are used to add additional context to the span link. + Attributes map[string]string `msg:"attributes,omitempty" json:"attributes"` + // Tracestate is the tracestate of the linked span. This field is optional. + Tracestate string `msg:"tracestate,omitempty" json:"tracestate"` + // Flags represents the W3C trace flags of the linked span. This field is optional. + Flags uint32 `msg:"flags,omitempty" json:"flags"` +} + // StartSpanOption is a configuration option that can be used with a Tracer's StartSpan method. type StartSpanOption func(cfg *StartSpanConfig) @@ -122,16 +156,25 @@ type StartSpanConfig struct { // new span. Tags map[string]interface{} - // Force-set the SpanID, rather than use a random number. If no Parent SpanContext is present, - // then this will also set the TraceID to the same value. + // SpanID will be the SpanID of the Span, overriding the random number that would + // be generated. If no Parent SpanContext is present, then this will also set the + // TraceID to the same value. SpanID uint64 // Context is the parent context where the span should be stored. Context context.Context + + // SpanLink represents a causal relationship between two spans. A span can have multiple links. + SpanLinks []SpanLink } -// Logger implementations are able to log given messages that the tracer might output. +// Logger implementations are able to log given messages that the tracer or profiler might output. type Logger interface { // Log prints the given message. Log(msg string) } + +// UseLogger sets l as the logger for all tracer and profiler logs. +func UseLogger(l Logger) { + log.UseLogger(l) +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/cassandra.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/cassandra.go deleted file mode 100644 index 5660a17fc..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/cassandra.go +++ /dev/null @@ -1,29 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -package ext - -const ( - // CassandraQuery is the tag name used for cassandra queries. - CassandraQuery = "cassandra.query" - - // CassandraBatch is the tag name used for cassandra batches. - CassandraBatch = "cassandra.batch" - - // CassandraConsistencyLevel is the tag name to set for consitency level. - CassandraConsistencyLevel = "cassandra.consistency_level" - - // CassandraCluster specifies the tag name that is used to set the cluster. - CassandraCluster = "cassandra.cluster" - - // CassandraRowCount specifies the tag name to use when settings the row count. - CassandraRowCount = "cassandra.row_count" - - // CassandraKeyspace is used as tag name for setting the key space. - CassandraKeyspace = "cassandra.keyspace" - - // CassandraPaginated specifies the tag name for paginated queries. - CassandraPaginated = "cassandra.paginated" -) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go index 779dd24ec..8074342d1 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/db.go @@ -18,4 +18,70 @@ const ( DBUser = "db.user" // DBStatement records a database statement for the given database type. DBStatement = "db.statement" + // DBSystem indicates the database management system (DBMS) product being used. + DBSystem = "db.system" +) + +// Available values for db.system. +const ( + DBSystemMemcached = "memcached" + DBSystemMySQL = "mysql" + DBSystemPostgreSQL = "postgresql" + DBSystemMicrosoftSQLServer = "mssql" + // DBSystemOtherSQL is used for other SQL databases not listed above. + DBSystemOtherSQL = "other_sql" + DBSystemElasticsearch = "elasticsearch" + DBSystemRedis = "redis" + DBSystemMongoDB = "mongodb" + DBSystemCassandra = "cassandra" + DBSystemConsulKV = "consul" + DBSystemLevelDB = "leveldb" + DBSystemBuntDB = "buntdb" +) + +// MicrosoftSQLServer tags. +const ( + // MicrosoftSQLServerInstanceName indicates the Microsoft SQL Server instance name connecting to. + MicrosoftSQLServerInstanceName = "db.mssql.instance_name" +) + +// MongoDB tags. +const ( + // MongoDBCollection indicates the collection being accessed. + MongoDBCollection = "db.mongodb.collection" +) + +// Redis tags. +const ( + // RedisDatabaseIndex indicates the Redis database index connected to. + RedisDatabaseIndex = "db.redis.database_index" +) + +// Cassandra tags. +const ( + // CassandraQuery is the tag name used for cassandra queries. + // Deprecated: this value is no longer used internally and will be removed in future versions. + CassandraQuery = "cassandra.query" + + // CassandraBatch is the tag name used for cassandra batches. + // Deprecated: this value is no longer used internally and will be removed in future versions. + CassandraBatch = "cassandra.batch" + + // CassandraConsistencyLevel is the tag name to set for consitency level. + CassandraConsistencyLevel = "cassandra.consistency_level" + + // CassandraCluster specifies the tag name that is used to set the cluster. + CassandraCluster = "cassandra.cluster" + + // CassandraRowCount specifies the tag name to use when settings the row count. + CassandraRowCount = "cassandra.row_count" + + // CassandraKeyspace is used as tag name for setting the key space. + CassandraKeyspace = "cassandra.keyspace" + + // CassandraPaginated specifies the tag name for paginated queries. + CassandraPaginated = "cassandra.paginated" + + // CassandraContactPoints holds the list of cassandra initial seed nodes used to discover the cluster. + CassandraContactPoints = "db.cassandra.contact.points" ) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/messaging.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/messaging.go new file mode 100644 index 000000000..553cfaa69 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/messaging.go @@ -0,0 +1,25 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023 Datadog, Inc. + +package ext + +const ( + // MessagingSystem identifies which messaging system created this span (kafka, rabbitmq, amazonsqs, googlepubsub...) + MessagingSystem = "messaging.system" +) + +// Available values for messaging.system. +const ( + MessagingSystemGCPPubsub = "googlepubsub" + MessagingSystemKafka = "kafka" +) + +// Kafka tags. +const ( + // MessagingKafkaPartition defines the Kafka partition the trace is associated with. + MessagingKafkaPartition = "messaging.kafka.partition" + // KafkaBootstrapServers holds a comma separated list of bootstrap servers as defined in producer or consumer config. + KafkaBootstrapServers = "messaging.kafka.bootstrap.servers" +) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go index 9c629cfd6..f9909cb8b 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/peer.go @@ -13,6 +13,7 @@ const ( // PeerService records the service name of the peer service. PeerService = "peer.service" // PeerHostname records the host name of the peer. + // Deprecated: Use NetworkDestinationName instead for hostname and NetworkDestinationIP for IP addresses PeerHostname = "peer.hostname" // PeerPort records the port number of the peer. PeerPort = "peer.port" diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/rpc.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/rpc.go new file mode 100644 index 000000000..e7c430822 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/rpc.go @@ -0,0 +1,34 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023 Datadog, Inc. + +package ext + +const ( + // RPCSystem identifies the RPC remoting system. + RPCSystem = "rpc.system" + // RPCService represents the full (logical) name of the service being called, including its package name, + // if applicable. Note this is the logical name of the service from the RPC interface perspective, + // which can be different from the name of any implementing class. + RPCService = "rpc.service" + // RPCMethod represents the name of the (logical) method being called. Note this is the logical name of the + // method from the RPC interface perspective, which can be different from the name of + // any implementing method/function. + RPCMethod = "rpc.method" +) + +// Well-known identifiers for rpc.system. +const ( + // RPCSystemGRPC identifies gRPC. + RPCSystemGRPC = "grpc" + // RPCSystemTwirp identifies Twirp. + RPCSystemTwirp = "twirp" +) + +// gRPC specific tags. +const ( + // GRPCFullMethod represents the full name of the logical method being called following the + // format: /$package.$service/$method + GRPCFullMethod = "rpc.grpc.full_method" +) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/span_kind.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/span_kind.go new file mode 100644 index 000000000..71a3ce50b --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/span_kind.go @@ -0,0 +1,32 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package ext + +// span_kind values are set per span following the opentelemetry standard +// falls under the values of client, server, producer, consumer, and internal +const ( + + // SpanKindServer indicates that the span covers server-side handling of a synchronous RPC or other remote request + // This span should not have any local parents but can have other distributed parents + SpanKindServer = "server" + + // SpanKindClient indicates that the span describes a request to some remote service. + // This span should not have any local children but can have other distributed children + SpanKindClient = "client" + + // SpanKindConsumer indicates that the span describes the initiators of an asynchronous request. + // This span should not have any local parents but can have other distributed parents + SpanKindConsumer = "consumer" + + // SpanKindProducer indicates that the span describes a child of an asynchronous producer request. + // This span should not have any local children but can have other distributed children + SpanKindProducer = "producer" + + // SpanKindInternal indicates that the span represents an internal operation within an application, + // as opposed to an operations with remote parents or children. + // This is the default value and not explicitly set to save memory + SpanKindInternal = "internal" +) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/system.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/system.go index de0579a2d..163720a4f 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/system.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/system.go @@ -8,5 +8,5 @@ package ext // Standard system metadata names const ( // The pid of the traced process - Pid = "system.pid" + Pid = "process_id" ) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go index 54d16ca57..375d7df7b 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go @@ -9,11 +9,22 @@ package ext const ( // TargetHost sets the target host address. + // Deprecated: Use NetworkDestinationName instead for hostname and NetworkDestinationIP for IP addresses TargetHost = "out.host" + // NetworkDestinationName is the remote hostname or similar where the outbound connection is being made to. + NetworkDestinationName = "network.destination.name" + + // NetworkDestinationIP is the remote address where the outbound connection is being made to. + NetworkDestinationIP = "network.destination.ip" + // TargetPort sets the target host port. + // Deprecated: Use NetworkDestinationPort instead. TargetPort = "out.port" + // NetworkDestinationPort is the remote port number of the outbound connection. + NetworkDestinationPort = "network.destination.port" + // SamplingPriority is the tag that marks the sampling priority of a span. // Deprecated in favor of ManualKeep and ManualDrop. SamplingPriority = "sampling.priority" @@ -68,7 +79,7 @@ const ( Error = "error" // ErrorMsg specifies the error message. - ErrorMsg = "error.msg" + ErrorMsg = "error.message" // ErrorType specifies the error type. ErrorType = "error.type" @@ -100,4 +111,10 @@ const ( // RuntimeID is a tag that contains a unique id for this process. RuntimeID = "runtime-id" + + // Component defines library integration the span originated from. + Component = "component" + + // SpanKind defines the kind of span based on Otel requirements (client, server, producer, consumer). + SpanKind = "span.kind" ) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go index 239e97626..363d1f998 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal/globaltracer.go @@ -6,35 +6,33 @@ package internal // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" import ( - "sync" + "sync/atomic" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" ) var ( - mu sync.RWMutex // guards globalTracer - globalTracer ddtrace.Tracer = &NoopTracer{} + // globalTracer stores the current tracer as *ddtrace.Tracer (pointer to interface). The + // atomic.Value type requires types to be consistent, which requires using *ddtrace.Tracer. + globalTracer atomic.Value ) +func init() { + var tracer ddtrace.Tracer = &NoopTracer{} + globalTracer.Store(&tracer) +} + // SetGlobalTracer sets the global tracer to t. func SetGlobalTracer(t ddtrace.Tracer) { - mu.Lock() - old := globalTracer - globalTracer = t - // Unlock before potentially calling Stop, to allow any shutdown mechanism - // to retrieve the active tracer without causing a deadlock on mutex mu. - mu.Unlock() + old := *globalTracer.Swap(&t).(*ddtrace.Tracer) if !Testing { - // avoid infinite loop when calling (*mocktracer.Tracer).Stop old.Stop() } } // GetGlobalTracer returns the currently active tracer. func GetGlobalTracer() ddtrace.Tracer { - mu.RLock() - defer mu.RUnlock() - return globalTracer + return *globalTracer.Load().(*ddtrace.Tracer) } // Testing is set to true when the mock tracer is active. It usually signifies that we are in a test @@ -47,20 +45,20 @@ var _ ddtrace.Tracer = (*NoopTracer)(nil) type NoopTracer struct{} // StartSpan implements ddtrace.Tracer. -func (NoopTracer) StartSpan(operationName string, opts ...ddtrace.StartSpanOption) ddtrace.Span { +func (NoopTracer) StartSpan(_ string, _ ...ddtrace.StartSpanOption) ddtrace.Span { return NoopSpan{} } // SetServiceInfo implements ddtrace.Tracer. -func (NoopTracer) SetServiceInfo(name, app, appType string) {} +func (NoopTracer) SetServiceInfo(_, _, _ string) {} // Extract implements ddtrace.Tracer. -func (NoopTracer) Extract(carrier interface{}) (ddtrace.SpanContext, error) { +func (NoopTracer) Extract(_ interface{}) (ddtrace.SpanContext, error) { return NoopSpanContext{}, nil } // Inject implements ddtrace.Tracer. -func (NoopTracer) Inject(context ddtrace.SpanContext, carrier interface{}) error { return nil } +func (NoopTracer) Inject(_ ddtrace.SpanContext, _ interface{}) error { return nil } // Stop implements ddtrace.Tracer. func (NoopTracer) Stop() {} @@ -71,19 +69,19 @@ var _ ddtrace.Span = (*NoopSpan)(nil) type NoopSpan struct{} // SetTag implements ddtrace.Span. -func (NoopSpan) SetTag(key string, value interface{}) {} +func (NoopSpan) SetTag(_ string, _ interface{}) {} // SetOperationName implements ddtrace.Span. -func (NoopSpan) SetOperationName(operationName string) {} +func (NoopSpan) SetOperationName(_ string) {} // BaggageItem implements ddtrace.Span. -func (NoopSpan) BaggageItem(key string) string { return "" } +func (NoopSpan) BaggageItem(_ string) string { return "" } // SetBaggageItem implements ddtrace.Span. -func (NoopSpan) SetBaggageItem(key, val string) {} +func (NoopSpan) SetBaggageItem(_, _ string) {} // Finish implements ddtrace.Span. -func (NoopSpan) Finish(opts ...ddtrace.FinishOption) {} +func (NoopSpan) Finish(_ ...ddtrace.FinishOption) {} // Tracer implements ddtrace.Span. func (NoopSpan) Tracer() ddtrace.Tracer { return NoopTracer{} } @@ -103,4 +101,4 @@ func (NoopSpanContext) SpanID() uint64 { return 0 } func (NoopSpanContext) TraceID() uint64 { return 0 } // ForeachBaggageItem implements ddtrace.SpanContext. -func (NoopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} +func (NoopSpanContext) ForeachBaggageItem(_ func(k, v string) bool) {} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/span.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/span.go index ec55f4c38..82439f802 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/span.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/span.go @@ -24,12 +24,12 @@ type span struct { *opentracer } -func (s *span) Context() opentracing.SpanContext { return s.Span.Context() } -func (s *span) Finish() { s.Span.Finish() } -func (s *span) Tracer() opentracing.Tracer { return s.opentracer } -func (s *span) LogEvent(event string) { /* deprecated */ } -func (s *span) LogEventWithPayload(event string, payload interface{}) { /* deprecated */ } -func (s *span) Log(data opentracing.LogData) { /* deprecated */ } +func (s *span) Context() opentracing.SpanContext { return s.Span.Context() } +func (s *span) Finish() { s.Span.Finish() } +func (s *span) Tracer() opentracing.Tracer { return s.opentracer } +func (s *span) LogEvent(_ string) { /* deprecated */ } +func (s *span) LogEventWithPayload(_ string, _ interface{}) { /* deprecated */ } +func (s *span) Log(_ opentracing.LogData) { /* deprecated */ } func (s *span) FinishWithOptions(opts opentracing.FinishOptions) { for _, lr := range opts.LogRecords { diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/tracer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/tracer.go index 73e9e6741..d91191ebe 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/tracer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/tracer.go @@ -16,7 +16,8 @@ // Opentracing operation name is what is called resource in Datadog's terms and the Opentracing "component" // tag is Datadog's operation name. Meaning that in order to define (in Opentracing terms) a span that // has the operation name "/user/profile" and the component "http.request", one would do: -// opentracing.StartSpan("http.request", opentracer.ResourceName("/user/profile")) +// +// opentracing.StartSpan("http.request", opentracer.ResourceName("/user/profile")) // // Some libraries and frameworks are supported out-of-the-box by using our integrations. You can see a list // of supported integrations here: https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib. They are fully @@ -29,6 +30,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" opentracing "github.com/opentracing/opentracing-go" ) @@ -42,6 +44,8 @@ func New(opts ...tracer.StartOption) opentracing.Tracer { var _ opentracing.Tracer = (*opentracer)(nil) +var telemetryTags = []string{"integration_name:opentracing"} + // opentracer implements opentracing.Tracer on top of ddtrace.Tracer. type opentracer struct{ ddtrace.Tracer } @@ -63,6 +67,7 @@ func (t *opentracer) StartSpan(operationName string, options ...opentracing.Star for k, v := range sso.Tags { opts = append(opts, tracer.Tag(k, v)) } + telemetry.GlobalClient.Count(telemetry.NamespaceTracers, "spans_created", 1.0, telemetryTags, true) return &span{ Span: t.Tracer.StartSpan(operationName, opts...), opentracer: t, diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/span_link_msgp.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/span_link_msgp.go new file mode 100644 index 000000000..c2b90ec51 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/span_link_msgp.go @@ -0,0 +1,221 @@ +package ddtrace + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "github.com/tinylib/msgp/msgp" +) + +// DecodeMsg implements msgp.Decodable +func (z *SpanLink) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "trace_id": + z.TraceID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "TraceID") + return + } + case "trace_id_high": + z.TraceIDHigh, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "TraceIDHigh") + return + } + case "span_id": + z.SpanID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "SpanID") + return + } + case "attributes": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Attributes") + return + } + if z.Attributes == nil { + z.Attributes = make(map[string]string, zb0002) + } else if len(z.Attributes) > 0 { + for key := range z.Attributes { + delete(z.Attributes, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Attributes") + return + } + za0002, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Attributes", za0001) + return + } + z.Attributes[za0001] = za0002 + } + case "tracestate": + z.Tracestate, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Tracestate") + return + } + case "flags": + z.Flags, err = dc.ReadUint32() + if err != nil { + err = msgp.WrapError(err, "Flags") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *SpanLink) EncodeMsg(en *msgp.Writer) (err error) { + // omitempty: check for empty values + zb0001Len := uint32(6) + var zb0001Mask uint8 /* 6 bits */ + if z.TraceIDHigh == 0 { + zb0001Len-- + zb0001Mask |= 0x2 + } + if z.Attributes == nil { + zb0001Len-- + zb0001Mask |= 0x8 + } + if z.Tracestate == "" { + zb0001Len-- + zb0001Mask |= 0x10 + } + if z.Flags == 0 { + zb0001Len-- + zb0001Mask |= 0x20 + } + // variable map header, size zb0001Len + err = en.Append(0x80 | uint8(zb0001Len)) + if err != nil { + return + } + if zb0001Len == 0 { + return + } + // write "trace_id" + err = en.Append(0xa8, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.TraceID) + if err != nil { + err = msgp.WrapError(err, "TraceID") + return + } + if (zb0001Mask & 0x2) == 0 { // if not empty + // write "trace_id_high" + err = en.Append(0xad, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x68, 0x69, 0x67, 0x68) + if err != nil { + return + } + err = en.WriteUint64(z.TraceIDHigh) + if err != nil { + err = msgp.WrapError(err, "TraceIDHigh") + return + } + } + // write "span_id" + err = en.Append(0xa7, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.SpanID) + if err != nil { + err = msgp.WrapError(err, "SpanID") + return + } + if (zb0001Mask & 0x8) == 0 { // if not empty + // write "attributes" + err = en.Append(0xaa, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Attributes))) + if err != nil { + err = msgp.WrapError(err, "Attributes") + return + } + for za0001, za0002 := range z.Attributes { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Attributes") + return + } + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Attributes", za0001) + return + } + } + } + if (zb0001Mask & 0x10) == 0 { // if not empty + // write "tracestate" + err = en.Append(0xaa, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x74, 0x61, 0x74, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Tracestate) + if err != nil { + err = msgp.WrapError(err, "Tracestate") + return + } + } + if (zb0001Mask & 0x20) == 0 { // if not empty + // write "flags" + err = en.Append(0xa5, 0x66, 0x6c, 0x61, 0x67, 0x73) + if err != nil { + return + } + err = en.WriteUint32(z.Flags) + if err != nil { + err = msgp.WrapError(err, "Flags") + return + } + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *SpanLink) Msgsize() (s int) { + s = 1 + 9 + msgp.Uint64Size + 14 + msgp.Uint64Size + 8 + msgp.Uint64Size + 11 + msgp.MapHeaderSize + if z.Attributes != nil { + for za0001, za0002 := range z.Attributes { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + } + } + s += 11 + msgp.StringPrefixSize + len(z.Tracestate) + 6 + msgp.Uint32Size + return +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/abandonedspans.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/abandonedspans.go new file mode 100644 index 000000000..defad4183 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/abandonedspans.go @@ -0,0 +1,300 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +import ( + "container/list" + "fmt" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +var ( + tickerInterval = 1 * time.Minute + logSize = 9000 +) + +// bucket is a not thread-safe generic implementation of a dynamic collection of elements +// stored under a value-bound key (like time). Inspired by concentrator.rawBucket. +type bucket[K comparable, T any] struct { + start, duration uint64 + // index is a map of data's entries by aggregating value to avoid iterating data. + index map[K]*list.Element + // data is a list because insertion order may be important to users. + data *list.List +} + +func newBucket[K comparable, T any](btime uint64, bsize int64) *bucket[K, T] { + return &bucket[K, T]{ + start: btime, + duration: uint64(bsize), + index: make(map[K]*list.Element), + data: list.New(), + } +} + +func (b *bucket[K, T]) add(k K, v T) { + e := b.data.PushBack(v) + b.index[k] = e +} + +func (b *bucket[K, T]) get(k K) (T, bool) { + e, ok := b.index[k] + if !ok { + // Compiler trick to return any zero value in generic code. + // https://stackoverflow.com/a/70589302 + var zero T + return zero, ok + } + return e.Value.(T), ok +} + +func (b *bucket[K, T]) remove(k K) { + e, ok := b.index[k] + if !ok { + return + } + delete(b.index, k) + _ = b.data.Remove(e) +} + +func (b *bucket[K, T]) Len() int { + return b.data.Len() +} + +// abandonedSpanCandidate is a struct to store the minimum required information about +// spans that can be abandoned. +type abandonedSpanCandidate struct { + Name string + TraceID, SpanID uint64 + Start int64 + Finished bool +} + +func newAbandonedSpanCandidate(s *span, finished bool) *abandonedSpanCandidate { + // finished is explicit instead of implicit as s.finished may be not set + // at the moment of calling this method. + // Also, locking is not required as it's called while the span is already locked or it's + // being initialized. + return &abandonedSpanCandidate{ + Name: s.Name, + TraceID: s.TraceID, + SpanID: s.SpanID, + Start: s.Start, + Finished: finished, + } +} + +// String takes a span and returns a human-readable string representing that span. +func (s *abandonedSpanCandidate) String() string { + age := now() - s.Start + a := fmt.Sprintf("%d sec", age/1e9) + return fmt.Sprintf("[name: %s, span_id: %d, trace_id: %d, age: %s],", s.Name, s.SpanID, s.TraceID, a) +} + +type abandonedSpansDebugger struct { + // buckets holds all the potentially abandoned tracked spans sharded by the configured interval. + buckets map[int64]*bucket[uint64, *abandonedSpanCandidate] + + // In takes candidate spans and adds them to the debugger. + In chan *abandonedSpanCandidate + + // waits for any active goroutines + wg sync.WaitGroup + + // stop causes the debugger to shut down when closed. + stop chan struct{} + + // stopped reports whether the debugger is stopped (when non-zero). + stopped uint32 + + // addedSpans and removedSpans are internal counters, mainly for testing + // purposes + addedSpans, removedSpans uint32 +} + +// newAbandonedSpansDebugger creates a new abandonedSpansDebugger debugger +func newAbandonedSpansDebugger() *abandonedSpansDebugger { + d := &abandonedSpansDebugger{ + buckets: make(map[int64]*bucket[uint64, *abandonedSpanCandidate]), + In: make(chan *abandonedSpanCandidate, 10000), + } + atomic.SwapUint32(&d.stopped, 1) + return d +} + +// Start periodically finds and reports potentially abandoned spans that are older +// than the given interval. These spans are stored in a bucketed linked list, +// sorted by their `Start` time, where the front of the list contains the oldest spans, +// and the end of the list contains the newest spans. +func (d *abandonedSpansDebugger) Start(interval time.Duration) { + if atomic.SwapUint32(&d.stopped, 0) == 0 { + // already running + log.Warn("(*abandonedSpansDebugger).Start called more than once. This is likely a programming error.") + return + } + d.stop = make(chan struct{}) + d.wg.Add(1) + go func() { + defer d.wg.Done() + tick := time.NewTicker(tickerInterval) + defer tick.Stop() + d.runConsumer(tick, &interval) + }() +} + +func (d *abandonedSpansDebugger) runConsumer(tick *time.Ticker, interval *time.Duration) { + for { + select { + case <-tick.C: + d.log(interval) + case s := <-d.In: + if s.Finished { + d.remove(s, *interval) + } else { + d.add(s, *interval) + } + case <-d.stop: + return + } + } +} + +func (d *abandonedSpansDebugger) Stop() { + if d == nil { + return + } + if atomic.SwapUint32(&d.stopped, 1) > 0 { + return + } + close(d.stop) + d.wg.Wait() + d.log(nil) +} + +func (d *abandonedSpansDebugger) add(s *abandonedSpanCandidate, interval time.Duration) { + // Locking was considered in this method and remove method, but it's not required as long + // as these methods are called from the single goroutine responsible for debugging + // the abandoned spans. + bucketSize := interval.Nanoseconds() + btime := alignTs(s.Start, bucketSize) + b, ok := d.buckets[btime] + if !ok { + b = newBucket[uint64, *abandonedSpanCandidate](uint64(btime), bucketSize) + d.buckets[btime] = b + } + + b.add(s.SpanID, s) + atomic.AddUint32(&d.addedSpans, 1) +} + +func (d *abandonedSpansDebugger) remove(s *abandonedSpanCandidate, interval time.Duration) { + bucketSize := interval.Nanoseconds() + btime := alignTs(s.Start, bucketSize) + b, ok := d.buckets[btime] + if !ok { + return + } + // If a matching bucket exists, attempt to find the element containing + // the finished span, then remove that element from the bucket. + // If a bucket becomes empty, also remove that bucket from the + // abandoned spans list. + b.remove(s.SpanID) + atomic.AddUint32(&d.removedSpans, 1) + if b.Len() > 0 { + return + } + delete(d.buckets, btime) +} + +// log returns a string containing potentially abandoned spans. If `interval` is +// `nil`, it will print all unfinished spans. If `interval` holds a time.Duration, it will +// only print spans that are older than `interval`. It will also truncate the log message to +// `logSize` bytes to prevent overloading the logger. +func (d *abandonedSpansDebugger) log(interval *time.Duration) { + var ( + sb strings.Builder + spanCount = 0 + truncated = false + curTime = now() + ) + + if len(d.buckets) == 0 { + return + } + + // maps are iterated in random order, and to guarantee that is iterated in + // creation order, it's required to sort first the buckets' keys. + keys := make([]int64, 0, len(d.buckets)) + for k := range d.buckets { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + for _, k := range keys { + if truncated { + break + } + + // Since spans are bucketed by time, finding a bucket that is newer + // than the allowed time interval means that all spans in this bucket + // and future buckets will be younger than `interval`, and thus aren't + // worth checking. + b := d.buckets[k] + if interval != nil && curTime-int64(b.start) < interval.Nanoseconds() { + break + } + + msg, nSpans := formatAbandonedSpans(b, interval, curTime) + spanCount += nSpans + space := logSize - len(sb.String()) + if len(msg) > space { + msg = msg[0:space] + truncated = true + } + sb.WriteString(msg) + } + + if spanCount == 0 { + return + } + + log.Warn("%d abandoned spans:", spanCount) + if truncated { + log.Warn("Too many abandoned spans. Truncating message.") + sb.WriteString("...") + } + log.Warn(sb.String()) +} + +// formatAbandonedSpans takes a bucket and returns a human-readable string representing +// the contents of it. If `interval` is not nil, it will check if the bucket might +// contain spans older than the user configured timeout. If it does, it will filter for +// older spans. If not, it will print all spans without checking their duration. +func formatAbandonedSpans(b *bucket[uint64, *abandonedSpanCandidate], interval *time.Duration, curTime int64) (string, int) { + var ( + sb strings.Builder + spanCount int + ) + for e := b.data.Front(); e != nil; e = e.Next() { + s := e.Value.(*abandonedSpanCandidate) + // If `interval` is not nil, it will check if the span is older than the + // user configured timeout, and discard it if it is not. + if interval != nil && curTime-s.Start < interval.Nanoseconds() { + continue + } + spanCount++ + msg := s.String() + sb.WriteString(msg) + } + return sb.String(), spanCount +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/context.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/context.go index 0194b2cef..5698dea68 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/context.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/context.go @@ -9,16 +9,13 @@ import ( "context" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + traceinternal "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal" ) -type contextKey struct{} - -var activeSpanKey = contextKey{} - // ContextWithSpan returns a copy of the given context which includes the span s. func ContextWithSpan(ctx context.Context, s Span) context.Context { - return context.WithValue(ctx, activeSpanKey, s) + return context.WithValue(ctx, internal.ActiveSpanKey, s) } // SpanFromContext returns the span contained in the given context. A second return @@ -26,18 +23,18 @@ func ContextWithSpan(ctx context.Context, s Span) context.Context { // span is returned. func SpanFromContext(ctx context.Context) (Span, bool) { if ctx == nil { - return &internal.NoopSpan{}, false + return &traceinternal.NoopSpan{}, false } - v := ctx.Value(activeSpanKey) + v := ctx.Value(internal.ActiveSpanKey) if s, ok := v.(ddtrace.Span); ok { return s, true } - return &internal.NoopSpan{}, false + return &traceinternal.NoopSpan{}, false } // StartSpanFromContext returns a new span with the given operation name and options. If a span // is found in the context, it will be used as the parent of the resulting span. If the ChildOf -// option is passed, the span from context will take precedence over it as the parent span. +// option is passed, it will only be used as the parent if there is no span found in `ctx`. func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) { // copy opts in case the caller reuses the slice in parallel // we will add at least 1, at most 2 items diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/data_streams.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/data_streams.go new file mode 100644 index 000000000..92f59f1c4 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/data_streams.go @@ -0,0 +1,74 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +import ( + "context" + + "gopkg.in/DataDog/dd-trace-go.v1/datastreams/options" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + idatastreams "gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams" +) + +// dataStreamsContainer is an object that contains a data streams processor. +type dataStreamsContainer interface { + GetDataStreamsProcessor() *idatastreams.Processor +} + +// GetDataStreamsProcessor returns the processor tracking data streams stats +func (t *tracer) GetDataStreamsProcessor() *idatastreams.Processor { + return t.dataStreams +} + +// SetDataStreamsCheckpoint sets a consume or produce checkpoint in a Data Streams pathway. +// This enables tracking data flow & end to end latency. +// To learn more about the data streams product, see: https://docs.datadoghq.com/data_streams/go/ +func SetDataStreamsCheckpoint(ctx context.Context, edgeTags ...string) (outCtx context.Context, ok bool) { + return SetDataStreamsCheckpointWithParams(ctx, options.CheckpointParams{}, edgeTags...) +} + +// SetDataStreamsCheckpointWithParams sets a consume or produce checkpoint in a Data Streams pathway. +// This enables tracking data flow & end to end latency. +// To learn more about the data streams product, see: https://docs.datadoghq.com/data_streams/go/ +func SetDataStreamsCheckpointWithParams(ctx context.Context, params options.CheckpointParams, edgeTags ...string) (outCtx context.Context, ok bool) { + if t, ok := internal.GetGlobalTracer().(dataStreamsContainer); ok { + if processor := t.GetDataStreamsProcessor(); processor != nil { + outCtx = processor.SetCheckpointWithParams(ctx, params, edgeTags...) + return outCtx, true + } + } + return ctx, false +} + +// TrackKafkaCommitOffset should be used in the consumer, to track when it acks offset. +// if used together with TrackKafkaProduceOffset it can generate a Kafka lag in seconds metric. +func TrackKafkaCommitOffset(group, topic string, partition int32, offset int64) { + if t, ok := internal.GetGlobalTracer().(dataStreamsContainer); ok { + if p := t.GetDataStreamsProcessor(); p != nil { + p.TrackKafkaCommitOffset(group, topic, partition, offset) + } + } +} + +// TrackKafkaProduceOffset should be used in the producer, to track when it produces a message. +// if used together with TrackKafkaCommitOffset it can generate a Kafka lag in seconds metric. +func TrackKafkaProduceOffset(topic string, partition int32, offset int64) { + if t, ok := internal.GetGlobalTracer().(dataStreamsContainer); ok { + if p := t.GetDataStreamsProcessor(); p != nil { + p.TrackKafkaProduceOffset(topic, partition, offset) + } + } +} + +// TrackKafkaHighWatermarkOffset should be used in the producer, to track when it produces a message. +// if used together with TrackKafkaCommitOffset it can generate a Kafka lag in seconds metric. +func TrackKafkaHighWatermarkOffset(cluster string, topic string, partition int32, offset int64) { + if t, ok := internal.GetGlobalTracer().(dataStreamsContainer); ok { + if p := t.GetDataStreamsProcessor(); p != nil { + p.TrackKafkaHighWatermarkOffset(cluster, topic, partition, offset) + } + } +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/doc.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/doc.go index 71aaf19b2..cf9b4edc5 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/doc.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/doc.go @@ -9,22 +9,25 @@ // tracer, simply call the start method along with an optional set of options. // By default, the trace agent is considered to be found at "localhost:8126". In a // setup where this would be different (let's say 127.0.0.1:1234), we could do: -// tracer.Start(tracer.WithAgentAddr("127.0.0.1:1234")) -// defer tracer.Stop() +// +// tracer.Start(tracer.WithAgentAddr("127.0.0.1:1234")) +// defer tracer.Stop() // // The tracing client can perform trace sampling. While the trace agent // already samples traces to reduce bandwidth usage, client sampling reduces // performance overhead. To make use of it, the package comes with a ready-to-use // rate sampler that can be passed to the tracer. To use it and keep only 30% of the // requests, one would do: -// s := tracer.NewRateSampler(0.3) -// tracer.Start(tracer.WithSampler(s)) +// +// s := tracer.NewRateSampler(0.3) +// tracer.Start(tracer.WithSampler(s)) // // More precise control of sampling rates can be configured using sampling rules. // This can be applied based on span name, service or both, and is used to determine // the sampling rate to apply. MaxPerSecond specifies max number of spans per second // that can be sampled per the rule and applies only to sampling rules of type // tracer.SamplingRuleSpan. If MaxPerSecond is not specified, the default is no limit. +// // rules := []tracer.SamplingRule{ // // sample 10% of traces with the span name "web.request" // tracer.NameRule("web.request", 0.1), @@ -33,23 +36,26 @@ // // sample 30% of traces when the span name is "db.query" and the service // // is "postgres.db" // tracer.NameServiceRule("db.query", "postgres.db", 0.3), -// // sample 100% of traces when service and name match these regular expressions -// {Service: regexp.MustCompile("^test-"), Name: regexp.MustCompile("http\\..*"), Rate: 1.0}, -// // sample 50% of traces when service and name match these glob patterns with no limit on the number of spans -// tracer.SpanNameServiceRule("^test-", "http\\..*", 0.5), -// // sample 50% of traces when service and name match these glob patterns up to 100 spans per second -// tracer.SpanNameServiceMPSRule("^test-", "http\\..*", 0.5, 100), +// // sample 100% of traces when name and service match these regular expressions +// {Name: regexp.MustCompile("web\\..*"), Service: regexp.MustCompile("^test-"), Rate: 1.0}, +// // sample 50% of spans when service and name match these glob patterns with no limit on the number of spans +// tracer.SpanNameServiceRule("web.*", "test-*", 0.5), +// // sample 50% of spans when service and name match these glob patterns up to 100 spans per second +// tracer.SpanNameServiceMPSRule("web.*", "test-*", 0.5, 100), // } // tracer.Start(tracer.WithSamplingRules(rules)) // defer tracer.Stop() // // Sampling rules can also be configured at runtime using the DD_TRACE_SAMPLING_RULES and // DD_SPAN_SAMPLING_RULES environment variables. When set, it overrides rules set by tracer.WithSamplingRules. -// The value is a JSON array of objects. All rule objects must have a "sample_rate". -// For trace sampling rules the "name" and "service" fields are optional. -// For span sampling rules, at least one of the fields must be specified and must be a valid glob pattern, -// i.e. a string where "*" matches any contiguous substring, even the empty string, +// The value is a JSON array of objects. +// For trace sampling rules, the "sample_rate" field is required, the "name" and "service" fields are optional. +// For span sampling rules, the "name" and "service", if specified, must be a valid glob pattern, +// i.e. a string where "*" matches any contiguous substring, even an empty string, // and "?" character matches exactly one of any character. +// The "sample_rate" field is optional, and if not specified, defaults to "1.0", sampling 100% of the spans. +// The "max_per_second" field is optional, and if not specified, defaults to 0, keeping all the previously sampled spans. +// // export DD_TRACE_SAMPLING_RULES='[{"name": "web.request", "sample_rate": 1.0}]' // export DD_SPAN_SAMPLING_RULES='[{"service":"test.?","name": "web.*", "sample_rate": 1.0, "max_per_second":100}]' // @@ -79,16 +85,21 @@ // with our propagation algorithm as long as they implement the TextMapReader and TextMapWriter // interfaces. An example alternate implementation is the MDCarrier in our gRPC integration. // -// As an example, injecting a span's context into an HTTP request would look like this: -// req, err := http.NewRequest("GET", "http://example.com", nil) -// // ... -// err := tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header)) -// // ... -// http.DefaultClient.Do(req) +// As an example, injecting a span's context into an HTTP request would look like this. +// (See the net/http contrib package for more examples https://pkg.go.dev/gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http): +// +// req, err := http.NewRequest("GET", "http://example.com", nil) +// // ... +// err := tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header)) +// // ... +// http.DefaultClient.Do(req) +// // Then, on the server side, to continue the trace one would do: -// sctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(req.Header)) -// // ... -// span := tracer.StartSpan("child.span", tracer.ChildOf(sctx)) +// +// sctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(req.Header)) +// // ... +// span := tracer.StartSpan("child.span", tracer.ChildOf(sctx)) +// // In the same manner, any means can be used as a carrier to inject a context into a transport. Go's // context can also be used as a means to transport spans within the same process. The methods // StartSpanFromContext, ContextWithSpan and SpanFromContext exist for this reason. diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/dynamic_config.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/dynamic_config.go new file mode 100644 index 000000000..919b4f722 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/dynamic_config.go @@ -0,0 +1,117 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +import ( + "sync" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" +) + +// dynamicConfig is a thread-safe generic data structure to represent configuration fields. +// It's designed to satisfy the dynamic configuration semantics (i.e reset, update, apply configuration changes). +// This structure will be extended to track the origin of configuration values as well (e.g remote_config, env_var). +type dynamicConfig[T any] struct { + sync.RWMutex + current T // holds the current configuration value + startup T // holds the startup configuration value + cfgName string // holds the name of the configuration, has to be compatible with telemetry.Configuration.Name + cfgOrigin string // holds the origin of the current configuration value (currently only supports remote_config, empty otherwise) + apply func(T) bool // executes any config-specific operations to propagate the update properly, returns whether the update was applied + equal func(x, y T) bool // compares two configuration values, this is used to avoid unnecessary config and telemetry updates +} + +func newDynamicConfig[T any](name string, val T, apply func(T) bool, equal func(x, y T) bool) dynamicConfig[T] { + return dynamicConfig[T]{ + cfgName: name, + current: val, + startup: val, + apply: apply, + equal: equal, + } +} + +// get returns the current configuration value +func (dc *dynamicConfig[T]) get() T { + dc.RLock() + defer dc.RUnlock() + return dc.current +} + +// update applies a new configuration value +func (dc *dynamicConfig[T]) update(val T, origin string) bool { + dc.Lock() + defer dc.Unlock() + if dc.equal(dc.current, val) { + return false + } + dc.current = val + dc.cfgOrigin = origin + return dc.apply(val) +} + +// reset re-applies the startup configuration value +func (dc *dynamicConfig[T]) reset() bool { + dc.Lock() + defer dc.Unlock() + if dc.equal(dc.current, dc.startup) { + return false + } + dc.current = dc.startup + dc.cfgOrigin = "" + return dc.apply(dc.startup) +} + +// handleRC processes a new configuration value from remote config +// Returns whether the configuration value has been updated or not +func (dc *dynamicConfig[T]) handleRC(val *T) bool { + if val != nil { + return dc.update(*val, "remote_config") + } + return dc.reset() +} + +// toTelemetry returns the current configuration value as telemetry.Configuration +func (dc *dynamicConfig[T]) toTelemetry() telemetry.Configuration { + dc.RLock() + defer dc.RUnlock() + return telemetry.Sanitize(telemetry.Configuration{ + Name: dc.cfgName, + Value: dc.current, + Origin: dc.cfgOrigin, + }) +} + +func equal[T comparable](x, y T) bool { + return x == y +} + +// equalSlice compares two slices of comparable values +// The comparison takes into account the order of the elements +func equalSlice[T comparable](x, y []T) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if v != y[i] { + return false + } + } + return true +} + +// equalMap compares two maps of comparable keys and values +func equalMap[T comparable](x, y map[T]interface{}) bool { + if len(x) != len(y) { + return false + } + for k, v := range x { + if yv, ok := y[k]; !ok || yv != v { + return false + } + } + return true +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/log.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/log.go index 9bdd93c00..bf4b46b6d 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/log.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/log.go @@ -23,50 +23,56 @@ import ( // startupInfo contains various information about the status of the tracer on startup. type startupInfo struct { - Date string `json:"date"` // ISO 8601 date and time of start - OSName string `json:"os_name"` // Windows, Darwin, Debian, etc. - OSVersion string `json:"os_version"` // Version of the OS - Version string `json:"version"` // Tracer version - Lang string `json:"lang"` // "Go" - LangVersion string `json:"lang_version"` // Go version, e.g. go1.13 - Env string `json:"env"` // Tracer env - Service string `json:"service"` // Tracer Service - AgentURL string `json:"agent_url"` // The address of the agent - AgentError string `json:"agent_error"` // Any error that occurred trying to connect to agent - Debug bool `json:"debug"` // Whether debug mode is enabled - AnalyticsEnabled bool `json:"analytics_enabled"` // True if there is a global analytics rate set - SampleRate string `json:"sample_rate"` // The default sampling rate for the rules sampler - SampleRateLimit string `json:"sample_rate_limit"` // The rate limit configured with the rules sampler - SamplingRules []SamplingRule `json:"sampling_rules"` // Rules used by the rules sampler - SamplingRulesError string `json:"sampling_rules_error"` // Any errors that occurred while parsing sampling rules - ServiceMappings map[string]string `json:"service_mappings"` // Service Mappings - Tags map[string]string `json:"tags"` // Global tags - RuntimeMetricsEnabled bool `json:"runtime_metrics_enabled"` // Whether or not runtime metrics are enabled - HealthMetricsEnabled bool `json:"health_metrics_enabled"` // Whether or not health metrics are enabled - ProfilerCodeHotspotsEnabled bool `json:"profiler_code_hotspots_enabled"` // Whether or not profiler code hotspots are enabled - ProfilerEndpointsEnabled bool `json:"profiler_endpoints_enabled"` // Whether or not profiler endpoints are enabled - ApplicationVersion string `json:"dd_version"` // Version of the user's application - Architecture string `json:"architecture"` // Architecture of host machine - GlobalService string `json:"global_service"` // Global service string. If not-nil should be same as Service. (#614) - LambdaMode string `json:"lambda_mode"` // Whether or not the client has enabled lambda mode - AppSec bool `json:"appsec"` // AppSec status: true when started, false otherwise. - AgentFeatures agentFeatures `json:"agent_features"` // Lists the capabilities of the agent. + Date string `json:"date"` // ISO 8601 date and time of start + OSName string `json:"os_name"` // Windows, Darwin, Debian, etc. + OSVersion string `json:"os_version"` // Version of the OS + Version string `json:"version"` // Tracer version + Lang string `json:"lang"` // "Go" + LangVersion string `json:"lang_version"` // Go version, e.g. go1.13 + Env string `json:"env"` // Tracer env + Service string `json:"service"` // Tracer Service + AgentURL string `json:"agent_url"` // The address of the agent + AgentError string `json:"agent_error"` // Any error that occurred trying to connect to agent + Debug bool `json:"debug"` // Whether debug mode is enabled + AnalyticsEnabled bool `json:"analytics_enabled"` // True if there is a global analytics rate set + SampleRate string `json:"sample_rate"` // The default sampling rate for the rules sampler + SampleRateLimit string `json:"sample_rate_limit"` // The rate limit configured with the rules sampler + SamplingRules []SamplingRule `json:"sampling_rules"` // Rules used by the rules sampler + SamplingRulesError string `json:"sampling_rules_error"` // Any errors that occurred while parsing sampling rules + ServiceMappings map[string]string `json:"service_mappings"` // Service Mappings + Tags map[string]string `json:"tags"` // Global tags + RuntimeMetricsEnabled bool `json:"runtime_metrics_enabled"` // Whether runtime metrics are enabled + HealthMetricsEnabled bool `json:"health_metrics_enabled"` // Whether health metrics are enabled + ProfilerCodeHotspotsEnabled bool `json:"profiler_code_hotspots_enabled"` // Whether profiler code hotspots are enabled + ProfilerEndpointsEnabled bool `json:"profiler_endpoints_enabled"` // Whether profiler endpoints are enabled + ApplicationVersion string `json:"dd_version"` // Version of the user's application + Architecture string `json:"architecture"` // Architecture of host machine + GlobalService string `json:"global_service"` // Global service string. If not-nil should be same as Service. (#614) + LambdaMode string `json:"lambda_mode"` // Whether the client has enabled lambda mode + AppSec bool `json:"appsec"` // AppSec status: true when started, false otherwise. + AgentFeatures agentFeatures `json:"agent_features"` // Lists the capabilities of the agent. + Integrations map[string]integrationConfig `json:"integrations"` // Available tracer integrations + PartialFlushEnabled bool `json:"partial_flush_enabled"` // Whether Partial Flushing is enabled + PartialFlushMinSpans int `json:"partial_flush_min_spans"` // The min number of spans to trigger a partial flush + Orchestrion orchestrionConfig `json:"orchestrion"` // Orchestrion (auto-instrumentation) configuration. + FeatureFlags []string `json:"feature_flags"` } // checkEndpoint tries to connect to the URL specified by endpoint. // If the endpoint is not reachable, checkEndpoint returns an error // explaining why. -func checkEndpoint(endpoint string) error { +func checkEndpoint(c *http.Client, endpoint string) error { req, err := http.NewRequest("POST", endpoint, bytes.NewReader([]byte{0x90})) if err != nil { return fmt.Errorf("cannot create http request: %v", err) } req.Header.Set(traceCountHeader, "0") req.Header.Set("Content-Type", "application/msgpack") - _, err = defaultClient.Do(req) + res, err := c.Do(req) if err != nil { return err } + defer res.Body.Close() return nil } @@ -74,10 +80,15 @@ func checkEndpoint(endpoint string) error { // JSON format. func logStartup(t *tracer) { tags := make(map[string]string) - for k, v := range t.config.globalTags { + for k, v := range t.config.globalTags.get() { tags[k] = fmt.Sprintf("%v", v) } + featureFlags := make([]string, 0, len(t.config.featureFlags)) + for f := range t.config.featureFlags { + featureFlags = append(featureFlags, f) + } + info := startupInfo{ Date: time.Now().Format(time.RFC3339), OSName: osinfo.OSName(), @@ -104,7 +115,12 @@ func logStartup(t *tracer) { GlobalService: globalconfig.ServiceName(), LambdaMode: fmt.Sprintf("%t", t.config.logToStdout), AgentFeatures: t.config.agent, + Integrations: t.config.integrations, AppSec: appsec.Enabled(), + PartialFlushEnabled: t.config.partialFlushEnabled, + PartialFlushMinSpans: t.config.partialFlushMinSpans, + Orchestrion: t.config.orchestrionCfg, + FeatureFlags: featureFlags, } if _, _, err := samplingRulesFromEnv(); err != nil { info.SamplingRulesError = fmt.Sprintf("%s", err) @@ -113,7 +129,7 @@ func logStartup(t *tracer) { info.SampleRateLimit = fmt.Sprintf("%v", limit) } if !t.config.logToStdout { - if err := checkEndpoint(t.config.transport.endpoint()); err != nil { + if err := checkEndpoint(t.config.httpClient, t.config.transport.endpoint()); err != nil { info.AgentError = fmt.Sprintf("%s", err) log.Warn("DIAGNOSTICS Unable to reach agent intake: %s", err) } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/metrics.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/metrics.go index 8f1738d72..409d8a439 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/metrics.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/metrics.go @@ -18,14 +18,6 @@ import ( // be reported. const defaultMetricsReportInterval = 10 * time.Second -type statsdClient interface { - Incr(name string, tags []string, rate float64) error - Count(name string, value int64, tags []string, rate float64) error - Gauge(name string, value float64, tags []string, rate float64) error - Timing(name string, value time.Duration, tags []string, rate float64) error - Close() error -} - // reportRuntimeMetrics periodically reports go runtime metrics at // the given interval. func (t *tracer) reportRuntimeMetrics(interval time.Duration) { @@ -46,7 +38,7 @@ func (t *tracer) reportRuntimeMetrics(interval time.Duration) { runtime.ReadMemStats(&ms) debug.ReadGCStats(&gc) - statsd := t.config.statsd + statsd := t.statsd // CPU statistics statsd.Gauge("runtime.go.num_cpu", float64(runtime.NumCPU()), nil, 1) statsd.Gauge("runtime.go.num_goroutine", float64(runtime.NumGoroutine()), nil, 1) @@ -99,9 +91,9 @@ func (t *tracer) reportHealthMetrics(interval time.Duration) { for { select { case <-ticker.C: - t.config.statsd.Count("datadog.tracer.spans_started", atomic.SwapInt64(&t.spansStarted, 0), nil, 1) - t.config.statsd.Count("datadog.tracer.spans_finished", atomic.SwapInt64(&t.spansFinished, 0), nil, 1) - t.config.statsd.Count("datadog.tracer.traces_dropped", atomic.SwapInt64(&t.tracesDropped, 0), []string{"reason:trace_too_large"}, 1) + t.statsd.Count("datadog.tracer.spans_started", int64(atomic.SwapUint32(&t.spansStarted, 0)), nil, 1) + t.statsd.Count("datadog.tracer.spans_finished", int64(atomic.SwapUint32(&t.spansFinished, 0)), nil, 1) + t.statsd.Count("datadog.tracer.traces_dropped", int64(atomic.SwapUint32(&t.tracesDropped, 0)), []string{"reason:trace_too_large"}, 1) case <-t.stop: return } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go index df68ae422..7f974d614 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go @@ -12,9 +12,12 @@ import ( "math" "net" "net/http" + "net/url" "os" "path/filepath" + "regexp" "runtime" + "runtime/debug" "strconv" "strings" "time" @@ -24,12 +27,75 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" + "gopkg.in/DataDog/dd-trace-go.v1/internal/normalizer" "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof" "gopkg.in/DataDog/dd-trace-go.v1/internal/version" "github.com/DataDog/datadog-go/v5/statsd" ) +var contribIntegrations = map[string]struct { + name string // user readable name for startup logs + imported bool // true if the user has imported the integration +}{ + "github.com/99designs/gqlgen": {"gqlgen", false}, + "github.com/aws/aws-sdk-go": {"AWS SDK", false}, + "github.com/aws/aws-sdk-go-v2": {"AWS SDK v2", false}, + "github.com/bradfitz/gomemcache": {"Memcache", false}, + "cloud.google.com/go/pubsub.v1": {"Pub/Sub", false}, + "github.com/confluentinc/confluent-kafka-go": {"Kafka (confluent)", false}, + "github.com/confluentinc/confluent-kafka-go/v2": {"Kafka (confluent) v2", false}, + "database/sql": {"SQL", false}, + "github.com/dimfeld/httptreemux/v5": {"HTTP Treemux", false}, + "github.com/elastic/go-elasticsearch/v6": {"Elasticsearch v6", false}, + "github.com/emicklei/go-restful": {"go-restful", false}, + "github.com/emicklei/go-restful/v3": {"go-restful v3", false}, + "github.com/garyburd/redigo": {"Redigo (dep)", false}, + "github.com/gin-gonic/gin": {"Gin", false}, + "github.com/globalsign/mgo": {"MongoDB (mgo)", false}, + "github.com/go-chi/chi": {"chi", false}, + "github.com/go-chi/chi/v5": {"chi v5", false}, + "github.com/go-pg/pg/v10": {"go-pg v10", false}, + "github.com/go-redis/redis": {"Redis", false}, + "github.com/go-redis/redis/v7": {"Redis v7", false}, + "github.com/go-redis/redis/v8": {"Redis v8", false}, + "go.mongodb.org/mongo-driver": {"MongoDB", false}, + "github.com/gocql/gocql": {"Cassandra", false}, + "github.com/gofiber/fiber/v2": {"Fiber", false}, + "github.com/gomodule/redigo": {"Redigo", false}, + "google.golang.org/api": {"Google API", false}, + "google.golang.org/grpc": {"gRPC", false}, + "google.golang.org/grpc/v12": {"gRPC v12", false}, + "gopkg.in/jinzhu/gorm.v1": {"Gorm (gopkg)", false}, + "github.com/gorilla/mux": {"Gorilla Mux", false}, + "gorm.io/gorm.v1": {"Gorm v1", false}, + "github.com/graph-gophers/graphql-go": {"GraphQL", false}, + "github.com/hashicorp/consul/api": {"Consul", false}, + "github.com/hashicorp/vault/api": {"Vault", false}, + "github.com/jinzhu/gorm": {"Gorm", false}, + "github.com/jmoiron/sqlx": {"SQLx", false}, + "github.com/julienschmidt/httprouter": {"HTTP Router", false}, + "k8s.io/client-go/kubernetes": {"Kubernetes", false}, + "github.com/labstack/echo": {"echo", false}, + "github.com/labstack/echo/v4": {"echo v4", false}, + "github.com/miekg/dns": {"miekg/dns", false}, + "net/http": {"HTTP", false}, + "gopkg.in/olivere/elastic.v5": {"Elasticsearch v5", false}, + "gopkg.in/olivere/elastic.v3": {"Elasticsearch v3", false}, + "github.com/redis/go-redis/v9": {"Redis v9", false}, + "github.com/segmentio/kafka-go": {"Kafka v0", false}, + "github.com/IBM/sarama": {"IBM sarama", false}, + "github.com/Shopify/sarama": {"Shopify sarama", false}, + "github.com/sirupsen/logrus": {"Logrus", false}, + "github.com/syndtr/goleveldb": {"LevelDB", false}, + "github.com/tidwall/buntdb": {"BuntDB", false}, + "github.com/twitchtv/twirp": {"Twirp", false}, + "github.com/urfave/negroni": {"Negroni", false}, + "github.com/valyala/fasthttp": {"FastHTTP", false}, + "github.com/zenazn/goji": {"Goji", false}, +} + var ( // defaultSocketAPM specifies the socket path to use for connecting to the trace-agent. // Replaced in tests @@ -52,6 +118,10 @@ type config struct { // of the behaviour of the tracer. agent agentFeatures + // integrations reports if the user has instrumented a Datadog integration and + // if they have a version of the library available to integrate. + integrations map[string]integrationConfig + // featureFlags specifies any enabled feature flags. featureFlags map[string]struct{} @@ -59,6 +129,10 @@ type config struct { // output instead of using the agent. This is used in Lambda environments. logToStdout bool + // sendRetries is the number of times a trace payload send is retried upon + // failure. + sendRetries int + // logStartup, when true, causes various startup info to be written // when the tracer starts. logStartup bool @@ -79,16 +153,15 @@ type config struct { // sampler specifies the sampler that will be used for sampling traces. sampler Sampler - // agentAddr specifies the hostname and port of the agent where the traces - // are sent to. - agentAddr string + // agentURL is the agent URL that receives traces from the tracer. + agentURL *url.URL // serviceMappings holds a set of service mappings to dynamically rename services serviceMappings map[string]string // globalTags holds a set of tags that will be automatically applied to // all spans. - globalTags map[string]interface{} + globalTags dynamicConfig[map[string]interface{}] // transport specifies the Transport interface which will be used to send data to the agent. transport transport @@ -115,15 +188,16 @@ type config struct { // combination of the environment variables DD_AGENT_HOST and DD_DOGSTATSD_PORT. dogstatsdAddr string - // statsd is used for tracking metrics associated with the runtime and the tracer. - statsd statsdClient + // statsdClient is set when a user provides a custom statsd client for tracking metrics + // associated with the runtime and the tracer. + statsdClient internal.StatsdClient // spanRules contains user-defined rules to determine the sampling rate to apply - // to trace spans. + // to a single span without affecting the entire trace spanRules []SamplingRule // traceRules contains user-defined rules to determine the sampling rate to apply - // to individual spans. + // to the entire trace if any spans satisfy the criteria traceRules []SamplingRule // tickChan specifies a channel which will receive the time every time the tracer must flush. @@ -141,7 +215,66 @@ type config struct { profilerEndpoints bool // enabled reports whether tracing is enabled. - enabled bool + enabled dynamicConfig[bool] + + // enableHostnameDetection specifies whether the tracer should enable hostname detection. + enableHostnameDetection bool + + // spanAttributeSchemaVersion holds the selected DD_TRACE_SPAN_ATTRIBUTE_SCHEMA version. + spanAttributeSchemaVersion int + + // peerServiceDefaultsEnabled indicates whether the peer.service tag calculation is enabled or not. + peerServiceDefaultsEnabled bool + + // peerServiceMappings holds a set of service mappings to dynamically rename peer.service values. + peerServiceMappings map[string]string + + // debugAbandonedSpans controls if the tracer should log when old, open spans are found + debugAbandonedSpans bool + + // spanTimeout represents how old a span can be before it should be logged as a possible + // misconfiguration + spanTimeout time.Duration + + // partialFlushMinSpans is the number of finished spans in a single trace to trigger a + // partial flush, or 0 if partial flushing is disabled. + // Value from DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, default 1000. + partialFlushMinSpans int + + // partialFlushEnabled specifices whether the tracer should enable partial flushing. Value + // from DD_TRACE_PARTIAL_FLUSH_ENABLED, default false. + partialFlushEnabled bool + + // statsComputationEnabled enables client-side stats computation (aka trace metrics). + statsComputationEnabled bool + + // dataStreamsMonitoringEnabled specifies whether the tracer should enable monitoring of data streams + dataStreamsMonitoringEnabled bool + + // orchestrionCfg holds Orchestrion (aka auto-instrumentation) configuration. + // Only used for telemetry currently. + orchestrionCfg orchestrionConfig + + // traceSampleRate holds the trace sample rate. + traceSampleRate dynamicConfig[float64] + + // headerAsTags holds the header as tags configuration. + headerAsTags dynamicConfig[[]string] + + contribStats bool + + // dynamicInstrumentationEnabled controls if the target application can be modified by Dynamic Instrumentation or not. + // Value from DD_DYNAMIC_INSTRUMENTATION_ENABLED, default false. + dynamicInstrumentationEnabled bool +} + +// orchestrionConfig contains Orchestrion configuration. +type orchestrionConfig struct { + // Enabled indicates whether this tracer was instanciated via Orchestrion. + Enabled bool `json:"enabled"` + + // Metadata holds Orchestrion specific metadata (e.g orchestrion version, mode (toolexec or manual) etc..) + Metadata map[string]string `json:"metadata,omitempty"` } // HasFeature reports whether feature f is enabled. @@ -153,43 +286,17 @@ func (c *config) HasFeature(f string) bool { // StartOption represents a function that can be provided as a parameter to Start. type StartOption func(*config) -// forEachStringTag runs fn on every key:val pair encountered in str. -// str may contain multiple key:val pairs separated by either space -// or comma (but not a mixture of both). -func forEachStringTag(str string, fn func(key string, val string)) { - sep := " " - if strings.Index(str, ",") > -1 { - // falling back to comma as separator - sep = "," - } - for _, tag := range strings.Split(str, sep) { - tag = strings.TrimSpace(tag) - if tag == "" { - continue - } - kv := strings.SplitN(tag, ":", 2) - key := strings.TrimSpace(kv[0]) - if key == "" { - continue - } - var val string - if len(kv) == 2 { - val = strings.TrimSpace(kv[1]) - } - fn(key, val) - } -} - // maxPropagatedTagsLength limits the size of DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH to prevent HTTP 413 responses. const maxPropagatedTagsLength = 512 +// partialFlushMinSpansDefault is the default number of spans for partial flushing, if enabled. +const partialFlushMinSpansDefault = 1000 + // newConfig renders the tracer configuration based on defaults, environment variables // and passed user opts. func newConfig(opts ...StartOption) *config { c := new(config) c.sampler = NewAllSampler() - c.agentAddr = resolveAgentAddr() - c.httpClient = defaultHTTPClient() if internal.BoolEnv("DD_TRACE_ANALYTICS_ENABLED", false) { globalconfig.SetAnalyticsRate(1.0) @@ -220,10 +327,18 @@ func newConfig(opts ...StartOption) *config { c.version = ver } if v := os.Getenv("DD_SERVICE_MAPPING"); v != "" { - forEachStringTag(v, func(key, val string) { WithServiceMapping(key, val)(c) }) + internal.ForEachStringTag(v, func(key, val string) { WithServiceMapping(key, val)(c) }) + } + c.headerAsTags = newDynamicConfig("trace_header_tags", nil, setHeaderTags, equalSlice[string]) + if v := os.Getenv("DD_TRACE_HEADER_TAGS"); v != "" { + WithHeaderTags(strings.Split(v, ","))(c) } if v := os.Getenv("DD_TAGS"); v != "" { - forEachStringTag(v, func(key, val string) { WithGlobalTag(key, val)(c) }) + tags := internal.ParseTagString(v) + internal.CleanGitMetadataTags(tags) + for key, val := range tags { + WithGlobalTag(key, val)(c) + } } if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok { // AWS_LAMBDA_FUNCTION_NAME being set indicates that we're running in an AWS Lambda environment. @@ -231,42 +346,108 @@ func newConfig(opts ...StartOption) *config { c.logToStdout = true } c.logStartup = internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true) + c.contribStats = internal.BoolEnv("DD_TRACE_CONTRIB_STATS_ENABLED", true) c.runtimeMetrics = internal.BoolEnv("DD_RUNTIME_METRICS_ENABLED", false) c.debug = internal.BoolEnv("DD_TRACE_DEBUG", false) - c.enabled = internal.BoolEnv("DD_TRACE_ENABLED", true) + c.enabled = newDynamicConfig("tracing_enabled", internal.BoolEnv("DD_TRACE_ENABLED", true), func(b bool) bool { return true }, equal[bool]) c.profilerEndpoints = internal.BoolEnv(traceprof.EndpointEnvVar, true) c.profilerHotspots = internal.BoolEnv(traceprof.CodeHotspotsEnvVar, true) + c.enableHostnameDetection = internal.BoolEnv("DD_CLIENT_HOSTNAME_ENABLED", true) + c.debugAbandonedSpans = internal.BoolEnv("DD_TRACE_DEBUG_ABANDONED_SPANS", false) + if c.debugAbandonedSpans { + c.spanTimeout = internal.DurationEnv("DD_TRACE_ABANDONED_SPAN_TIMEOUT", 10*time.Minute) + } + c.statsComputationEnabled = internal.BoolEnv("DD_TRACE_STATS_COMPUTATION_ENABLED", false) + c.dataStreamsMonitoringEnabled = internal.BoolEnv("DD_DATA_STREAMS_ENABLED", false) + c.partialFlushEnabled = internal.BoolEnv("DD_TRACE_PARTIAL_FLUSH_ENABLED", false) + c.partialFlushMinSpans = internal.IntEnv("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", partialFlushMinSpansDefault) + if c.partialFlushMinSpans <= 0 { + log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is not a valid value, setting to default %d", c.partialFlushMinSpans, partialFlushMinSpansDefault) + c.partialFlushMinSpans = partialFlushMinSpansDefault + } else if c.partialFlushMinSpans >= traceMaxSize { + log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is above the max number of spans that can be kept in memory for a single trace (%d spans), so partial flushing will never trigger, setting to default %d", c.partialFlushMinSpans, traceMaxSize, partialFlushMinSpansDefault) + c.partialFlushMinSpans = partialFlushMinSpansDefault + } + // TODO(partialFlush): consider logging a warning if DD_TRACE_PARTIAL_FLUSH_MIN_SPANS + // is set, but DD_TRACE_PARTIAL_FLUSH_ENABLED is not true. Or just assume it should be enabled + // if it's explicitly set, and don't require both variables to be configured. + + c.dynamicInstrumentationEnabled = internal.BoolEnv("DD_DYNAMIC_INSTRUMENTATION_ENABLED", false) + + schemaVersionStr := os.Getenv("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA") + if v, ok := namingschema.ParseVersion(schemaVersionStr); ok { + namingschema.SetVersion(v) + c.spanAttributeSchemaVersion = int(v) + } else { + v := namingschema.SetDefaultVersion() + c.spanAttributeSchemaVersion = int(v) + log.Warn("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA=%s is not a valid value, setting to default of v%d", schemaVersionStr, v) + } + // Allow DD_TRACE_SPAN_ATTRIBUTE_SCHEMA=v0 users to disable default integration (contrib AKA v0) service names. + // These default service names are always disabled for v1 onwards. + namingschema.SetUseGlobalServiceName(internal.BoolEnv("DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED", false)) + + // peer.service tag default calculation is enabled by default if using attribute schema >= 1 + c.peerServiceDefaultsEnabled = true + if c.spanAttributeSchemaVersion == int(namingschema.SchemaV0) { + c.peerServiceDefaultsEnabled = internal.BoolEnv("DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED", false) + } + c.peerServiceMappings = make(map[string]string) + if v := os.Getenv("DD_TRACE_PEER_SERVICE_MAPPING"); v != "" { + internal.ForEachStringTag(v, func(key, val string) { c.peerServiceMappings[key] = val }) + } for _, fn := range opts { fn(c) } + if c.agentURL == nil { + c.agentURL = resolveAgentAddr() + if url := internal.AgentURLFromEnv(); url != nil { + c.agentURL = url + } + } + if c.agentURL.Scheme == "unix" { + // If we're connecting over UDS we can just rely on the agent to provide the hostname + log.Debug("connecting to agent over unix, do not set hostname on any traces") + c.enableHostnameDetection = false + c.httpClient = udsClient(c.agentURL.Path) + c.agentURL = &url.URL{ + Scheme: "http", + Host: fmt.Sprintf("UDS_%s", strings.NewReplacer(":", "_", "/", "_", `\`, "_").Replace(c.agentURL.Path)), + } + } else if c.httpClient == nil { + c.httpClient = defaultClient + } WithGlobalTag(ext.RuntimeID, globalconfig.RuntimeID())(c) + globalTags := c.globalTags.get() if c.env == "" { - if v, ok := c.globalTags["env"]; ok { + if v, ok := globalTags["env"]; ok { if e, ok := v.(string); ok { c.env = e } } } if c.version == "" { - if v, ok := c.globalTags["version"]; ok { + if v, ok := globalTags["version"]; ok { if ver, ok := v.(string); ok { c.version = ver } } } if c.serviceName == "" { - if v, ok := c.globalTags["service"]; ok { + if v, ok := globalTags["service"]; ok { if s, ok := v.(string); ok { c.serviceName = s globalconfig.SetServiceName(s) } } else { + // There is not an explicit service set, default to binary name. + // In this case, don't set a global service name so the contribs continue using their defaults. c.serviceName = filepath.Base(os.Args[0]) } } if c.transport == nil { - c.transport = newHTTPTransport(c.agentAddr, c.httpClient) + c.transport = newHTTPTransport(c.agentURL.String(), c.httpClient) } if c.propagator == nil { envKey := "DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH" @@ -289,8 +470,14 @@ func newConfig(opts ...StartOption) *config { if c.debug { log.SetLevel(log.LevelDebug) } - c.loadAgentFeatures() - if c.statsd == nil { + c.agent = loadAgentFeatures(c.logToStdout, c.agentURL, c.httpClient) + info, ok := debug.ReadBuildInfo() + if !ok { + c.loadContribIntegrations([]*debug.Module{}) + } else { + c.loadContribIntegrations(info.Deps) + } + if c.statsdClient == nil { // configure statsd client addr := c.dogstatsdAddr if addr == "" { @@ -311,17 +498,26 @@ func newConfig(opts ...StartOption) *config { // not a valid TCP address, leave it as it is (could be a socket connection) } c.dogstatsdAddr = addr - client, err := statsd.New(addr, statsd.WithMaxMessagesPerPayload(40), statsd.WithTags(statsTags(c))) - if err != nil { - log.Warn("Runtime and health metrics disabled: %v", err) - c.statsd = &statsd.NoOpClient{} - } else { - c.statsd = client - } } + // Re-initialize the globalTags config with the value constructed from the environment and start options + // This allows persisting the initial value of globalTags for future resets and updates. + c.initGlobalTags(c.globalTags.get()) + return c } +func newStatsdClient(c *config) (internal.StatsdClient, error) { + if c.statsdClient != nil { + return c.statsdClient, nil + } + + client, err := statsd.New(c.dogstatsdAddr, statsd.WithMaxMessagesPerPayload(40), statsd.WithTags(statsTags(c))) + if err != nil { + return &statsd.NoOpClient{}, err + } + return client, nil +} + // defaultHTTPClient returns the default http.Client to start the tracer with. func defaultHTTPClient() *http.Client { if _, err := os.Stat(defaultSocketAPM); err == nil { @@ -368,6 +564,12 @@ func defaultDogstatsdAddr() string { return net.JoinHostPort(host, port) } +type integrationConfig struct { + Instrumented bool `json:"instrumented"` // indicates if the user has imported and used the integration + Available bool `json:"available"` // indicates if the user is using a library that can be used with DataDog integrations + Version string `json:"available_version"` // if available, indicates the version of the library the user has +} + // agentFeatures holds information about the trace-agent's capabilities. // When running WithLambdaMode, a zero-value of this struct will be used // as features. @@ -380,6 +582,10 @@ type agentFeatures struct { // the /v0.6/stats endpoint. Stats bool + // DataStreams reports whether the agent can receive data streams stats on + // the /v0.1/pipeline_stats endpoint. + DataStreams bool + // StatsdPort specifies the Dogstatsd port as provided by the agent. // If it's the default, it will be 0, which means 8125. StatsdPort int @@ -396,13 +602,12 @@ func (a *agentFeatures) HasFlag(feat string) bool { // loadAgentFeatures queries the trace-agent for its capabilities and updates // the tracer's behaviour. -func (c *config) loadAgentFeatures() { - c.agent = agentFeatures{} - if c.logToStdout { +func loadAgentFeatures(logToStdout bool, agentURL *url.URL, httpClient *http.Client) (features agentFeatures) { + if logToStdout { // there is no agent; all features off return } - resp, err := c.httpClient.Get(fmt.Sprintf("http://%s/info", c.agentAddr)) + resp, err := httpClient.Get(fmt.Sprintf("%s/info", agentURL)) if err != nil { log.Error("Loading features: %v", err) return @@ -423,22 +628,74 @@ func (c *config) loadAgentFeatures() { log.Error("Decoding features: %v", err) return } - c.agent.DropP0s = info.ClientDropP0s - c.agent.StatsdPort = info.StatsdPort + features.DropP0s = info.ClientDropP0s + features.StatsdPort = info.StatsdPort for _, endpoint := range info.Endpoints { switch endpoint { case "/v0.6/stats": - c.agent.Stats = true + features.Stats = true + case "/v0.1/pipeline_stats": + features.DataStreams = true } } - c.agent.featureFlags = make(map[string]struct{}, len(info.FeatureFlags)) + features.featureFlags = make(map[string]struct{}, len(info.FeatureFlags)) for _, flag := range info.FeatureFlags { - c.agent.featureFlags[flag] = struct{}{} + features.featureFlags[flag] = struct{}{} } + return features +} + +// MarkIntegrationImported labels the given integration as imported +func MarkIntegrationImported(integration string) bool { + s, ok := contribIntegrations[integration] + if !ok { + return false + } + s.imported = true + contribIntegrations[integration] = s + return true +} + +func (c *config) loadContribIntegrations(deps []*debug.Module) { + integrations := map[string]integrationConfig{} + for _, s := range contribIntegrations { + integrations[s.name] = integrationConfig{ + Instrumented: s.imported, + } + } + for _, d := range deps { + p := d.Path + // special use case, since gRPC does not update version number + if p == "google.golang.org/grpc" { + re := regexp.MustCompile(`v(\d.\d)\d*`) + match := re.FindStringSubmatch(d.Version) + if match == nil { + log.Warn("Unable to parse version of GRPC %v", d.Version) + continue + } + ver, err := strconv.ParseFloat(match[1], 32) + if err != nil { + log.Warn("Unable to parse version of GRPC %v as a float", d.Version) + continue + } + if ver <= 1.2 { + p = p + "/v12" + } + } + s, ok := contribIntegrations[p] + if !ok { + continue + } + conf := integrations[s.name] + conf.Available = true + conf.Version = d.Version + integrations[s.name] = conf + } + c.integrations = integrations } func (c *config) canComputeStats() bool { - return c.agent.Stats && c.HasFeature("discovery") + return c.agent.Stats && (c.HasFeature("discovery") || c.statsComputationEnabled) } func (c *config) canDropP0s() bool { @@ -460,7 +717,7 @@ func statsTags(c *config) []string { if c.hostname != "" { tags = append(tags, "host:"+c.hostname) } - for k, v := range c.globalTags { + for k, v := range c.globalTags.get() { if vstr, ok := v.(string); ok { tags = append(tags, k+":"+vstr) } @@ -471,7 +728,7 @@ func statsTags(c *config) []string { // withNoopStats is used for testing to disable statsd client func withNoopStats() StartOption { return func(c *config) { - c.statsd = &statsd.NoOpClient{} + c.statsdClient = &statsd.NoOpClient{} } } @@ -491,6 +748,9 @@ func WithFeatureFlags(feats ...string) StartOption { } // WithLogger sets logger as the tracer's error printer. +// Diagnostic and startup tracer logs are prefixed to simplify the search within logs. +// If JSON logging format is required, it's possible to wrap tracer logs using an existing JSON logger with this +// function. To learn more about this possibility, please visit: https://github.com/DataDog/dd-trace-go/issues/2152#issuecomment-1790586933 func WithLogger(logger ddtrace.Logger) StartOption { return func(c *config) { c.logger = logger @@ -525,12 +785,23 @@ func WithDebugMode(enabled bool) StartOption { } // WithLambdaMode enables lambda mode on the tracer, for use with AWS Lambda. +// This option is only required if the the Datadog Lambda Extension is not +// running. func WithLambdaMode(enabled bool) StartOption { return func(c *config) { c.logToStdout = enabled } } +// WithSendRetries enables re-sending payloads that are not successfully +// submitted to the agent. This will cause the tracer to retry the send at +// most `retries` times. +func WithSendRetries(retries int) StartOption { + return func(c *config) { + c.sendRetries = retries + } +} + // WithPropagator sets an alternative propagator to be used by the tracer. func WithPropagator(p Propagator) StartOption { return func(c *config) { @@ -561,11 +832,22 @@ func WithService(name string) StartOption { } } +// WithGlobalServiceName causes contrib libraries to use the global service name and not any locally defined service name. +// This is synonymous with `DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED`. +func WithGlobalServiceName(enabled bool) StartOption { + return func(_ *config) { + namingschema.SetUseGlobalServiceName(enabled) + } +} + // WithAgentAddr sets the address where the agent is located. The default is // localhost:8126. It should contain both host and port. func WithAgentAddr(addr string) StartOption { return func(c *config) { - c.agentAddr = addr + c.agentURL = &url.URL{ + Scheme: "http", + Host: addr, + } } } @@ -588,17 +870,47 @@ func WithServiceMapping(from, to string) StartOption { } } +// WithPeerServiceDefaults sets default calculation for peer.service. +func WithPeerServiceDefaults(enabled bool) StartOption { + // TODO: add link to public docs + return func(c *config) { + c.peerServiceDefaultsEnabled = enabled + } +} + +// WithPeerServiceMapping determines the value of the peer.service tag "from" to be renamed to service "to". +func WithPeerServiceMapping(from, to string) StartOption { + return func(c *config) { + if c.peerServiceMappings == nil { + c.peerServiceMappings = make(map[string]string) + } + c.peerServiceMappings[from] = to + } +} + // WithGlobalTag sets a key/value pair which will be set as a tag on all spans // created by tracer. This option may be used multiple times. func WithGlobalTag(k string, v interface{}) StartOption { return func(c *config) { - if c.globalTags == nil { - c.globalTags = make(map[string]interface{}) + if c.globalTags.get() == nil { + c.initGlobalTags(map[string]interface{}{}) } - c.globalTags[k] = v + c.globalTags.Lock() + defer c.globalTags.Unlock() + c.globalTags.current[k] = v } } +// initGlobalTags initializes the globalTags config with the provided init value +func (c *config) initGlobalTags(init map[string]interface{}) { + apply := func(map[string]interface{}) bool { + // always set the runtime ID on updates + c.globalTags.current[ext.RuntimeID] = globalconfig.RuntimeID() + return true + } + c.globalTags = newDynamicConfig[map[string]interface{}]("trace_tags", init, apply, equalMap[string]) +} + // WithSampler sets the given sampler to be used with the tracer. By default // an all-permissive sampler is used. func WithSampler(s Sampler) StartOption { @@ -625,7 +937,12 @@ func WithHTTPClient(client *http.Client) StartOption { // WithUDS configures the HTTP client to dial the Datadog Agent via the specified Unix Domain Socket path. func WithUDS(socketPath string) StartOption { - return WithHTTPClient(udsClient(socketPath)) + return func(c *config) { + c.agentURL = &url.URL{ + Scheme: "unix", + Path: socketPath, + } + } } // WithAnalytics allows specifying whether Trace Search & Analytics should be enabled @@ -661,9 +978,10 @@ func WithRuntimeMetrics() StartOption { // WithDogstatsdAddress specifies the address to connect to for sending metrics to the Datadog // Agent. It should be a "host:port" string, or the path to a unix domain socket.If not set, it // attempts to determine the address of the statsd service according to the following rules: -// 1. Look for /var/run/datadog/dsd.socket and use it if present. IF NOT, continue to #2. -// 2. The host is determined by DD_AGENT_HOST, and defaults to "localhost" -// 3. The port is retrieved from the agent. If not present, it is determined by DD_DOGSTATSD_PORT, and defaults to 8125 +// 1. Look for /var/run/datadog/dsd.socket and use it if present. IF NOT, continue to #2. +// 2. The host is determined by DD_AGENT_HOST, and defaults to "localhost" +// 3. The port is retrieved from the agent. If not present, it is determined by DD_DOGSTATSD_PORT, and defaults to 8125 +// // This option is in effect when WithRuntimeMetrics is enabled. func WithDogstatsdAddress(addr string) StartOption { return func(cfg *config) { @@ -715,7 +1033,7 @@ func WithHostname(name string) StartOption { // WithTraceEnabled allows specifying whether tracing will be enabled func WithTraceEnabled(enabled bool) StartOption { return func(c *config) { - c.enabled = enabled + c.enabled = newDynamicConfig("tracing_enabled", enabled, func(b bool) bool { return true }, equal[bool]) } } @@ -751,6 +1069,55 @@ func WithProfilerEndpoints(enabled bool) StartOption { } } +// WithDebugSpansMode enables debugging old spans that may have been +// abandoned, which may prevent traces from being set to the Datadog +// Agent, especially if partial flushing is off. +// This setting can also be configured by setting DD_TRACE_DEBUG_ABANDONED_SPANS +// to true. The timeout will default to 10 minutes, unless overwritten +// by DD_TRACE_ABANDONED_SPAN_TIMEOUT. +// This feature is disabled by default. Turning on this debug mode may +// be expensive, so it should only be enabled for debugging purposes. +func WithDebugSpansMode(timeout time.Duration) StartOption { + return func(c *config) { + c.debugAbandonedSpans = true + c.spanTimeout = timeout + } +} + +// WithPartialFlushing enables flushing of partially finished traces. +// This is done after "numSpans" have finished in a single local trace at +// which point all finished spans in that trace will be flushed, freeing up +// any memory they were consuming. This can also be configured by setting +// DD_TRACE_PARTIAL_FLUSH_ENABLED to true, which will default to 1000 spans +// unless overriden with DD_TRACE_PARTIAL_FLUSH_MIN_SPANS. Partial flushing +// is disabled by default. +func WithPartialFlushing(numSpans int) StartOption { + return func(c *config) { + c.partialFlushEnabled = true + c.partialFlushMinSpans = numSpans + } +} + +// WithStatsComputation enables client-side stats computation, allowing +// the tracer to compute stats from traces. This can reduce network traffic +// to the Datadog Agent, and produce more accurate stats data. +// This can also be configured by setting DD_TRACE_STATS_COMPUTATION_ENABLED to true. +// Client-side stats is off by default. +func WithStatsComputation(enabled bool) StartOption { + return func(c *config) { + c.statsComputationEnabled = enabled + } +} + +// WithOrchestrion configures Orchestrion's auto-instrumentation metadata. +// This option is only intended to be used by Orchestrion https://github.com/DataDog/orchestrion +func WithOrchestrion(metadata map[string]string) StartOption { + return func(c *config) { + c.orchestrionCfg.Enabled = true + c.orchestrionCfg.Metadata = metadata + } +} + // StartSpanOption is a configuration option for StartSpan. It is aliased in order // to help godoc group all the functions returning it together. It is considered // more correct to refer to it as the type as the origin, ddtrace.StartSpanOption. @@ -783,6 +1150,13 @@ func SpanType(name string) StartSpanOption { return Tag(ext.SpanType, name) } +// WithSpanLinks sets span links on the started span. +func WithSpanLinks(links []ddtrace.SpanLink) StartSpanOption { + return func(cfg *ddtrace.StartSpanConfig) { + cfg.SpanLinks = append(cfg.SpanLinks, links...) + } +} + var measuredTag = Tag(keyMeasured, 1) // Measured marks this span to be measured for metrics and stats calculations. @@ -875,52 +1249,95 @@ func StackFrames(n, skip uint) FinishOption { } } +// WithHeaderTags enables the integration to attach HTTP request headers as span tags. +// Warning: +// Using this feature can risk exposing sensitive data such as authorization tokens to Datadog. +// Special headers can not be sub-selected. E.g., an entire Cookie header would be transmitted, without the ability to choose specific Cookies. +func WithHeaderTags(headerAsTags []string) StartOption { + return func(c *config) { + c.headerAsTags = newDynamicConfig("trace_header_tags", headerAsTags, setHeaderTags, equalSlice[string]) + setHeaderTags(headerAsTags) + } +} + +// setHeaderTags sets the global header tags. +// Always resets the global value and returns true. +func setHeaderTags(headerAsTags []string) bool { + globalconfig.ClearHeaderTags() + for _, h := range headerAsTags { + if strings.HasPrefix(h, "x-datadog-") { + continue + } + header, tag := normalizer.HeaderTag(h) + globalconfig.SetHeaderTag(header, tag) + } + return true +} + +// WithContribStats opens up a channel of communication between tracer and contrib libraries +// for submitting stats from contribs to Datadog via the tracer's statsd client +// It is enabled by default but can be disabled with `WithContribStats(false)` +func WithContribStats(enabled bool) StartOption { + return func(c *config) { + c.contribStats = enabled + } +} + // UserMonitoringConfig is used to configure what is used to identify a user. // This configuration can be set by combining one or several UserMonitoringOption with a call to SetUser(). type UserMonitoringConfig struct { - propagateID bool - email string - name string - role string - sessionID string - scope string + PropagateID bool + Email string + Name string + Role string + SessionID string + Scope string + Metadata map[string]string } // UserMonitoringOption represents a function that can be provided as a parameter to SetUser. type UserMonitoringOption func(*UserMonitoringConfig) +// WithUserMetadata returns the option setting additional metadata of the authenticated user. +// This can be used multiple times and the given data will be tracked as `usr.{key}=value`. +func WithUserMetadata(key, value string) UserMonitoringOption { + return func(cfg *UserMonitoringConfig) { + cfg.Metadata[key] = value + } +} + // WithUserEmail returns the option setting the email of the authenticated user. func WithUserEmail(email string) UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.email = email + cfg.Email = email } } // WithUserName returns the option setting the name of the authenticated user. func WithUserName(name string) UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.name = name + cfg.Name = name } } // WithUserSessionID returns the option setting the session ID of the authenticated user. func WithUserSessionID(sessionID string) UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.sessionID = sessionID + cfg.SessionID = sessionID } } // WithUserRole returns the option setting the role of the authenticated user. func WithUserRole(role string) UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.role = role + cfg.Role = role } } // WithUserScope returns the option setting the scope (authorizations) of the authenticated user. func WithUserScope(scope string) UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.scope = scope + cfg.Scope = scope } } @@ -930,6 +1347,6 @@ func WithUserScope(scope string) UserMonitoringOption { // personal identifiable information or any kind of sensitive data, as it will be leaked to other services. func WithPropagation() UserMonitoringOption { return func(cfg *UserMonitoringConfig) { - cfg.propagateID = true + cfg.PropagateID = true } } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/payload.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/payload.go index 2107ac063..8b08e8a71 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/payload.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/payload.go @@ -24,8 +24,22 @@ import ( // payload implements io.Reader and can be used with the decoder directly. To create // a new payload use the newPayload method. // -// payload is not safe for concurrent use, is meant to be used only once and eventually -// dismissed. +// payload is not safe for concurrent use. +// +// payload is meant to be used only once and eventually dismissed with the +// single exception of retrying failed flush attempts. +// +// âš ï¸ Warning! +// +// The payload should not be reused for multiple sets of traces. Resetting the +// payload for re-use requires the transport to wait for the HTTP package to +// Close the request body before attempting to re-use it again! This requires +// additional logic to be in place. See: +// +// • https://github.com/golang/go/blob/go1.16/src/net/http/client.go#L136-L138 +// • https://github.com/DataDog/dd-trace-go/pull/475 +// • https://github.com/DataDog/dd-trace-go/pull/549 +// • https://github.com/DataDog/dd-trace-go/pull/976 type payload struct { // header specifies the first few bytes in the msgpack stream // indicating the type of array (fixarray, array16 or array32) @@ -36,10 +50,13 @@ type payload struct { off int // count specifies the number of items in the stream. - count uint64 + count uint32 // buf holds the sequence of msgpack-encoded items. buf bytes.Buffer + + // reader is used for reading the contents of buf. + reader *bytes.Reader } var _ io.Reader = (*payload)(nil) @@ -55,17 +72,18 @@ func newPayload() *payload { // push pushes a new item into the stream. func (p *payload) push(t spanList) error { + p.buf.Grow(t.Msgsize()) if err := msgp.Encode(&p.buf, t); err != nil { return err } - atomic.AddUint64(&p.count, 1) + atomic.AddUint32(&p.count, 1) p.updateHeader() return nil } // itemCount returns the number of items available in the srteam. func (p *payload) itemCount() int { - return int(atomic.LoadUint64(&p.count)) + return int(atomic.LoadUint32(&p.count)) } // size returns the payload size in bytes. After the first read the value becomes @@ -74,22 +92,20 @@ func (p *payload) size() int { return p.buf.Len() + len(p.header) - p.off } -// reset should *not* be used. It is not implemented and is only here to serve -// as information on how to implement it in case the same payload object ever -// needs to be reused. +// reset sets up the payload to be read a second time. It maintains the +// underlying byte contents of the buffer. reset should not be used in order to +// reuse the payload for another set of traces. func (p *payload) reset() { - // âš ï¸ Warning! - // - // Resetting the payload for re-use requires the transport to wait for the - // HTTP package to Close the request body before attempting to re-use it - // again! This requires additional logic to be in place. See: - // - // • https://github.com/golang/go/blob/go1.16/src/net/http/client.go#L136-L138 - // • https://github.com/DataDog/dd-trace-go/pull/475 - // • https://github.com/DataDog/dd-trace-go/pull/549 - // • https://github.com/DataDog/dd-trace-go/pull/976 - // - panic("not implemented") + p.updateHeader() + if p.reader != nil { + p.reader.Seek(0, 0) + } +} + +// clear empties the payload buffers. +func (p *payload) clear() { + p.buf = bytes.Buffer{} + p.reader = nil } // https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family @@ -102,7 +118,7 @@ const ( // updateHeader updates the payload header based on the number of items currently // present in the stream. func (p *payload) updateHeader() { - n := atomic.LoadUint64(&p.count) + n := uint64(atomic.LoadUint32(&p.count)) switch { case n <= 15: p.header[7] = msgpackArrayFix + byte(n) @@ -120,10 +136,6 @@ func (p *payload) updateHeader() { // Close implements io.Closer func (p *payload) Close() error { - // Once the payload has been read, clear the buffer for garbage collection to avoid - // a memory leak when references to this object may still be kept by faulty transport - // implementations or the standard library. See dd-trace-go#976 - p.buf = bytes.Buffer{} return nil } @@ -135,5 +147,8 @@ func (p *payload) Read(b []byte) (n int, err error) { p.off += n return n, nil } - return p.buf.Read(b) + if p.reader == nil { + p.reader = bytes.NewReader(p.buf.Bytes()) + } + return p.reader.Read(b) } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagating_tags.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagating_tags.go new file mode 100644 index 000000000..0d5ddde1f --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagating_tags.go @@ -0,0 +1,68 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +func (t *trace) hasPropagatingTag(k string) bool { + t.mu.RLock() + defer t.mu.RUnlock() + _, ok := t.propagatingTags[k] + return ok +} + +func (t *trace) propagatingTag(k string) string { + t.mu.RLock() + defer t.mu.RUnlock() + return t.propagatingTags[k] +} + +// setPropagatingTag sets the key/value pair as a trace propagating tag. +func (t *trace) setPropagatingTag(key, value string) { + t.mu.Lock() + defer t.mu.Unlock() + t.setPropagatingTagLocked(key, value) +} + +// setPropagatingTagLocked sets the key/value pair as a trace propagating tag. +// Not safe for concurrent use, setPropagatingTag should be used instead in that case. +func (t *trace) setPropagatingTagLocked(key, value string) { + if t.propagatingTags == nil { + t.propagatingTags = make(map[string]string, 1) + } + t.propagatingTags[key] = value +} + +// unsetPropagatingTag deletes the key/value pair from the trace's propagated tags. +func (t *trace) unsetPropagatingTag(key string) { + t.mu.Lock() + defer t.mu.Unlock() + delete(t.propagatingTags, key) +} + +// iteratePropagatingTags allows safe iteration through the propagating tags of a trace. +// the trace must not be modified during this call, as it is locked for reading. +// +// f should return whether or not the iteration should continue. +func (t *trace) iteratePropagatingTags(f func(k, v string) bool) { + t.mu.RLock() + defer t.mu.RUnlock() + for k, v := range t.propagatingTags { + if !f(k, v) { + break + } + } +} + +func (t *trace) replacePropagatingTags(tags map[string]string) { + t.mu.Lock() + defer t.mu.Unlock() + t.propagatingTags = tags +} + +func (t *trace) propagatingTagsLen() int { + t.mu.RLock() + defer t.mu.RUnlock() + return len(t.propagatingTags) +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagator.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagator.go index 93c596227..d2ace0d27 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagator.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/propagator.go @@ -41,7 +41,7 @@ type TextMapReader interface { var ( // ErrInvalidCarrier is returned when the carrier provided to the propagator - // does not implemented the correct interfaces. + // does not implement the correct interfaces. ErrInvalidCarrier = errors.New("invalid carrier") // ErrInvalidSpanContext is returned when the span context found in the diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/remote_config.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/remote_config.go new file mode 100644 index 000000000..f3a8abd39 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/remote_config.go @@ -0,0 +1,224 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +import ( + "encoding/json" + "fmt" + "strings" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" + + "github.com/DataDog/datadog-agent/pkg/remoteconfig/state" +) + +type configData struct { + Action string `json:"action"` + ServiceTarget target `json:"service_target"` + LibConfig libConfig `json:"lib_config"` +} + +type target struct { + Service string `json:"service"` + Env string `json:"env"` +} + +type libConfig struct { + Enabled *bool `json:"tracing_enabled,omitempty"` + SamplingRate *float64 `json:"tracing_sampling_rate,omitempty"` + HeaderTags *headerTags `json:"tracing_header_tags,omitempty"` + Tags *tags `json:"tracing_tags,omitempty"` +} + +type headerTags []headerTag + +type headerTag struct { + Header string `json:"header"` + TagName string `json:"tag_name"` +} + +func (hts *headerTags) toSlice() *[]string { + if hts == nil { + return nil + } + s := make([]string, len(*hts)) + for i, ht := range *hts { + s[i] = ht.toString() + } + return &s +} + +func (ht headerTag) toString() string { + var sb strings.Builder + sb.WriteString(ht.Header) + sb.WriteString(":") + sb.WriteString(ht.TagName) + return sb.String() +} + +type tags []string + +func (t *tags) toMap() *map[string]interface{} { + if t == nil { + return nil + } + m := make(map[string]interface{}, len(*t)) + for _, tag := range *t { + if kv := strings.SplitN(tag, ":", 2); len(kv) == 2 { + m[kv[0]] = kv[1] + } + } + return &m +} + +func (t *tracer) dynamicInstrumentationRCUpdate(u remoteconfig.ProductUpdate) map[string]state.ApplyStatus { + + applyStatus := map[string]state.ApplyStatus{} + + for k, v := range u { + log.Debug("Received dynamic instrumentation RC configuration for %s\n", k) + applyStatus[k] = state.ApplyStatus{State: state.ApplyStateUnknown} + passFullConfiguration(k, string(v)) + } + + return applyStatus +} + +// passFullConfiguration is used as a stable interface to find the configuration in via bpf. Go-DI attaches +// a bpf program to this function and extracts the raw bytes accordingly. +// +//go:noinline +func passFullConfiguration(_, _ string) {} + +// onRemoteConfigUpdate is a remote config callaback responsible for processing APM_TRACING RC-product updates. +func (t *tracer) onRemoteConfigUpdate(u remoteconfig.ProductUpdate) map[string]state.ApplyStatus { + statuses := map[string]state.ApplyStatus{} + if len(u) == 0 { + return statuses + } + removed := func() bool { + // Returns true if all the values in the update are nil. + for _, raw := range u { + if raw != nil { + return false + } + } + return true + } + var telemConfigs []telemetry.Configuration + if removed() { + // The remote-config client is signaling that the configuration has been deleted for this product. + // We re-apply the startup configuration values. + for path := range u { + log.Debug("Nil payload from RC. Path: %s.", path) + statuses[path] = state.ApplyStatus{State: state.ApplyStateAcknowledged} + } + log.Debug("Resetting configurations") + updated := t.config.traceSampleRate.reset() + if updated { + telemConfigs = append(telemConfigs, t.config.traceSampleRate.toTelemetry()) + } + updated = t.config.headerAsTags.reset() + if updated { + telemConfigs = append(telemConfigs, t.config.headerAsTags.toTelemetry()) + } + updated = t.config.globalTags.reset() + if updated { + telemConfigs = append(telemConfigs, t.config.globalTags.toTelemetry()) + } + if !t.config.enabled.current { + log.Debug("APM Tracing is disabled. Restart the service to enable it.") + } + if len(telemConfigs) > 0 { + log.Debug("Reporting %d configuration changes to telemetry", len(telemConfigs)) + telemetry.GlobalClient.ConfigChange(telemConfigs) + } + return statuses + } + for path, raw := range u { + if raw == nil { + continue + } + log.Debug("Processing config from RC. Path: %s. Raw: %s", path, raw) + var c configData + if err := json.Unmarshal(raw, &c); err != nil { + log.Debug("Error while unmarshalling payload for %s: %v. Configuration won't be applied.", path, err) + statuses[path] = state.ApplyStatus{State: state.ApplyStateError, Error: err.Error()} + continue + } + if c.ServiceTarget.Service != t.config.serviceName { + log.Debug("Skipping config for service %s. Current service is %s", c.ServiceTarget.Service, t.config.serviceName) + statuses[path] = state.ApplyStatus{State: state.ApplyStateError, Error: "service mismatch"} + continue + } + if c.ServiceTarget.Env != t.config.env { + log.Debug("Skipping config for env %s. Current env is %s", c.ServiceTarget.Env, t.config.env) + statuses[path] = state.ApplyStatus{State: state.ApplyStateError, Error: "env mismatch"} + continue + } + statuses[path] = state.ApplyStatus{State: state.ApplyStateAcknowledged} + updated := t.config.traceSampleRate.handleRC(c.LibConfig.SamplingRate) + if updated { + telemConfigs = append(telemConfigs, t.config.traceSampleRate.toTelemetry()) + } + updated = t.config.headerAsTags.handleRC(c.LibConfig.HeaderTags.toSlice()) + if updated { + telemConfigs = append(telemConfigs, t.config.headerAsTags.toTelemetry()) + } + updated = t.config.globalTags.handleRC(c.LibConfig.Tags.toMap()) + if updated { + telemConfigs = append(telemConfigs, t.config.globalTags.toTelemetry()) + } + if c.LibConfig.Enabled != nil { + if t.config.enabled.current == true && *c.LibConfig.Enabled == false { + log.Debug("Disabled APM Tracing through RC. Restart the service to enable it.") + t.config.enabled.handleRC(c.LibConfig.Enabled) + telemConfigs = append(telemConfigs, t.config.enabled.toTelemetry()) + } else if t.config.enabled.current == false && *c.LibConfig.Enabled == true { + log.Debug("APM Tracing is disabled. Restart the service to enable it.") + } + } + } + if len(telemConfigs) > 0 { + log.Debug("Reporting %d configuration changes to telemetry", len(telemConfigs)) + telemetry.GlobalClient.ConfigChange(telemConfigs) + } + return statuses +} + +// startRemoteConfig starts the remote config client. +// It registers the APM_TRACING product with a callback, +// and the LIVE_DEBUGGING product without a callback. +func (t *tracer) startRemoteConfig(rcConfig remoteconfig.ClientConfig) error { + err := remoteconfig.Start(rcConfig) + if err != nil { + return err + } + + var dynamicInstrumentationError, apmTracingError error + + if t.config.dynamicInstrumentationEnabled { + dynamicInstrumentationError = remoteconfig.Subscribe("LIVE_DEBUGGING", t.dynamicInstrumentationRCUpdate) + } + + apmTracingError = remoteconfig.Subscribe( + state.ProductAPMTracing, + t.onRemoteConfigUpdate, + remoteconfig.APMTracingSampleRate, + remoteconfig.APMTracingHTTPHeaderTags, + remoteconfig.APMTracingCustomTags, + remoteconfig.APMTracingEnabled, + ) + + if apmTracingError != nil || dynamicInstrumentationError != nil { + return fmt.Errorf("could not subscribe to at least one remote config product: %s; %s", + apmTracingError, dynamicInstrumentationError) + } + + return nil +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/rules_sampler.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/rules_sampler.go index b7ac1daea..85b8d9cf5 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/rules_sampler.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/rules_sampler.go @@ -16,11 +16,11 @@ import ( "sync" "time" - "golang.org/x/time/rate" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" + + "golang.org/x/time/rate" ) // rulesSampler holds instances of trace sampler and single span sampler, that are configured with the given set of rules. @@ -37,14 +37,16 @@ type rulesSampler struct { // Rules are split between trace and single span sampling rules according to their type. // Such rules are user-defined through environment variable or WithSamplingRules option. // Invalid rules or environment variable values are tolerated, by logging warnings and then ignoring them. -func newRulesSampler(traceRules, spanRules []SamplingRule) *rulesSampler { +func newRulesSampler(traceRules, spanRules []SamplingRule, traceSampleRate float64) *rulesSampler { return &rulesSampler{ - traces: newTraceRulesSampler(traceRules), + traces: newTraceRulesSampler(traceRules, traceSampleRate), spans: newSingleSpanRulesSampler(spanRules), } } -func (r *rulesSampler) SampleTrace(s *span) bool { return r.traces.apply(s) } +func (r *rulesSampler) SampleTrace(s *span) bool { return r.traces.sampleRules(s) } + +func (r *rulesSampler) SampleTraceGlobalRate(s *span) bool { return r.traces.sampleGlobalRate(s) } func (r *rulesSampler) SampleSpan(s *span) bool { return r.spans.apply(s) } @@ -70,24 +72,37 @@ type SamplingRule struct { // If not specified, the default is no limit. MaxPerSecond float64 - ruleType SamplingRuleType - exactService string - exactName string - limiter *rateLimiter + // Resource specifies the regex pattern that a span resource must match. + Resource *regexp.Regexp + + // Tags specifies the map of key-value patterns that span tags must match. + Tags map[string]*regexp.Regexp + + ruleType SamplingRuleType + limiter *rateLimiter } // match returns true when the span's details match all the expected values in the rule. func (sr *SamplingRule) match(s *span) bool { if sr.Service != nil && !sr.Service.MatchString(s.Service) { return false - } else if sr.exactService != "" && sr.exactService != s.Service { - return false } if sr.Name != nil && !sr.Name.MatchString(s.Name) { return false - } else if sr.exactName != "" && sr.exactName != s.Name { + } + if sr.Resource != nil && !sr.Resource.MatchString(s.Resource) { return false } + s.Lock() + defer s.Unlock() + if sr.Tags != nil && s.Meta != nil { + for k, regex := range sr.Tags { + v, ok := s.Meta[k] + if !ok || !regex.MatchString(v) { + return false + } + } + } return true } @@ -95,6 +110,8 @@ func (sr *SamplingRule) match(s *span) bool { type SamplingRuleType int const ( + SamplingRuleUndefined SamplingRuleType = 0 + // SamplingRuleTrace specifies a sampling rule that applies to the entire trace if any spans satisfy the criteria. // If a sampling rule is of type SamplingRuleTrace, such rule determines the sampling rate to apply // to trace spans. If a span matches that rule, it will impact the trace sampling decision. @@ -123,8 +140,9 @@ func (sr SamplingRuleType) String() string { // to spans that match the service name provided. func ServiceRule(service string, rate float64) SamplingRule { return SamplingRule{ - exactService: service, - Rate: rate, + Service: globMatch(service), + ruleType: SamplingRuleTrace, + Rate: rate, } } @@ -132,8 +150,9 @@ func ServiceRule(service string, rate float64) SamplingRule { // to spans that match the operation name provided. func NameRule(name string, rate float64) SamplingRule { return SamplingRule{ - exactName: name, - Rate: rate, + Name: globMatch(name), + ruleType: SamplingRuleTrace, + Rate: rate, } } @@ -141,16 +160,50 @@ func NameRule(name string, rate float64) SamplingRule { // to spans matching both the operation and service names provided. func NameServiceRule(name string, service string, rate float64) SamplingRule { return SamplingRule{ - exactService: service, - exactName: name, - Rate: rate, + Service: globMatch(service), + Name: globMatch(name), + ruleType: SamplingRuleTrace, + Rate: rate, } } // RateRule returns a SamplingRule that applies the provided sampling rate to all spans. func RateRule(rate float64) SamplingRule { return SamplingRule{ - Rate: rate, + Rate: rate, + ruleType: SamplingRuleTrace, + } +} + +// TagsResourceRule returns a SamplingRule that applies the provided sampling rate to traces with spans that match +// resource, name, service and tags provided. +func TagsResourceRule(tags map[string]*regexp.Regexp, resource, name, service string, rate float64) SamplingRule { + return SamplingRule{ + Service: globMatch(service), + Name: globMatch(name), + Resource: globMatch(resource), + Rate: rate, + Tags: tags, + ruleType: SamplingRuleTrace, + } +} + +// SpanTagsResourceRule returns a SamplingRule that applies the provided sampling rate to spans that match +// resource, name, service and tags provided. Values of the tags map are expected to be in glob format. +func SpanTagsResourceRule(tags map[string]string, resource, name, service string, rate float64) SamplingRule { + globTags := make(map[string]*regexp.Regexp, len(tags)) + for k, v := range tags { + if g := globMatch(v); g != nil { + globTags[k] = g + } + } + return SamplingRule{ + Service: globMatch(service), + Name: globMatch(name), + Resource: globMatch(resource), + Rate: rate, + Tags: globTags, + ruleType: SamplingRuleSpan, } } @@ -159,12 +212,11 @@ func RateRule(rate float64) SamplingRule { // Operation and service fields must be valid glob patterns. func SpanNameServiceRule(name, service string, rate float64) SamplingRule { return SamplingRule{ - Service: globMatch(service), - Name: globMatch(name), - Rate: rate, - ruleType: SamplingRuleSpan, - exactName: name, - limiter: newSingleSpanRateLimiter(0), + Service: globMatch(service), + Name: globMatch(name), + Rate: rate, + ruleType: SamplingRuleSpan, + limiter: newSingleSpanRateLimiter(0), } } @@ -179,7 +231,6 @@ func SpanNameServiceMPSRule(name, service string, rate, limit float64) SamplingR MaxPerSecond: limit, Rate: rate, ruleType: SamplingRuleSpan, - exactName: name, limiter: newSingleSpanRateLimiter(limit), } } @@ -199,6 +250,7 @@ func SpanNameServiceMPSRule(name, service string, rate, limit float64) SamplingR // Its value is the number of spans to sample per second. // Spans that matched the rules but exceeded the rate limit are not sampled. type traceRulesSampler struct { + m sync.RWMutex rules []SamplingRule // the rules to match spans with globalRate float64 // a rate to apply when no rules match a span limiter *rateLimiter // used to limit the volume of spans sampled @@ -206,10 +258,10 @@ type traceRulesSampler struct { // newTraceRulesSampler configures a *traceRulesSampler instance using the given set of rules. // Invalid rules or environment variable values are tolerated, by logging warnings and then ignoring them. -func newTraceRulesSampler(rules []SamplingRule) *traceRulesSampler { +func newTraceRulesSampler(rules []SamplingRule, traceSampleRate float64) *traceRulesSampler { return &traceRulesSampler{ rules: rules, - globalRate: globalSampleRate(), + globalRate: traceSampleRate, limiter: newRateLimiter(), } } @@ -235,21 +287,66 @@ func globalSampleRate() float64 { } func (rs *traceRulesSampler) enabled() bool { + rs.m.RLock() + defer rs.m.RUnlock() return len(rs.rules) > 0 || !math.IsNaN(rs.globalRate) } -// apply uses the sampling rules to determine the sampling rate for the -// provided span. If the rules don't match, and a default rate hasn't been -// set using DD_TRACE_SAMPLE_RATE, then it returns false and the span is not +// setGlobalSampleRate sets the global sample rate to the given value. +// Returns whether the value was changed or not. +func (rs *traceRulesSampler) setGlobalSampleRate(rate float64) bool { + if rate < 0.0 || rate > 1.0 { + log.Warn("Ignoring trace sample rate %f: value out of range [0,1]", rate) + return false + } + rs.m.Lock() + defer rs.m.Unlock() + if math.IsNaN(rs.globalRate) && math.IsNaN(rate) { + // NaN is not considered equal to any number, including itself. + // It should be compared with math.IsNaN + return false + } + if rs.globalRate == rate { + return false + } + rs.globalRate = rate + return true +} + +// sampleGlobalRate applies the global trace sampling rate to the span. If the rate is Nan, +// the function return false, then it returns false and the span is not // modified. -func (rs *traceRulesSampler) apply(span *span) bool { +func (rs *traceRulesSampler) sampleGlobalRate(span *span) bool { + if !rs.enabled() { + // short path when disabled + return false + } + + rs.m.RLock() + rate := rs.globalRate + rs.m.RUnlock() + + if math.IsNaN(rate) { + return false + } + + rs.applyRate(span, rate, time.Now()) + return true +} + +// sampleRules uses the sampling rules to determine the sampling rate for the +// provided span. If the rules don't match, then it returns false and the span is not +// modified. +func (rs *traceRulesSampler) sampleRules(span *span) bool { if !rs.enabled() { // short path when disabled return false } var matched bool + rs.m.RLock() rate := rs.globalRate + rs.m.RUnlock() for _, rule := range rs.rules { if rule.match(span) { matched = true @@ -257,28 +354,28 @@ func (rs *traceRulesSampler) apply(span *span) bool { break } } - if !matched && math.IsNaN(rate) { + if !matched { // no matching rule or global rate, so we want to fall back // to priority sampling return false } - rs.applyRule(span, rate, time.Now()) + rs.applyRate(span, rate, time.Now()) return true } -func (rs *traceRulesSampler) applyRule(span *span, rate float64, now time.Time) { +func (rs *traceRulesSampler) applyRate(span *span, rate float64, now time.Time) { span.SetTag(keyRulesSamplerAppliedRate, rate) if !sampledByRate(span.TraceID, rate) { - span.setSamplingPriority(ext.PriorityUserReject, samplernames.RuleRate, rate) + span.setSamplingPriority(ext.PriorityUserReject, samplernames.RuleRate) return } sampled, rate := rs.limiter.allowOne(now) if sampled { - span.setSamplingPriority(ext.PriorityUserKeep, samplernames.RuleRate, rate) + span.setSamplingPriority(ext.PriorityUserKeep, samplernames.RuleRate) } else { - span.setSamplingPriority(ext.PriorityUserReject, samplernames.RuleRate, rate) + span.setSamplingPriority(ext.PriorityUserReject, samplernames.RuleRate) } span.SetTag(keyRulesSamplerLimiterRate, rate) } @@ -355,18 +452,16 @@ func (rs *singleSpanRulesSampler) apply(span *span) bool { rate := rule.Rate span.setMetric(keyRulesSamplerAppliedRate, rate) if !sampledByRate(span.SpanID, rate) { - span.setSamplingPriority(ext.PriorityUserReject, samplernames.RuleRate, rate) return false } var sampled bool if rule.limiter != nil { - sampled, rate = rule.limiter.allowOne(time.Now()) + sampled, rate = rule.limiter.allowOne(nowTime()) if !sampled { return false } } - span.setSamplingPriority(ext.PriorityUserKeep, samplernames.RuleRate, rate) - span.setMetric(keySpanSamplingMechanism, samplingMechanismSingleSpan) + span.setMetric(keySpanSamplingMechanism, float64(samplernames.SingleSpan)) span.setMetric(keySingleSpanSamplingRuleRate, rate) if rule.MaxPerSecond != 0 { span.setMetric(keySingleSpanSamplingMPS, rule.MaxPerSecond) @@ -439,7 +534,7 @@ func newSingleSpanRateLimiter(mps float64) *rateLimiter { // and '*' treated as regex metacharacters. func globMatch(pattern string) *regexp.Regexp { if pattern == "" { - return regexp.MustCompile("^.*$") + return nil } // escaping regex characters pattern = regexp.QuoteMeta(pattern) @@ -450,7 +545,8 @@ func globMatch(pattern string) *regexp.Regexp { return regexp.MustCompile(fmt.Sprintf("^%s$", pattern)) } -// samplingRulesFromEnv parses sampling rules from the DD_TRACE_SAMPLING_RULES, +// samplingRulesFromEnv parses sampling rules from +// the DD_TRACE_SAMPLING_RULES, DD_TRACE_SAMPLING_RULES_FILE // DD_SPAN_SAMPLING_RULES and DD_SPAN_SAMPLING_RULES_FILE environment variables. func samplingRulesFromEnv() (trace, span []SamplingRule, err error) { var errs []string @@ -459,59 +555,122 @@ func samplingRulesFromEnv() (trace, span []SamplingRule, err error) { err = fmt.Errorf("\n\t%s", strings.Join(errs, "\n\t")) } }() - rulesFromEnv := os.Getenv("DD_TRACE_SAMPLING_RULES") - if rulesFromEnv != "" { - trace, err = unmarshalSamplingRules([]byte(rulesFromEnv), SamplingRuleTrace) + + rulesByType := func(spanType SamplingRuleType) (rules []SamplingRule, errs []string) { + env := fmt.Sprintf("DD_%s_SAMPLING_RULES", strings.ToUpper(spanType.String())) + rulesEnv := os.Getenv(fmt.Sprintf("DD_%s_SAMPLING_RULES", strings.ToUpper(spanType.String()))) + rules, err := unmarshalSamplingRules([]byte(rulesEnv), spanType) if err != nil { errs = append(errs, err.Error()) } - } - span, err = unmarshalSamplingRules([]byte(os.Getenv("DD_SPAN_SAMPLING_RULES")), SamplingRuleSpan) - if err != nil { - errs = append(errs, err.Error()) - } - rulesFile := os.Getenv("DD_SPAN_SAMPLING_RULES_FILE") - if len(span) != 0 { - if rulesFile != "" { - log.Warn("DIAGNOSTICS Error(s): DD_SPAN_SAMPLING_RULES is available and will take precedence over DD_SPAN_SAMPLING_RULES_FILE") + rulesFile := os.Getenv(env + "_FILE") + if len(rules) != 0 { + if rulesFile != "" { + log.Warn("DIAGNOSTICS Error(s): %s is available and will take precedence over %s_FILE", env, env) + } + return rules, errs + } + if rulesFile == "" { + return rules, errs } - return trace, span, err - } - if rulesFile != "" { rulesFromEnvFile, err := os.ReadFile(rulesFile) if err != nil { - errs = append(errs, fmt.Sprintf("Couldn't read file from DD_SPAN_SAMPLING_RULES_FILE: %v", err)) + errs = append(errs, fmt.Sprintf("Couldn't read file from %s_FILE: %v", env, err)) } - span, err = unmarshalSamplingRules(rulesFromEnvFile, SamplingRuleSpan) + rules, err = unmarshalSamplingRules(rulesFromEnvFile, spanType) if err != nil { errs = append(errs, err.Error()) } + return rules, errs + } + + trace, tErrs := rulesByType(SamplingRuleTrace) + if len(tErrs) != 0 { + errs = append(errs, tErrs...) + } + span, sErrs := rulesByType(SamplingRuleSpan) + if len(sErrs) != 0 { + errs = append(errs, sErrs...) } return trace, span, err } +func (sr *SamplingRule) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + var v jsonRule + if err := json.Unmarshal(b, &v); err != nil { + return err + } + rules, err := validateRules([]jsonRule{v}, SamplingRuleUndefined) + if err != nil { + return err + } + *sr = rules[0] + return nil +} + +type jsonRule struct { + Service string `json:"service"` + Name string `json:"name"` + Rate json.Number `json:"sample_rate"` + MaxPerSecond float64 `json:"max_per_second"` + Resource string `json:"resource"` + Tags map[string]string `json:"tags"` + Type *SamplingRuleType `json:"type,omitempty"` +} + +func (j jsonRule) String() string { + var s []string + if j.Service != "" { + s = append(s, fmt.Sprintf("Service:%s", j.Service)) + } + if j.Name != "" { + s = append(s, fmt.Sprintf("Name:%s", j.Name)) + } + if j.Rate != "" { + s = append(s, fmt.Sprintf("Rate:%s", j.Rate)) + } + if j.MaxPerSecond != 0 { + s = append(s, fmt.Sprintf("MaxPerSecond:%f", j.MaxPerSecond)) + } + if j.Resource != "" { + s = append(s, fmt.Sprintf("Resource:%s", j.Resource)) + } + if len(j.Tags) != 0 { + s = append(s, fmt.Sprintf("Tags:%v", j.Tags)) + } + if j.Type != nil { + s = append(s, fmt.Sprintf("Type: %v", *j.Type)) + } + return fmt.Sprintf("{%s}", strings.Join(s, " ")) +} + // unmarshalSamplingRules unmarshals JSON from b and returns the sampling rules found, attributing // the type t to them. If any errors are occurred, they are returned. func unmarshalSamplingRules(b []byte, spanType SamplingRuleType) ([]SamplingRule, error) { if len(b) == 0 { return nil, nil } - var jsonRules []struct { - Service string `json:"service"` - Name string `json:"name"` - Rate json.Number `json:"sample_rate"` - MaxPerSecond float64 `json:"max_per_second"` - } + var jsonRules []jsonRule + // if the JSON is an array, unmarshal it as an array of rules err := json.Unmarshal(b, &jsonRules) if err != nil { return nil, fmt.Errorf("error unmarshalling JSON: %v", err) } - rules := make([]SamplingRule, 0, len(jsonRules)) + return validateRules(jsonRules, spanType) +} + +func validateRules(jsonRules []jsonRule, spanType SamplingRuleType) ([]SamplingRule, error) { var errs []string + rules := make([]SamplingRule, 0, len(jsonRules)) for i, v := range jsonRules { if v.Rate == "" { - errs = append(errs, fmt.Sprintf("at index %d: rate not provided", i)) - continue + v.Rate = "1" + } + if v.Type != nil && *v.Type != spanType { + spanType = *v.Type } rate, err := v.Rate.Float64() if err != nil { @@ -519,33 +678,23 @@ func unmarshalSamplingRules(b []byte, spanType SamplingRuleType) ([]SamplingRule continue } if rate < 0.0 || rate > 1.0 { - errs = append(errs, fmt.Sprintf("at index %d: ignoring rule %+v: rate is out of [0.0, 1.0] range", i, v)) + errs = append(errs, fmt.Sprintf("at index %d: ignoring rule %s: rate is out of [0.0, 1.0] range", i, v.String())) continue } - switch spanType { - case SamplingRuleSpan: - if v.Service == "" && v.Name == "" { - errs = append(errs, fmt.Sprintf("at index %d: ignoring rule %+v: service name and operation name are not provided", i, v)) - continue - } - rules = append(rules, SamplingRule{ - Service: globMatch(v.Service), - Name: globMatch(v.Name), - Rate: rate, - MaxPerSecond: v.MaxPerSecond, - limiter: newSingleSpanRateLimiter(v.MaxPerSecond), - ruleType: SamplingRuleSpan, - }) - case SamplingRuleTrace: - switch { - case v.Service != "" && v.Name != "": - rules = append(rules, NameServiceRule(v.Name, v.Service, rate)) - case v.Service != "": - rules = append(rules, ServiceRule(v.Service, rate)) - case v.Name != "": - rules = append(rules, NameRule(v.Name, rate)) - } + tagGlobs := make(map[string]*regexp.Regexp, len(v.Tags)) + for k, g := range v.Tags { + tagGlobs[k] = globMatch(g) } + rules = append(rules, SamplingRule{ + Service: globMatch(v.Service), + Name: globMatch(v.Name), + Rate: rate, + MaxPerSecond: v.MaxPerSecond, + Resource: globMatch(v.Resource), + Tags: tagGlobs, + ruleType: spanType, + limiter: newSingleSpanRateLimiter(v.MaxPerSecond), + }) } if len(errs) != 0 { return rules, fmt.Errorf("%s", strings.Join(errs, "\n\t")) @@ -556,26 +705,36 @@ func unmarshalSamplingRules(b []byte, spanType SamplingRuleType) ([]SamplingRule // MarshalJSON implements the json.Marshaler interface. func (sr *SamplingRule) MarshalJSON() ([]byte, error) { s := struct { - Service string `json:"service"` - Name string `json:"name"` - Rate float64 `json:"sample_rate"` - Type string `json:"type"` - MaxPerSecond *float64 `json:"max_per_second,omitempty"` + Service string `json:"service,omitempty"` + Name string `json:"name,omitempty"` + Resource string `json:"resource,omitempty"` + Rate float64 `json:"sample_rate"` + Tags map[string]string `json:"tags,omitempty"` + Type *string `json:"type,omitempty"` + MaxPerSecond *float64 `json:"max_per_second,omitempty"` }{} - if sr.exactService != "" { - s.Service = sr.exactService - } else if sr.Service != nil { - s.Service = fmt.Sprintf("%s", sr.Service) + if sr.Service != nil { + s.Service = sr.Service.String() } - if sr.exactName != "" { - s.Name = sr.exactName - } else if sr.Name != nil { - s.Name = fmt.Sprintf("%s", sr.Name) + if sr.Name != nil { + s.Name = sr.Name.String() } - s.Rate = sr.Rate - s.Type = fmt.Sprintf("%v(%d)", sr.ruleType.String(), sr.ruleType) if sr.MaxPerSecond != 0 { s.MaxPerSecond = &sr.MaxPerSecond } + if sr.Resource != nil { + s.Resource = sr.Resource.String() + } + s.Rate = sr.Rate + if v := sr.ruleType.String(); v != "" { + t := fmt.Sprintf("%v(%d)", v, sr.ruleType) + s.Type = &t + } + s.Tags = make(map[string]string, len(sr.Tags)) + for k, v := range sr.Tags { + if v != nil { + s.Tags[k] = v.String() + } + } return json.Marshal(&s) } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go index ce44a1876..2c95caf26 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go @@ -142,9 +142,9 @@ func (ps *prioritySampler) getRate(spn *span) float64 { func (ps *prioritySampler) apply(spn *span) { rate := ps.getRate(spn) if sampledByRate(spn.TraceID, rate) { - spn.setSamplingPriority(ext.PriorityAutoKeep, samplernames.AgentRate, rate) + spn.setSamplingPriority(ext.PriorityAutoKeep, samplernames.AgentRate) } else { - spn.setSamplingPriority(ext.PriorityAutoReject, samplernames.AgentRate, rate) + spn.setSamplingPriority(ext.PriorityAutoReject, samplernames.AgentRate) } spn.SetTag(keySamplingPriorityRate, rate) } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go index 1567ec001..574f7ce3c 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go @@ -16,6 +16,7 @@ import ( "reflect" "runtime" "runtime/pprof" + rt "runtime/trace" "strconv" "strings" "sync" @@ -25,6 +26,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + sharedinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" @@ -62,21 +64,23 @@ type errorConfig struct { type span struct { sync.RWMutex `msg:"-"` // all fields are protected by this RWMutex - Name string `msg:"name"` // operation name - Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request") - Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users") - Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache") - Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch - Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds - Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata - Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics - SpanID uint64 `msg:"span_id"` // identifier of this span - TraceID uint64 `msg:"trace_id"` // identifier of the root span - ParentID uint64 `msg:"parent_id"` // identifier of the span's direct parent - Error int32 `msg:"error"` // error status of the span; 0 means no errors - + Name string `msg:"name"` // operation name + Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request") + Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users") + Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache") + Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch + Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds + Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata + Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics + SpanID uint64 `msg:"span_id"` // identifier of this span + TraceID uint64 `msg:"trace_id"` // lower 64-bits of the root span identifier + ParentID uint64 `msg:"parent_id"` // identifier of the span's direct parent + Error int32 `msg:"error"` // error status of the span; 0 means no errors + SpanLinks []ddtrace.SpanLink `msg:"span_links"` // links to other spans + + goExecTraced bool `msg:"-"` noDebugStack bool `msg:"-"` // disables debug stack traces - finished bool `msg:"-"` // true if the span has been submitted to a tracer. + finished bool `msg:"-"` // true if the span has been submitted to a tracer. Can only be read/modified if the trace is locked. context *spanContext `msg:"-"` // span propagation context pprofCtxActive context.Context `msg:"-"` // contains pprof.WithLabel labels to tell the profiler more about this span @@ -158,52 +162,117 @@ func (s *span) SetTag(key string, value interface{}) { s.setMeta(key, v.String()) return } + if value != nil { + // Arrays will be translated to dot notation. e.g. + // {"myarr.0": "foo", "myarr.1": "bar"} + // which will be displayed as an array in the UI. + switch reflect.TypeOf(value).Kind() { + case reflect.Slice: + slice := reflect.ValueOf(value) + for i := 0; i < slice.Len(); i++ { + key := fmt.Sprintf("%s.%d", key, i) + v := slice.Index(i) + if num, ok := toFloat64(v.Interface()); ok { + s.setMetric(key, num) + } else { + s.setMeta(key, fmt.Sprintf("%v", v)) + } + } + return + } + } // not numeric, not a string, not a fmt.Stringer, not a bool, and not an error s.setMeta(key, fmt.Sprint(value)) } // setSamplingPriority locks then span, then updates the sampling priority. // It also updates the trace's sampling priority. -func (s *span) setSamplingPriority(priority int, sampler samplernames.SamplerName, rate float64) { +func (s *span) setSamplingPriority(priority int, sampler samplernames.SamplerName) { s.Lock() defer s.Unlock() - s.setSamplingPriorityLocked(priority, sampler, rate) + s.setSamplingPriorityLocked(priority, sampler) } -// setUser sets the span user ID tag as well as some optional user monitoring tags depending on the configuration. -// The function assumes that the span it is called on is the trace's root span. -func (s *span) setUser(id string, cfg UserMonitoringConfig) { - trace := s.context.trace - s.Lock() - defer s.Unlock() - if cfg.propagateID { +// Root returns the root span of the span's trace. The return value shouldn't be +// nil as long as the root span is valid and not finished. +func (s *span) Root() Span { + return s.root() +} + +// root returns the root span of the span's trace. The return value shouldn't be +// nil as long as the root span is valid and not finished. +// As opposed to the public Root method, this one returns the actual span type +// when internal usage requires it (to avoid type assertions from Root's return +// value). +func (s *span) root() *span { + if s == nil || s.context == nil { + return nil + } + if s.context.trace == nil { + return nil + } + return s.context.trace.root +} + +// SetUser associates user information to the current trace which the +// provided span belongs to. The options can be used to tune which user +// bit of information gets monitored. In case of distributed traces, +// the user id can be propagated across traces using the WithPropagation() option. +// See https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/?tab=set_user#add-user-information-to-traces +func (s *span) SetUser(id string, opts ...UserMonitoringOption) { + cfg := UserMonitoringConfig{ + Metadata: make(map[string]string), + } + for _, fn := range opts { + fn(&cfg) + } + root := s.root() + trace := root.context.trace + root.Lock() + defer root.Unlock() + // We don't lock spans when flushing, so we could have a data race when + // modifying a span as it's being flushed. This protects us against that + // race, since spans are marked `finished` before we flush them. + if root.finished { + return + } + if cfg.PropagateID { // Delete usr.id from the tags since _dd.p.usr.id takes precedence - delete(s.Meta, keyUserID) + delete(root.Meta, keyUserID) idenc := base64.StdEncoding.EncodeToString([]byte(id)) trace.setPropagatingTag(keyPropagatedUserID, idenc) + s.context.updated = true } else { - // Unset the propagated user ID so that a propagated user ID coming from upstream won't be propagated anymore. - trace.unsetPropagatingTag(keyPropagatedUserID) - delete(s.Meta, keyPropagatedUserID) - // setMeta is used since the span is already locked - s.setMeta(keyUserID, id) - } - for k, v := range map[string]string{ - keyUserEmail: cfg.email, - keyUserName: cfg.name, - keyUserScope: cfg.scope, - keyUserRole: cfg.role, - keyUserSessionID: cfg.sessionID, - } { + if trace.hasPropagatingTag(keyPropagatedUserID) { + // Unset the propagated user ID so that a propagated user ID coming from upstream won't be propagated anymore. + trace.unsetPropagatingTag(keyPropagatedUserID) + s.context.updated = true + } + delete(root.Meta, keyPropagatedUserID) + } + + usrData := map[string]string{ + keyUserID: id, + keyUserEmail: cfg.Email, + keyUserName: cfg.Name, + keyUserScope: cfg.Scope, + keyUserRole: cfg.Role, + keyUserSessionID: cfg.SessionID, + } + for k, v := range cfg.Metadata { + usrData[fmt.Sprintf("usr.%s", k)] = v + } + for k, v := range usrData { if v != "" { - s.setMeta(k, v) + // setMeta is used since the span is already locked + root.setMeta(k, v) } } } // setSamplingPriorityLocked updates the sampling priority. // It also updates the trace's sampling priority. -func (s *span) setSamplingPriorityLocked(priority int, sampler samplernames.SamplerName, rate float64) { +func (s *span) setSamplingPriorityLocked(priority int, sampler samplernames.SamplerName) { // We don't lock spans when flushing, so we could have a data race when // modifying a span as it's being flushed. This protects us against that // race, since spans are marked `finished` before we flush them. @@ -211,7 +280,7 @@ func (s *span) setSamplingPriorityLocked(priority int, sampler samplernames.Samp return } s.setMetric(keySamplingPriority, float64(priority)) - s.context.setSamplingPriority(priority, sampler, rate) + s.context.setSamplingPriority(priority, sampler) } // setTagError sets the error tag. It accounts for various valid scenarios. @@ -221,13 +290,13 @@ func (s *span) setTagError(value interface{}, cfg errorConfig) { if yes { if s.Error == 0 { // new error - atomic.AddInt64(&s.context.errors, 1) + atomic.AddInt32(&s.context.errors, 1) } s.Error = 1 } else { if s.Error > 0 { // flip from active to inactive - atomic.AddInt64(&s.context.errors, -1) + atomic.AddInt32(&s.context.errors, -1) } s.Error = 0 } @@ -332,11 +401,11 @@ func (s *span) setTagBool(key string, v bool) { } case ext.ManualDrop: if v { - s.setSamplingPriorityLocked(ext.PriorityUserReject, samplernames.Manual, math.NaN()) + s.setSamplingPriorityLocked(ext.PriorityUserReject, samplernames.Manual) } case ext.ManualKeep: if v { - s.setSamplingPriorityLocked(ext.PriorityUserKeep, samplernames.Manual, math.NaN()) + s.setSamplingPriorityLocked(ext.PriorityUserKeep, samplernames.Manual) } default: if v { @@ -357,12 +426,12 @@ func (s *span) setMetric(key string, v float64) { switch key { case ext.ManualKeep: if v == float64(samplernames.AppSec) { - s.setSamplingPriorityLocked(ext.PriorityUserKeep, samplernames.AppSec, math.NaN()) + s.setSamplingPriorityLocked(ext.PriorityUserKeep, samplernames.AppSec) } case ext.SamplingPriority: // ext.SamplingPriority is deprecated in favor of ext.ManualKeep and ext.ManualDrop. // We have it here for backward compatibility. - s.setSamplingPriorityLocked(int(v), samplernames.Manual, math.NaN()) + s.setSamplingPriorityLocked(int(v), samplernames.Manual) default: s.Metrics[key] = v } @@ -395,6 +464,29 @@ func (s *span) Finish(opts ...ddtrace.FinishOption) { if s.taskEnd != nil { s.taskEnd() } + if s.goExecTraced && rt.IsEnabled() { + // Only tag spans as traced if they both started & ended with + // execution tracing enabled. This is technically not sufficient + // for spans which could straddle the boundary between two + // execution traces, but there's really nothing we can do in + // those cases since execution tracing tasks aren't recorded in + // traces if they started before the trace. + s.SetTag("go_execution_traced", "yes") + } else if s.goExecTraced { + // If the span started with tracing enabled, but tracing wasn't + // enabled when the span finished, we still have some data to + // show. If tracing wasn't enabled when the span started, we + // won't have data in the execution trace to identify it so + // there's nothign we can show. + s.SetTag("go_execution_traced", "partial") + } + + if tr, ok := internal.GetGlobalTracer().(*tracer); ok && tr.rulesSampling.traces.enabled() { + if !s.context.trace.isLocked() { + tr.rulesSampling.SampleTrace(s) + } + } + s.finish(t) if s.pprofCtxRestore != nil { @@ -434,10 +526,12 @@ func (s *span) finish(finishTime int64) { if s.Duration < 0 { s.Duration = 0 } - s.finished = true keep := true if t, ok := internal.GetGlobalTracer().(*tracer); ok { + if !t.config.enabled.current { + return + } // we have an active tracer if t.config.canComputeStats() && shouldComputeStats(s) { // the agent supports computed stats @@ -452,11 +546,25 @@ func (s *span) finish(finishTime int64) { // the agent supports dropping p0's in the client keep = shouldKeep(s) } + if t.config.debugAbandonedSpans { + // the tracer supports debugging abandoned spans + select { + case t.abandonedSpansDebugger.In <- newAbandonedSpanCandidate(s, true): + // ok + default: + log.Error("Abandoned spans channel full, disregarding span.") + } + } } if keep { // a single kept span keeps the whole trace. s.context.trace.keep() } + if log.DebugEnabled() { + // avoid allocating the ...interface{} argument if debug logging is disabled + log.Debug("Finished Span: %v, Operation: %s, Resource: %s, Tags: %v, %v", + s, s.Name, s.Resource, s.Meta, s.Metrics) + } s.context.finish() } @@ -514,11 +622,11 @@ func obfuscatedResource(o *obfuscate.Obfuscator, typ, resource string) string { // shouldKeep reports whether the trace should be kept. // a single span being kept implies the whole trace being kept. func shouldKeep(s *span) bool { - if p, ok := s.context.samplingPriority(); ok && p > 0 { + if p, ok := s.context.SamplingPriority(); ok && p > 0 { // positive sampling priorities stay return true } - if atomic.LoadInt64(&s.context.errors) > 0 { + if atomic.LoadInt32(&s.context.errors) > 0 { // traces with any span containing an error get kept return true } @@ -550,6 +658,7 @@ func (s *span) String() string { fmt.Sprintf("Service: %s", s.Service), fmt.Sprintf("Resource: %s", s.Resource), fmt.Sprintf("TraceID: %d", s.TraceID), + fmt.Sprintf("TraceID128: %s", s.context.TraceID128()), fmt.Sprintf("SpanID: %d", s.SpanID), fmt.Sprintf("ParentID: %d", s.ParentID), fmt.Sprintf("Start: %s", time.Unix(0, s.Start)), @@ -591,18 +700,28 @@ func (s *span) Format(f fmt.State, c rune) { fmt.Fprintf(f, "dd.version=%s ", v) } } - fmt.Fprintf(f, `dd.trace_id="%d" dd.span_id="%d"`, s.TraceID, s.SpanID) + var traceID string + if sharedinternal.BoolEnv("DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED", false) && s.context.traceID.HasUpper() { + traceID = s.context.TraceID128() + } else { + traceID = fmt.Sprintf("%d", s.TraceID) + } + fmt.Fprintf(f, `dd.trace_id=%q `, traceID) + fmt.Fprintf(f, `dd.span_id="%d" `, s.SpanID) + fmt.Fprintf(f, `dd.parent_id="%d"`, s.ParentID) default: fmt.Fprintf(f, "%%!%c(ddtrace.Span=%v)", c, s) } } const ( - keySamplingPriority = "_sampling_priority_v1" - keySamplingPriorityRate = "_dd.agent_psr" - keyDecisionMaker = "_dd.p.dm" - keyServiceHash = "_dd.dm.service_hash" - keyOrigin = "_dd.origin" + keySamplingPriority = "_sampling_priority_v1" + keySamplingPriorityRate = "_dd.agent_psr" + keyDecisionMaker = "_dd.p.dm" + keyServiceHash = "_dd.dm.service_hash" + keyOrigin = "_dd.origin" + // keyHostname can be used to override the agent's hostname detection when using `WithHostname`. Not to be confused with keyTracerHostname + // which is set via auto-detection. keyHostname = "_dd.hostname" keyRulesSamplerAppliedRate = "_dd.rule_psr" keyRulesSamplerLimiterRate = "_dd.limit_psr" @@ -621,9 +740,21 @@ const ( keySingleSpanSamplingMPS = "_dd.span_sampling.max_per_second" // keyPropagatedUserID holds the propagated user identifier, if user id propagation is enabled. keyPropagatedUserID = "_dd.p.usr.id" + //keyTracerHostname holds the tracer detected hostname, only present when not connected over UDS to agent. + keyTracerHostname = "_dd.tracer_hostname" + // keyTraceID128 is the lowercase, hex encoded upper 64 bits of a 128-bit trace id, if present. + keyTraceID128 = "_dd.p.tid" + // keySpanAttributeSchemaVersion holds the selected DD_TRACE_SPAN_ATTRIBUTE_SCHEMA version. + keySpanAttributeSchemaVersion = "_dd.trace_span_attribute_schema" + // keyPeerServiceSource indicates the precursor tag that was used as the value of peer.service. + keyPeerServiceSource = "_dd.peer.service.source" + // keyPeerServiceRemappedFrom indicates the previous value for peer.service, in case remapping happened. + keyPeerServiceRemappedFrom = "_dd.peer.service.remapped_from" + // keyBaseService contains the globally configured tracer service name. It is only set for spans that override it. + keyBaseService = "_dd.base_service" ) -// The following set of tags is used for user monitoring and set through calls to span.setUser(). +// The following set of tags is used for user monitoring and set through calls to span.SetUser(). const ( keyUserID = "usr.id" keyUserEmail = "usr.email" @@ -632,9 +763,3 @@ const ( keyUserScope = "usr.scope" keyUserSessionID = "usr.session_id" ) - -const ( - // samplingMechanismSingleSpan specifies value reserved to indicate that a span was kept - // on account of a single span sampling rule. - samplingMechanismSingleSpan = 8 -) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go index 16bb758f8..219711387 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span_msgp.go @@ -1,18 +1,102 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - package tracer -// NOTE: THIS FILE WAS PRODUCED BY THE -// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) -// DO NOT EDIT +// Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( "github.com/tinylib/msgp/msgp" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" ) +// DecodeMsg implements msgp.Decodable +func (z *errorConfig) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "noDebugStack": + z.noDebugStack, err = dc.ReadBool() + if err != nil { + err = msgp.WrapError(err, "noDebugStack") + return + } + case "stackFrames": + z.stackFrames, err = dc.ReadUint() + if err != nil { + err = msgp.WrapError(err, "stackFrames") + return + } + case "stackSkip": + z.stackSkip, err = dc.ReadUint() + if err != nil { + err = msgp.WrapError(err, "stackSkip") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z errorConfig) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "noDebugStack" + err = en.Append(0x83, 0xac, 0x6e, 0x6f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x53, 0x74, 0x61, 0x63, 0x6b) + if err != nil { + return + } + err = en.WriteBool(z.noDebugStack) + if err != nil { + err = msgp.WrapError(err, "noDebugStack") + return + } + // write "stackFrames" + err = en.Append(0xab, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73) + if err != nil { + return + } + err = en.WriteUint(z.stackFrames) + if err != nil { + err = msgp.WrapError(err, "stackFrames") + return + } + // write "stackSkip" + err = en.Append(0xa9, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x53, 0x6b, 0x69, 0x70) + if err != nil { + return + } + err = en.WriteUint(z.stackSkip) + if err != nil { + err = msgp.WrapError(err, "stackSkip") + return + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z errorConfig) Msgsize() (s int) { + s = 1 + 13 + msgp.BoolSize + 12 + msgp.UintSize + 10 + msgp.UintSize + return +} + // DecodeMsg implements msgp.Decodable func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { var field []byte @@ -20,52 +104,61 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { var zb0001 uint32 zb0001, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err) return } for zb0001 > 0 { zb0001-- field, err = dc.ReadMapKeyPtr() if err != nil { + err = msgp.WrapError(err) return } switch msgp.UnsafeString(field) { case "name": z.Name, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Name") return } case "service": z.Service, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Service") return } case "resource": z.Resource, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Resource") return } case "type": z.Type, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Type") return } case "start": z.Start, err = dc.ReadInt64() if err != nil { + err = msgp.WrapError(err, "Start") return } case "duration": z.Duration, err = dc.ReadInt64() if err != nil { + err = msgp.WrapError(err, "Duration") return } case "meta": var zb0002 uint32 zb0002, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err, "Meta") return } - if z.Meta == nil && zb0002 > 0 { + if z.Meta == nil { z.Meta = make(map[string]string, zb0002) } else if len(z.Meta) > 0 { for key := range z.Meta { @@ -78,10 +171,12 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { var za0002 string za0001, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Meta") return } za0002, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Meta", za0001) return } z.Meta[za0001] = za0002 @@ -90,9 +185,10 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { var zb0003 uint32 zb0003, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err, "Metrics") return } - if z.Metrics == nil && zb0003 > 0 { + if z.Metrics == nil { z.Metrics = make(map[string]float64, zb0003) } else if len(z.Metrics) > 0 { for key := range z.Metrics { @@ -105,10 +201,12 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { var za0004 float64 za0003, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Metrics") return } za0004, err = dc.ReadFloat64() if err != nil { + err = msgp.WrapError(err, "Metrics", za0003) return } z.Metrics[za0003] = za0004 @@ -116,26 +214,50 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { case "span_id": z.SpanID, err = dc.ReadUint64() if err != nil { + err = msgp.WrapError(err, "SpanID") return } case "trace_id": z.TraceID, err = dc.ReadUint64() if err != nil { + err = msgp.WrapError(err, "TraceID") return } case "parent_id": z.ParentID, err = dc.ReadUint64() if err != nil { + err = msgp.WrapError(err, "ParentID") return } case "error": z.Error, err = dc.ReadInt32() if err != nil { + err = msgp.WrapError(err, "Error") return } + case "span_links": + var zb0004 uint32 + zb0004, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "SpanLinks") + return + } + if cap(z.SpanLinks) >= int(zb0004) { + z.SpanLinks = (z.SpanLinks)[:zb0004] + } else { + z.SpanLinks = make([]ddtrace.SpanLink, zb0004) + } + for za0005 := range z.SpanLinks { + err = z.SpanLinks[za0005].DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, "SpanLinks", za0005) + return + } + } default: err = dc.Skip() if err != nil { + err = msgp.WrapError(err) return } } @@ -145,14 +267,33 @@ func (z *span) DecodeMsg(dc *msgp.Reader) (err error) { // EncodeMsg implements msgp.Encodable func (z *span) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 12 + // omitempty: check for empty values + zb0001Len := uint32(13) + var zb0001Mask uint16 /* 13 bits */ + if z.Meta == nil { + zb0001Len-- + zb0001Mask |= 0x40 + } + if z.Metrics == nil { + zb0001Len-- + zb0001Mask |= 0x80 + } + // variable map header, size zb0001Len + err = en.Append(0x80 | uint8(zb0001Len)) + if err != nil { + return + } + if zb0001Len == 0 { + return + } // write "name" - err = en.Append(0x8c, 0xa4, 0x6e, 0x61, 0x6d, 0x65) + err = en.Append(0xa4, 0x6e, 0x61, 0x6d, 0x65) if err != nil { return } err = en.WriteString(z.Name) if err != nil { + err = msgp.WrapError(err, "Name") return } // write "service" @@ -162,6 +303,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteString(z.Service) if err != nil { + err = msgp.WrapError(err, "Service") return } // write "resource" @@ -171,6 +313,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteString(z.Resource) if err != nil { + err = msgp.WrapError(err, "Resource") return } // write "type" @@ -180,6 +323,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteString(z.Type) if err != nil { + err = msgp.WrapError(err, "Type") return } // write "start" @@ -189,6 +333,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteInt64(z.Start) if err != nil { + err = msgp.WrapError(err, "Start") return } // write "duration" @@ -198,45 +343,56 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteInt64(z.Duration) if err != nil { + err = msgp.WrapError(err, "Duration") return } - // write "meta" - err = en.Append(0xa4, 0x6d, 0x65, 0x74, 0x61) - if err != nil { - return - } - err = en.WriteMapHeader(uint32(len(z.Meta))) - if err != nil { - return - } - for za0001, za0002 := range z.Meta { - err = en.WriteString(za0001) + if (zb0001Mask & 0x40) == 0 { // if not empty + // write "meta" + err = en.Append(0xa4, 0x6d, 0x65, 0x74, 0x61) if err != nil { return } - err = en.WriteString(za0002) + err = en.WriteMapHeader(uint32(len(z.Meta))) if err != nil { + err = msgp.WrapError(err, "Meta") return } + for za0001, za0002 := range z.Meta { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Meta") + return + } + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Meta", za0001) + return + } + } } - // write "metrics" - err = en.Append(0xa7, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73) - if err != nil { - return - } - err = en.WriteMapHeader(uint32(len(z.Metrics))) - if err != nil { - return - } - for za0003, za0004 := range z.Metrics { - err = en.WriteString(za0003) + if (zb0001Mask & 0x80) == 0 { // if not empty + // write "metrics" + err = en.Append(0xa7, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73) if err != nil { return } - err = en.WriteFloat64(za0004) + err = en.WriteMapHeader(uint32(len(z.Metrics))) if err != nil { + err = msgp.WrapError(err, "Metrics") return } + for za0003, za0004 := range z.Metrics { + err = en.WriteString(za0003) + if err != nil { + err = msgp.WrapError(err, "Metrics") + return + } + err = en.WriteFloat64(za0004) + if err != nil { + err = msgp.WrapError(err, "Metrics", za0003) + return + } + } } // write "span_id" err = en.Append(0xa7, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64) @@ -245,6 +401,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteUint64(z.SpanID) if err != nil { + err = msgp.WrapError(err, "SpanID") return } // write "trace_id" @@ -254,6 +411,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteUint64(z.TraceID) if err != nil { + err = msgp.WrapError(err, "TraceID") return } // write "parent_id" @@ -263,6 +421,7 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteUint64(z.ParentID) if err != nil { + err = msgp.WrapError(err, "ParentID") return } // write "error" @@ -272,8 +431,26 @@ func (z *span) EncodeMsg(en *msgp.Writer) (err error) { } err = en.WriteInt32(z.Error) if err != nil { + err = msgp.WrapError(err, "Error") + return + } + // write "span_links" + err = en.Append(0xaa, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73) + if err != nil { + return + } + err = en.WriteArrayHeader(uint32(len(z.SpanLinks))) + if err != nil { + err = msgp.WrapError(err, "SpanLinks") return } + for za0005 := range z.SpanLinks { + err = z.SpanLinks[za0005].EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, "SpanLinks", za0005) + return + } + } return } @@ -293,7 +470,10 @@ func (z *span) Msgsize() (s int) { s += msgp.StringPrefixSize + len(za0003) + msgp.Float64Size } } - s += 8 + msgp.Uint64Size + 9 + msgp.Uint64Size + 10 + msgp.Uint64Size + 6 + msgp.Int32Size + s += 8 + msgp.Uint64Size + 9 + msgp.Uint64Size + 10 + msgp.Uint64Size + 6 + msgp.Int32Size + 11 + msgp.ArrayHeaderSize + for za0005 := range z.SpanLinks { + s += z.SpanLinks[za0005].Msgsize() + } return } @@ -302,6 +482,7 @@ func (z *spanList) DecodeMsg(dc *msgp.Reader) (err error) { var zb0002 uint32 zb0002, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } if cap((*z)) >= int(zb0002) { @@ -313,6 +494,7 @@ func (z *spanList) DecodeMsg(dc *msgp.Reader) (err error) { if dc.IsNil() { err = dc.ReadNil() if err != nil { + err = msgp.WrapError(err, zb0001) return } (*z)[zb0001] = nil @@ -322,6 +504,7 @@ func (z *spanList) DecodeMsg(dc *msgp.Reader) (err error) { } err = (*z)[zb0001].DecodeMsg(dc) if err != nil { + err = msgp.WrapError(err, zb0001) return } } @@ -333,6 +516,7 @@ func (z *spanList) DecodeMsg(dc *msgp.Reader) (err error) { func (z spanList) EncodeMsg(en *msgp.Writer) (err error) { err = en.WriteArrayHeader(uint32(len(z))) if err != nil { + err = msgp.WrapError(err) return } for zb0003 := range z { @@ -344,6 +528,7 @@ func (z spanList) EncodeMsg(en *msgp.Writer) (err error) { } else { err = z[zb0003].EncodeMsg(en) if err != nil { + err = msgp.WrapError(err, zb0003) return } } @@ -369,6 +554,7 @@ func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) { var zb0003 uint32 zb0003, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } if cap((*z)) >= int(zb0003) { @@ -380,6 +566,7 @@ func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) { var zb0004 uint32 zb0004, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err, zb0001) return } if cap((*z)[zb0001]) >= int(zb0004) { @@ -391,6 +578,7 @@ func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) { if dc.IsNil() { err = dc.ReadNil() if err != nil { + err = msgp.WrapError(err, zb0001, zb0002) return } (*z)[zb0001][zb0002] = nil @@ -400,6 +588,7 @@ func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) { } err = (*z)[zb0001][zb0002].DecodeMsg(dc) if err != nil { + err = msgp.WrapError(err, zb0001, zb0002) return } } @@ -412,11 +601,13 @@ func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) { func (z spanLists) EncodeMsg(en *msgp.Writer) (err error) { err = en.WriteArrayHeader(uint32(len(z))) if err != nil { + err = msgp.WrapError(err) return } for zb0005 := range z { err = en.WriteArrayHeader(uint32(len(z[zb0005]))) if err != nil { + err = msgp.WrapError(err, zb0005) return } for zb0006 := range z[zb0005] { @@ -428,6 +619,7 @@ func (z spanLists) EncodeMsg(en *msgp.Writer) (err error) { } else { err = z[zb0005][zb0006].EncodeMsg(en) if err != nil { + err = msgp.WrapError(err, zb0005, zb0006) return } } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go index 99c8d7dc4..eea659e1e 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go @@ -6,38 +6,99 @@ package tracer import ( - "math" + "encoding/binary" + "encoding/hex" + "fmt" "strconv" + "strings" "sync" "sync/atomic" + "time" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + ginternal "gopkg.in/DataDog/dd-trace-go.v1/internal" + sharedinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) var _ ddtrace.SpanContext = (*spanContext)(nil) +type traceID [16]byte // traceID in big endian, i.e. + +var emptyTraceID traceID + +func (t *traceID) HexEncoded() string { + return hex.EncodeToString(t[:]) +} + +func (t *traceID) Lower() uint64 { + return binary.BigEndian.Uint64(t[8:]) +} + +func (t *traceID) Upper() uint64 { + return binary.BigEndian.Uint64(t[:8]) +} + +func (t *traceID) SetLower(i uint64) { + binary.BigEndian.PutUint64(t[8:], i) +} + +func (t *traceID) SetUpper(i uint64) { + binary.BigEndian.PutUint64(t[:8], i) +} + +func (t *traceID) SetUpperFromHex(s string) error { + u, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return fmt.Errorf("malformed %q: %s", s, err) + } + t.SetUpper(u) + return nil +} + +func (t *traceID) Empty() bool { + return *t == emptyTraceID +} + +func (t *traceID) HasUpper() bool { + //TODO: in go 1.20 we can simplify this + for _, b := range t[:8] { + if b != 0 { + return true + } + } + return false +} + +func (t *traceID) UpperHex() string { + return hex.EncodeToString(t[:8]) +} + // SpanContext represents a span state that can propagate to descendant spans // and across process boundaries. It contains all the information needed to // spawn a direct descendant of the span that it belongs to. It can be used // to create distributed tracing by propagating it using the provided interfaces. type spanContext struct { + updated bool // updated is tracking changes for priority / origin / x-datadog-tags + // the below group should propagate only locally trace *trace // reference to the trace that this span belongs too span *span // reference to the span that hosts this context - errors int64 // number of spans with errors in this trace + errors int32 // number of spans with errors in this trace // the below group should propagate cross-process - traceID uint64 + traceID traceID spanID uint64 mu sync.RWMutex // guards below fields baggage map[string]string - hasBaggage int32 // atomic int for quick checking presence of baggage. 0 indicates no baggage, otherwise baggage exists. + hasBaggage uint32 // atomic int for quick checking presence of baggage. 0 indicates no baggage, otherwise baggage exists. origin string // e.g. "synthetics" } @@ -48,11 +109,12 @@ type spanContext struct { // for the same span. func newSpanContext(span *span, parent *spanContext) *spanContext { context := &spanContext{ - traceID: span.TraceID, - spanID: span.SpanID, - span: span, + spanID: span.SpanID, + span: span, } + context.traceID.SetLower(span.TraceID) if parent != nil { + context.traceID.SetUpper(parent.traceID.Upper()) context.trace = parent.trace context.origin = parent.origin context.errors = parent.errors @@ -60,6 +122,15 @@ func newSpanContext(span *span, parent *spanContext) *spanContext { context.setBaggageItem(k, v) return true }) + } else if sharedinternal.BoolEnv("DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED", true) { + // add 128 bit trace id, if enabled, formatted as big-endian: + // <32-bit unix seconds> <32 bits of zero> <64 random bits> + id128 := time.Duration(span.Start) / time.Second + // casting from int64 -> uint32 should be safe since the start time won't be + // negative, and the seconds should fit within 32-bits for the foreseeable future. + // (We only want 32 bits of time, then the rest is zero) + tUp := uint64(uint32(id128)) << 32 // We need the time at the upper 32 bits of the uint + context.traceID.SetUpper(tUp) } if context.trace == nil { context.trace = newTrace() @@ -70,6 +141,10 @@ func newSpanContext(span *span, parent *spanContext) *spanContext { } // put span in context's trace context.trace.push(span) + // setting context.updated to false here is necessary to distinguish + // between initializing properties of the span (priority) + // and updating them after extracting context through propagators + context.updated = false return context } @@ -77,11 +152,24 @@ func newSpanContext(span *span, parent *spanContext) *spanContext { func (c *spanContext) SpanID() uint64 { return c.spanID } // TraceID implements ddtrace.SpanContext. -func (c *spanContext) TraceID() uint64 { return c.traceID } +func (c *spanContext) TraceID() uint64 { return c.traceID.Lower() } + +// TraceID128 implements ddtrace.SpanContextW3C. +func (c *spanContext) TraceID128() string { + if c == nil { + return "" + } + return c.traceID.HexEncoded() +} + +// TraceID128Bytes implements ddtrace.SpanContextW3C. +func (c *spanContext) TraceID128Bytes() [16]byte { + return c.traceID +} // ForeachBaggageItem implements ddtrace.SpanContext. func (c *spanContext) ForeachBaggageItem(handler func(k, v string) bool) { - if atomic.LoadInt32(&c.hasBaggage) == 0 { + if atomic.LoadUint32(&c.hasBaggage) == 0 { return } c.mu.RLock() @@ -93,14 +181,17 @@ func (c *spanContext) ForeachBaggageItem(handler func(k, v string) bool) { } } -func (c *spanContext) setSamplingPriority(p int, sampler samplernames.SamplerName, rate float64) { +func (c *spanContext) setSamplingPriority(p int, sampler samplernames.SamplerName) { if c.trace == nil { c.trace = newTrace() } - c.trace.setSamplingPriority(p, sampler, rate, c.span) + if c.trace.setSamplingPriority(p, sampler) { + // the trace's sampling priority was updated: mark this as updated + c.updated = true + } } -func (c *spanContext) samplingPriority() (p int, ok bool) { +func (c *spanContext) SamplingPriority() (p int, ok bool) { if c.trace == nil { return 0, false } @@ -111,14 +202,14 @@ func (c *spanContext) setBaggageItem(key, val string) { c.mu.Lock() defer c.mu.Unlock() if c.baggage == nil { - atomic.StoreInt32(&c.hasBaggage, 1) + atomic.StoreUint32(&c.hasBaggage, 1) c.baggage = make(map[string]string, 1) } c.baggage[key] = val } func (c *spanContext) baggageItem(key string) string { - if atomic.LoadInt32(&c.hasBaggage) == 0 { + if atomic.LoadUint32(&c.hasBaggage) == 0 { return "" } c.mu.RLock() @@ -127,8 +218,8 @@ func (c *spanContext) baggageItem(key string) string { } func (c *spanContext) meta(key string) (val string, ok bool) { - c.mu.RLock() - defer c.mu.RUnlock() + c.span.RLock() + defer c.span.RUnlock() val, ok = c.span.Meta[key] return val, ok } @@ -137,7 +228,7 @@ func (c *spanContext) meta(key string) (val string, ok bool) { func (c *spanContext) finish() { c.trace.finishedOne(c.span) } // samplingDecision is the decision to send a trace to the agent or not. -type samplingDecision int64 +type samplingDecision uint32 const ( // decisionNone is the default state of a trace. @@ -202,66 +293,68 @@ func (t *trace) samplingPriority() (p int, ok bool) { return t.samplingPriorityLocked() } -func (t *trace) setSamplingPriority(p int, sampler samplernames.SamplerName, rate float64, span *span) { +// setSamplingPriority sets the sampling priority and returns true if it was modified. +func (t *trace) setSamplingPriority(p int, sampler samplernames.SamplerName) bool { t.mu.Lock() defer t.mu.Unlock() - t.setSamplingPriorityLocked(p, sampler, rate, span) + return t.setSamplingPriorityLocked(p, sampler) } func (t *trace) keep() { - atomic.CompareAndSwapInt64((*int64)(&t.samplingDecision), int64(decisionNone), int64(decisionKeep)) + atomic.CompareAndSwapUint32((*uint32)(&t.samplingDecision), uint32(decisionNone), uint32(decisionKeep)) } func (t *trace) drop() { - atomic.CompareAndSwapInt64((*int64)(&t.samplingDecision), int64(decisionNone), int64(decisionDrop)) + atomic.CompareAndSwapUint32((*uint32)(&t.samplingDecision), uint32(decisionNone), uint32(decisionDrop)) } func (t *trace) setTag(key, value string) { - if t.tags == nil { - t.tags = make(map[string]string, 1) - } - t.tags[key] = value -} - -// setPropagatingTag sets the key/value pair as a trace propagating tag. -func (t *trace) setPropagatingTag(key, value string) { t.mu.Lock() defer t.mu.Unlock() - t.setPropagatingTagLocked(key, value) + t.setTagLocked(key, value) } -// setPropagatingTagLocked sets the key/value pair as a trace propagating tag. -// Not safe for concurrent use, setPropagatingTag should be used instead in that case. -func (t *trace) setPropagatingTagLocked(key, value string) { - if t.propagatingTags == nil { - t.propagatingTags = make(map[string]string, 1) +func (t *trace) setTagLocked(key, value string) { + if t.tags == nil { + t.tags = make(map[string]string, 1) } - t.propagatingTags[key] = value -} - -// unsetPropagatingTag deletes the key/value pair from the trace's propagated tags. -func (t *trace) unsetPropagatingTag(key string) { - t.mu.Lock() - defer t.mu.Unlock() - delete(t.propagatingTags, key) + t.tags[key] = value } -func (t *trace) setSamplingPriorityLocked(p int, sampler samplernames.SamplerName, rate float64, span *span) { +func (t *trace) setSamplingPriorityLocked(p int, sampler samplernames.SamplerName) bool { if t.locked { - return + return false } + + updatedPriority := t.priority == nil || *t.priority != float64(p) + if t.priority == nil { t.priority = new(float64) } *t.priority = float64(p) _, ok := t.propagatingTags[keyDecisionMaker] - if p > 0 && !ok { - // we have a positive priority and the sampling mechanism isn't set + if p > 0 && !ok && sampler != samplernames.Unknown { + // We have a positive priority and the sampling mechanism isn't set. + // Send nothing when sampler is `Unknown` for RFC compliance. t.setPropagatingTagLocked(keyDecisionMaker, "-"+strconv.Itoa(int(sampler))) } if p <= 0 && ok { delete(t.propagatingTags, keyDecisionMaker) } + + return updatedPriority +} + +func (t *trace) isLocked() bool { + t.mu.RLock() + defer t.mu.RUnlock() + return t.locked +} + +func (t *trace) setLocked(locked bool) { + t.mu.Lock() + defer t.mu.Unlock() + t.locked = locked } // push pushes a new span into the trace. If the buffer is full, it returns @@ -279,33 +372,69 @@ func (t *trace) push(sp *span) { t.spans = nil // GC log.Error("trace buffer full (%d), dropping trace", traceMaxSize) if haveTracer { - atomic.AddInt64(&tr.tracesDropped, 1) + atomic.AddUint32(&tr.tracesDropped, 1) } return } if v, ok := sp.Metrics[keySamplingPriority]; ok { - t.setSamplingPriorityLocked(int(v), samplernames.Upstream, math.NaN(), nil) + t.setSamplingPriorityLocked(int(v), samplernames.Unknown) } t.spans = append(t.spans, sp) if haveTracer { - atomic.AddInt64(&tr.spansStarted, 1) + atomic.AddUint32(&tr.spansStarted, 1) + } +} + +// setTraceTags sets all "trace level" tags on the provided span +// t must already be locked. +func (t *trace) setTraceTags(s *span, tr *tracer) { + for k, v := range t.tags { + s.setMeta(k, v) + } + for k, v := range t.propagatingTags { + s.setMeta(k, v) + } + for k, v := range ginternal.GetTracerGitMetadataTags() { + s.setMeta(k, v) + } + if s.context != nil && s.context.traceID.HasUpper() { + s.setMeta(keyTraceID128, s.context.traceID.UpperHex()) + } + if hn := tr.hostname(); hn != "" { + s.setMeta(keyTracerHostname, hn) } } // finishedOne acknowledges that another span in the trace has finished, and checks // if the trace is complete, in which case it calls the onFinish function. It uses -// the given priority, if non-nil, to mark the root span. +// the given priority, if non-nil, to mark the root span. This also will trigger a partial flush +// if enabled and the total number of finished spans is greater than or equal to the partial flush limit. +// The provided span must be locked. func (t *trace) finishedOne(s *span) { t.mu.Lock() defer t.mu.Unlock() + s.finished = true if t.full { // capacity has been reached, the buffer is no longer tracking // all the spans in the trace, so the below conditions will not // be accurate and would trigger a pre-mature flush, exposing us // to a race condition where spans can be modified while flushing. + // + // TODO(partialFlush): should we do a partial flush in this scenario? return } t.finished++ + tr, ok := internal.GetGlobalTracer().(*tracer) + if !ok { + return + } + setPeerService(s, tr.config) + + // attach the _dd.base_service tag only when the globally configured service name is different from the + // span service name. + if s.Service != "" && !strings.EqualFold(s.Service, tr.config.serviceName) { + s.Meta[keyBaseService] = tr.config.serviceName + } if s == t.root && t.priority != nil { // after the root has finished we lock down the priority; // we won't be able to make changes to a span after finishing @@ -319,28 +448,133 @@ func (t *trace) finishedOne(s *span) { // TODO(barbayar): make sure this doesn't happen in vain when switching to // the new wire format. We won't need to set the tags on the first span // in the chunk there. - for k, v := range t.tags { - s.setMeta(k, v) - } - for k, v := range t.propagatingTags { - s.setMeta(k, v) - } - } - if len(t.spans) != t.finished { - return + t.setTraceTags(s, tr) } - defer func() { + + if len(t.spans) == t.finished { // perform a full flush of all spans + t.finishChunk(tr, &chunk{ + spans: t.spans, + willSend: decisionKeep == samplingDecision(atomic.LoadUint32((*uint32)(&t.samplingDecision))), + }) t.spans = nil - t.finished = 0 // important, because a buffer can be used for several flushes - }() - tr, ok := internal.GetGlobalTracer().(*tracer) - if !ok { return } - // we have a tracer that can receive completed traces. - atomic.AddInt64(&tr.spansFinished, int64(len(t.spans))) - tr.pushTrace(&finishedTrace{ - spans: t.spans, - decision: samplingDecision(atomic.LoadInt64((*int64)(&t.samplingDecision))), + + doPartialFlush := tr.config.partialFlushEnabled && t.finished >= tr.config.partialFlushMinSpans + if !doPartialFlush { + return // The trace hasn't completed and partial flushing will not occur + } + log.Debug("Partial flush triggered with %d finished spans", t.finished) + telemetry.GlobalClient.Count(telemetry.NamespaceTracers, "trace_partial_flush.count", 1, []string{"reason:large_trace"}, true) + finishedSpans := make([]*span, 0, t.finished) + leftoverSpans := make([]*span, 0, len(t.spans)-t.finished) + for _, s2 := range t.spans { + if s2.finished { + finishedSpans = append(finishedSpans, s2) + } else { + leftoverSpans = append(leftoverSpans, s2) + } + } + // TODO: (Support MetricKindDist) Re-enable these when we actually support `MetricKindDist` + //telemetry.GlobalClient.Record(telemetry.NamespaceTracers, telemetry.MetricKindDist, "trace_partial_flush.spans_closed", float64(len(finishedSpans)), nil, true) + //telemetry.GlobalClient.Record(telemetry.NamespaceTracers, telemetry.MetricKindDist, "trace_partial_flush.spans_remaining", float64(len(leftoverSpans)), nil, true) + finishedSpans[0].setMetric(keySamplingPriority, *t.priority) + if s != t.spans[0] { + // Make sure the first span in the chunk has the trace-level tags + t.setTraceTags(finishedSpans[0], tr) + } + t.finishChunk(tr, &chunk{ + spans: finishedSpans, + willSend: decisionKeep == samplingDecision(atomic.LoadUint32((*uint32)(&t.samplingDecision))), }) + t.spans = leftoverSpans +} + +func (t *trace) finishChunk(tr *tracer, ch *chunk) { + atomic.AddUint32(&tr.spansFinished, uint32(len(ch.spans))) + tr.pushChunk(ch) + t.finished = 0 // important, because a buffer can be used for several flushes +} + +// setPeerService sets the peer.service, _dd.peer.service.source, and _dd.peer.service.remapped_from +// tags as applicable for the given span. +func setPeerService(s *span, cfg *config) { + if _, ok := s.Meta[ext.PeerService]; ok { // peer.service already set on the span + s.setMeta(keyPeerServiceSource, ext.PeerService) + } else { // no peer.service currently set + spanKind := s.Meta[ext.SpanKind] + isOutboundRequest := spanKind == ext.SpanKindClient || spanKind == ext.SpanKindProducer + shouldSetDefaultPeerService := isOutboundRequest && cfg.peerServiceDefaultsEnabled + if !shouldSetDefaultPeerService { + return + } + source := setPeerServiceFromSource(s) + if source == "" { + log.Debug("No source tag value could be found for span %q, peer.service not set", s.Name) + return + } + s.setMeta(keyPeerServiceSource, source) + } + // Overwrite existing peer.service value if remapped by the user + ps := s.Meta[ext.PeerService] + if to, ok := cfg.peerServiceMappings[ps]; ok { + s.setMeta(keyPeerServiceRemappedFrom, ps) + s.setMeta(ext.PeerService, to) + } +} + +// setPeerServiceFromSource sets peer.service from the sources determined +// by the tags on the span. It returns the source tag name that it used for +// the peer.service value, or the empty string if no valid source tag was available. +func setPeerServiceFromSource(s *span) string { + has := func(tag string) bool { + _, ok := s.Meta[tag] + return ok + } + var sources []string + useTargetHost := true + switch { + // order of the cases and their sources matters here. These are in priority order (highest to lowest) + case has("aws_service"): + sources = []string{ + "queuename", + "topicname", + "streamname", + "tablename", + "bucketname", + } + case s.Meta[ext.DBSystem] == ext.DBSystemCassandra: + sources = []string{ + ext.CassandraContactPoints, + } + useTargetHost = false + case has(ext.DBSystem): + sources = []string{ + ext.DBName, + ext.DBInstance, + } + case has(ext.MessagingSystem): + sources = []string{ + ext.KafkaBootstrapServers, + } + case has(ext.RPCSystem): + sources = []string{ + ext.RPCService, + } + } + // network destination tags will be used as fallback unless there are higher priority sources already set. + if useTargetHost { + sources = append(sources, []string{ + ext.NetworkDestinationName, + ext.PeerHostname, + ext.TargetHost, + }...) + } + for _, source := range sources { + if val, ok := s.Meta[source]; ok { + s.setMeta(ext.PeerService, val) + return source + } + } + return "" } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sqlcomment.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sqlcomment.go index 05cd0263d..72ad4229b 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sqlcomment.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sqlcomment.go @@ -12,92 +12,146 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" ) // SQLCommentInjectionMode represents the mode of SQL comment injection. -type SQLCommentInjectionMode string +// +// Deprecated: Use DBMPropagationMode instead. +type SQLCommentInjectionMode DBMPropagationMode const ( // SQLInjectionUndefined represents the comment injection mode is not set. This is the same as SQLInjectionDisabled. - SQLInjectionUndefined SQLCommentInjectionMode = "" + SQLInjectionUndefined SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeUndefined) // SQLInjectionDisabled represents the comment injection mode where all injection is disabled. - SQLInjectionDisabled SQLCommentInjectionMode = "disabled" + SQLInjectionDisabled SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeDisabled) // SQLInjectionModeService represents the comment injection mode where only service tags (name, env, version) are injected. - SQLInjectionModeService SQLCommentInjectionMode = "service" + SQLInjectionModeService SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeService) // SQLInjectionModeFull represents the comment injection mode where both service tags and tracing tags. Tracing tags include span id, trace id and sampling priority. - SQLInjectionModeFull SQLCommentInjectionMode = "full" + SQLInjectionModeFull SQLCommentInjectionMode = SQLCommentInjectionMode(DBMPropagationModeFull) +) + +// DBMPropagationMode represents the mode of dbm propagation. +// +// Note that enabling sql comment propagation results in potentially confidential data (service names) +// being stored in the databases which can then be accessed by other 3rd parties that have been granted +// access to the database. +type DBMPropagationMode string + +const ( + // DBMPropagationModeUndefined represents the dbm propagation mode not being set. This is the same as DBMPropagationModeDisabled. + DBMPropagationModeUndefined DBMPropagationMode = "" + // DBMPropagationModeDisabled represents the dbm propagation mode where all propagation is disabled. + DBMPropagationModeDisabled DBMPropagationMode = "disabled" + // DBMPropagationModeService represents the dbm propagation mode where only service tags (name, env, version) are propagated to dbm. + DBMPropagationModeService DBMPropagationMode = "service" + // DBMPropagationModeFull represents the dbm propagation mode where both service tags and tracing tags are propagated. Tracing tags include span id, trace id and the sampled flag. + DBMPropagationModeFull DBMPropagationMode = "full" ) // Key names for SQL comment tags. const ( - sqlCommentKeySamplingPriority = "ddsp" - sqlCommentTraceID = "ddtid" - sqlCommentSpanID = "ddsid" - sqlCommentService = "ddsn" - sqlCommentVersion = "ddsv" - sqlCommentEnv = "dde" + sqlCommentTraceParent = "traceparent" + sqlCommentParentService = "ddps" + sqlCommentDBService = "dddbs" + sqlCommentParentVersion = "ddpv" + sqlCommentEnv = "dde" + // These keys are for the database we are connecting to, instead of the service we are running in. + // "Peer" is the OpenTelemetry nomenclature for "thing I am talking to" + sqlCommentPeerHostname = "ddh" + sqlCommentPeerDBName = "dddb" ) +// Current trace context version (see https://www.w3.org/TR/trace-context/#version) +const w3cContextVersion = "00" + // SQLCommentCarrier is a carrier implementation that injects a span context in a SQL query in the form // of a sqlcommenter formatted comment prepended to the original query text. // See https://google.github.io/sqlcommenter/spec/ for more details. type SQLCommentCarrier struct { - Query string - Mode SQLCommentInjectionMode - SpanID uint64 + Query string + Mode DBMPropagationMode + DBServiceName string + SpanID uint64 + PeerDBHostname string + PeerDBName string } // Inject injects a span context in the carrier's Query field as a comment. func (c *SQLCommentCarrier) Inject(spanCtx ddtrace.SpanContext) error { - c.SpanID = random.Uint64() + c.SpanID = generateSpanID(now()) tags := make(map[string]string) switch c.Mode { - case SQLInjectionUndefined: + case DBMPropagationModeUndefined: fallthrough - case SQLInjectionDisabled: + case DBMPropagationModeDisabled: return nil - case SQLInjectionModeFull: + case DBMPropagationModeFull: var ( - samplingPriority int - traceID uint64 + sampled int64 + traceID uint64 ) if ctx, ok := spanCtx.(*spanContext); ok { - if sp, ok := ctx.samplingPriority(); ok { - samplingPriority = sp + if sp, ok := ctx.SamplingPriority(); ok && sp > 0 { + sampled = 1 } traceID = ctx.TraceID() } - if traceID == 0 { + if traceID == 0 { // check if this is a root span traceID = c.SpanID } - tags[sqlCommentTraceID] = strconv.FormatUint(traceID, 10) - tags[sqlCommentSpanID] = strconv.FormatUint(c.SpanID, 10) - tags[sqlCommentKeySamplingPriority] = strconv.Itoa(samplingPriority) + tags[sqlCommentTraceParent] = encodeTraceParent(traceID, c.SpanID, sampled) fallthrough - case SQLInjectionModeService: - var env, version string + case DBMPropagationModeService: if ctx, ok := spanCtx.(*spanContext); ok { - if e, ok := ctx.meta(ext.Environment); ok { - env = e + if e, ok := ctx.meta(ext.Environment); ok && e != "" { + tags[sqlCommentEnv] = e } - if v, ok := ctx.meta(ext.Version); ok { - version = v + if v, ok := ctx.meta(ext.Version); ok && v != "" { + tags[sqlCommentParentVersion] = v + } + if c.PeerDBName != "" { + tags[sqlCommentPeerDBName] = c.PeerDBName + } + if c.PeerDBHostname != "" { + tags[sqlCommentPeerHostname] = c.PeerDBHostname } } if globalconfig.ServiceName() != "" { - tags[sqlCommentService] = globalconfig.ServiceName() - } - if env != "" { - tags[sqlCommentEnv] = env - } - if version != "" { - tags[sqlCommentVersion] = version + tags[sqlCommentParentService] = globalconfig.ServiceName() } + tags[sqlCommentDBService] = c.DBServiceName } c.Query = commentQuery(c.Query, tags) return nil } +// encodeTraceParent encodes trace parent as per the w3c trace context spec (https://www.w3.org/TR/trace-context/#version). +func encodeTraceParent(traceID uint64, spanID uint64, sampled int64) string { + var b strings.Builder + // traceparent has a fixed length of 55: + // 2 bytes for the version, 32 for the trace id, 16 for the span id, 2 for the sampled flag and 3 for separators + b.Grow(55) + b.WriteString(w3cContextVersion) + b.WriteRune('-') + tid := strconv.FormatUint(traceID, 16) + for i := 0; i < 32-len(tid); i++ { + b.WriteRune('0') + } + b.WriteString(tid) + b.WriteRune('-') + sid := strconv.FormatUint(spanID, 16) + for i := 0; i < 16-len(sid); i++ { + b.WriteRune('0') + } + b.WriteString(sid) + b.WriteRune('-') + b.WriteRune('0') + b.WriteString(strconv.FormatInt(sampled, 16)) + return b.String() +} + var ( keyReplacer = strings.NewReplacer(" ", "%20", "!", "%21", "#", "%23", "$", "%24", "%", "%25", "&", "%26", "'", "%27", "(", "%28", ")", "%29", "*", "%2A", "+", "%2B", ",", "%2C", "/", "%2F", ":", "%3A", ";", "%3B", "=", "%3D", "?", "%3F", "@", "%40", "[", "%5B", "]", "%5D") valueReplacer = strings.NewReplacer(" ", "%20", "!", "%21", "#", "%23", "$", "%24", "%", "%25", "&", "%26", "'", "%27", "(", "%28", ")", "%29", "*", "%2A", "+", "%2B", ",", "%2C", "/", "%2F", ":", "%3A", ";", "%3B", "=", "%3D", "?", "%3F", "@", "%40", "[", "%5B", "]", "%5D", "'", "\\'") @@ -113,7 +167,7 @@ func commentQuery(query string, tags map[string]string) string { var b strings.Builder // the sqlcommenter specification dictates that tags should be sorted. Since we know all injected keys, // we skip a sorting operation by specifying the order of keys statically - orderedKeys := []string{sqlCommentEnv, sqlCommentSpanID, sqlCommentService, sqlCommentKeySamplingPriority, sqlCommentVersion, sqlCommentTraceID} + orderedKeys := []string{sqlCommentDBService, sqlCommentEnv, sqlCommentParentService, sqlCommentParentVersion, sqlCommentTraceParent, sqlCommentPeerHostname, sqlCommentPeerDBName} first := true for _, k := range orderedKeys { if v, ok := tags[k]; ok { @@ -141,12 +195,118 @@ func commentQuery(query string, tags map[string]string) string { if query == "" { return b.String() } + log.Debug("Injected sql comment: %s", b.String()) b.WriteRune(' ') b.WriteString(query) return b.String() } -// Extract is not implemented on SQLCommentCarrier +// Extract parses for key value attributes in a sql query injected with trace information in order to build a span context func (c *SQLCommentCarrier) Extract() (ddtrace.SpanContext, error) { - return nil, nil + var ctx *spanContext + // There may be multiple comments within the sql query, so we must identify which one contains trace information. + // We look at each comment until we find one that contains a traceparent + if traceComment, found := findTraceComment(c.Query); found { + var err error + if ctx, err = spanContextFromTraceComment(traceComment); err != nil { + return nil, err + } + } else { + return nil, ErrSpanContextNotFound + } + if ctx.traceID.Empty() || ctx.spanID == 0 { + return nil, ErrSpanContextNotFound + } + return ctx, nil +} + +// spanContextFromTraceComment looks for specific kv pairs in a comment containing trace information. +// It returns a span context with the appropriate attributes +func spanContextFromTraceComment(c string) (*spanContext, error) { + var ctx spanContext + kvs := strings.Split(c, ",") + for _, unparsedKV := range kvs { + splitKV := strings.Split(unparsedKV, "=") + if len(splitKV) != 2 { + return nil, ErrSpanContextCorrupted + } + key := splitKV[0] + value := strings.Trim(splitKV[1], "'") + switch key { + case sqlCommentTraceParent: + traceIDLower, traceIDUpper, spanID, sampled, err := decodeTraceParent(value) + if err != nil { + return nil, err + } + ctx.traceID.SetLower(traceIDLower) + ctx.traceID.SetUpper(traceIDUpper) + ctx.spanID = spanID + ctx.setSamplingPriority(sampled, samplernames.Unknown) + default: + } + } + return &ctx, nil +} + +// decodeTraceParent decodes trace parent as per the w3c trace context spec (https://www.w3.org/TR/trace-context/#version). +// this also supports decoding traceparents from open telemetry sql comments which are 128 bit +func decodeTraceParent(traceParent string) (traceIDLower uint64, traceIDUpper uint64, spanID uint64, sampled int, err error) { + if len(traceParent) < 55 { + return 0, 0, 0, 0, ErrSpanContextCorrupted + } + version := traceParent[0:2] + switch version { + case w3cContextVersion: + if traceIDUpper, err = strconv.ParseUint(traceParent[3:19], 16, 64); err != nil { + return 0, 0, 0, 0, ErrSpanContextCorrupted + } + if traceIDLower, err = strconv.ParseUint(traceParent[19:35], 16, 64); err != nil { + return 0, 0, 0, 0, ErrSpanContextCorrupted + } + if spanID, err = strconv.ParseUint(traceParent[36:52], 16, 64); err != nil { + return 0, 0, 0, 0, ErrSpanContextCorrupted + } + if sampled, err = strconv.Atoi(traceParent[53:55]); err != nil { + return 0, 0, 0, 0, ErrSpanContextCorrupted + } + default: + } + return traceIDLower, traceIDUpper, spanID, sampled, err +} + +// findTraceComment looks for a sql comment that contains trace information by looking for the keyword traceparent +func findTraceComment(query string) (traceComment string, found bool) { + startIndex := -1 + containsTrace := false + keyLength := len(sqlCommentTraceParent) + qLength := len(query) + for i := 0; i < qLength-1; { + if query[i] == '/' && query[i+1] == '*' { + // look for leading /* + startIndex = i + i += 2 + containsTrace = false + } else if query[i] == '*' && query[i+1] == '/' { + // look for closing */ + if startIndex == -1 { + // malformed comment, it did not have a leading /* + return "", false + } + if !containsTrace { + // ignore this comment, it was not a trace comment + startIndex = -1 + i += 2 + } else { + // do not return the query with the leading /* or trailing */ + return query[startIndex+2 : i], true + } + } else if !containsTrace && i+keyLength < qLength && query[i:i+keyLength] == sqlCommentTraceParent { + // look for occurrence of keyword in the query if not yet found and make sure we don't go out of range + containsTrace = true + i += keyLength + } else { + i++ + } + } + return "", false } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/stats.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/stats.go index 99029de55..720a2a023 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/stats.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/stats.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "github.com/DataDog/datadog-go/v5/statsd" @@ -52,12 +53,13 @@ type concentrator struct { buckets map[int64]*rawBucket // stopped reports whether the concentrator is stopped (when non-zero) - stopped uint64 + stopped uint32 - wg sync.WaitGroup // waits for any active goroutines - bucketSize int64 // the size of a bucket in nanoseconds - stop chan struct{} // closing this channel triggers shutdown - cfg *config // tracer startup configuration + wg sync.WaitGroup // waits for any active goroutines + bucketSize int64 // the size of a bucket in nanoseconds + stop chan struct{} // closing this channel triggers shutdown + cfg *config // tracer startup configuration + statsdClient internal.StatsdClient // statsd client for sending metrics. } // newConcentrator creates a new concentrator using the given tracer @@ -79,7 +81,7 @@ func alignTs(ts, bucketSize int64) int64 { return ts - ts%bucketSize } // Start starts the concentrator. A started concentrator needs to be stopped // in order to gracefully shut down, using Stop. func (c *concentrator) Start() { - if atomic.SwapUint64(&c.stopped, 0) == 0 { + if atomic.SwapUint32(&c.stopped, 0) == 0 { // already running log.Warn("(*concentrator).Start called more than once. This is likely a programming error.") return @@ -112,11 +114,11 @@ func (c *concentrator) runFlusher(tick <-chan time.Time) { } // statsd returns any tracer configured statsd client, or a no-op. -func (c *concentrator) statsd() statsdClient { - if c.cfg.statsd == nil { +func (c *concentrator) statsd() internal.StatsdClient { + if c.statsdClient == nil { return &statsd.NoOpClient{} } - return c.cfg.statsd + return c.statsdClient } // runIngester runs the loop which accepts incoming data on the concentrator's In @@ -149,7 +151,7 @@ func (c *concentrator) add(s *aggregableSpan) { // Stop stops the concentrator and blocks until the operation completes. func (c *concentrator) Stop() { - if atomic.SwapUint64(&c.stopped, 1) > 0 { + if atomic.SwapUint32(&c.stopped, 1) > 0 { return } close(c.stop) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/telemetry.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/telemetry.go new file mode 100644 index 000000000..30927b116 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/telemetry.go @@ -0,0 +1,104 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package tracer + +import ( + "fmt" + "strings" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" +) + +// startTelemetry starts the global instrumentation telemetry client with tracer data +// unless instrumentation telemetry is disabled via the DD_INSTRUMENTATION_TELEMETRY_ENABLED +// env var. +// If the telemetry client has already been started by the profiler, then +// an app-product-change event is sent with appsec information and an app-client-configuration-change +// event is sent with tracer config data. +// Note that the tracer is not considered as a standalone product by telemetry so we cannot send +// an app-product-change event for the tracer. +func startTelemetry(c *config) { + if telemetry.Disabled() { + // Do not do extra work populating config data if instrumentation telemetry is disabled. + return + } + telemetry.GlobalClient.ApplyOps( + telemetry.WithService(c.serviceName), + telemetry.WithEnv(c.env), + telemetry.WithHTTPClient(c.httpClient), + // c.logToStdout is true if serverless is turned on + telemetry.WithURL(c.logToStdout, c.agentURL.String()), + telemetry.WithVersion(c.version), + ) + telemetryConfigs := []telemetry.Configuration{ + {Name: "trace_debug_enabled", Value: c.debug}, + {Name: "agent_feature_drop_p0s", Value: c.agent.DropP0s}, + {Name: "stats_computation_enabled", Value: c.canComputeStats()}, + {Name: "dogstatsd_port", Value: c.agent.StatsdPort}, + {Name: "lambda_mode", Value: c.logToStdout}, + {Name: "send_retries", Value: c.sendRetries}, + {Name: "trace_startup_logs_enabled", Value: c.logStartup}, + {Name: "service", Value: c.serviceName}, + {Name: "universal_version", Value: c.universalVersion}, + {Name: "env", Value: c.env}, + {Name: "agent_url", Value: c.agentURL.String()}, + {Name: "agent_hostname", Value: c.hostname}, + {Name: "runtime_metrics_enabled", Value: c.runtimeMetrics}, + {Name: "dogstatsd_addr", Value: c.dogstatsdAddr}, + {Name: "trace_debug_enabled", Value: !c.noDebugStack}, + {Name: "profiling_hotspots_enabled", Value: c.profilerHotspots}, + {Name: "profiling_endpoints_enabled", Value: c.profilerEndpoints}, + {Name: "trace_span_attribute_schema", Value: c.spanAttributeSchemaVersion}, + {Name: "trace_peer_service_defaults_enabled", Value: c.peerServiceDefaultsEnabled}, + {Name: "orchestrion_enabled", Value: c.orchestrionCfg.Enabled}, + {Name: "trace_enabled", Value: c.enabled.current}, + c.traceSampleRate.toTelemetry(), + c.headerAsTags.toTelemetry(), + c.globalTags.toTelemetry(), + } + var peerServiceMapping []string + for key, value := range c.peerServiceMappings { + peerServiceMapping = append(peerServiceMapping, fmt.Sprintf("%s:%s", key, value)) + } + telemetryConfigs = append(telemetryConfigs, + telemetry.Configuration{Name: "trace_peer_service_mapping", Value: strings.Join(peerServiceMapping, ",")}) + + if chained, ok := c.propagator.(*chainedPropagator); ok { + telemetryConfigs = append(telemetryConfigs, + telemetry.Configuration{Name: "trace_propagation_style_inject", Value: chained.injectorNames}) + telemetryConfigs = append(telemetryConfigs, + telemetry.Configuration{Name: "trace_propagation_style_extract", Value: chained.extractorsNames}) + } + for k, v := range c.featureFlags { + telemetryConfigs = append(telemetryConfigs, telemetry.Configuration{Name: k, Value: v}) + } + for k, v := range c.serviceMappings { + telemetryConfigs = append(telemetryConfigs, telemetry.Configuration{Name: "service_mapping_" + k, Value: v}) + } + for k, v := range c.globalTags.get() { + telemetryConfigs = append(telemetryConfigs, telemetry.Configuration{Name: "global_tag_" + k, Value: v}) + } + rules := append(c.spanRules, c.traceRules...) + for _, rule := range rules { + var service string + var name string + if rule.Service != nil { + service = rule.Service.String() + } + if rule.Name != nil { + name = rule.Name.String() + } + telemetryConfigs = append(telemetryConfigs, + telemetry.Configuration{Name: fmt.Sprintf("sr_%s_(%s)_(%s)", rule.ruleType.String(), service, name), + Value: fmt.Sprintf("rate:%f_maxPerSecond:%f", rule.Rate, rule.MaxPerSecond)}) + } + if c.orchestrionCfg.Enabled { + for k, v := range c.orchestrionCfg.Metadata { + telemetryConfigs = append(telemetryConfigs, telemetry.Configuration{Name: "orchestrion_" + k, Value: v}) + } + } + telemetry.GlobalClient.ProductChange(telemetry.NamespaceTracers, true, telemetryConfigs) +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go index 3bca6ab73..76d540b97 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go @@ -7,14 +7,15 @@ package tracer import ( "fmt" - "math" "net/http" "os" + "regexp" "strconv" "strings" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" ) @@ -66,8 +67,12 @@ func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error { } const ( - headerPropagationStyleInject = "DD_PROPAGATION_STYLE_INJECT" - headerPropagationStyleExtract = "DD_PROPAGATION_STYLE_EXTRACT" + headerPropagationStyleInject = "DD_TRACE_PROPAGATION_STYLE_INJECT" + headerPropagationStyleExtract = "DD_TRACE_PROPAGATION_STYLE_EXTRACT" + headerPropagationStyle = "DD_TRACE_PROPAGATION_STYLE" + + headerPropagationStyleInjectDeprecated = "DD_PROPAGATION_STYLE_INJECT" // deprecated + headerPropagationStyleExtractDeprecated = "DD_PROPAGATION_STYLE_EXTRACT" // deprecated ) const ( @@ -128,6 +133,13 @@ type PropagatorConfig struct { // NewPropagator returns a new propagator which uses TextMap to inject // and extract values. It propagates trace and span IDs and baggage. // To use the defaults, nil may be provided in place of the config. +// +// The inject and extract propagators are determined using environment variables +// with the following order of precedence: +// 1. DD_TRACE_PROPAGATION_STYLE_INJECT +// 2. DD_PROPAGATION_STYLE_INJECT (deprecated) +// 3. DD_TRACE_PROPAGATION_STYLE (applies to both inject and extract) +// 4. If none of the above, use default values func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator { if cfg == nil { cfg = new(PropagatorConfig) @@ -144,62 +156,98 @@ func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator if cfg.PriorityHeader == "" { cfg.PriorityHeader = DefaultPriorityHeader } + cp := new(chainedPropagator) + cp.onlyExtractFirst = internal.BoolEnv("DD_TRACE_PROPAGATION_EXTRACT_FIRST", false) if len(propagators) > 0 { - return &chainedPropagator{ - injectors: propagators, - extractors: propagators, + cp.injectors = propagators + cp.extractors = propagators + return cp + } + injectorsPs := os.Getenv(headerPropagationStyleInject) + if injectorsPs == "" { + if injectorsPs = os.Getenv(headerPropagationStyleInjectDeprecated); injectorsPs != "" { + log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleInjectDeprecated, headerPropagationStyleInject, headerPropagationStyle) } } - return &chainedPropagator{ - injectors: getPropagators(cfg, headerPropagationStyleInject), - extractors: getPropagators(cfg, headerPropagationStyleExtract), + extractorsPs := os.Getenv(headerPropagationStyleExtract) + if extractorsPs == "" { + if extractorsPs = os.Getenv(headerPropagationStyleExtractDeprecated); extractorsPs != "" { + log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleExtractDeprecated, headerPropagationStyleExtract, headerPropagationStyle) + } } + cp.injectors, cp.injectorNames = getPropagators(cfg, injectorsPs) + cp.extractors, cp.extractorsNames = getPropagators(cfg, extractorsPs) + return cp } // chainedPropagator implements Propagator and applies a list of injectors and extractors. // When injecting, all injectors are called to propagate the span context. // When extracting, it tries each extractor, selecting the first successful one. type chainedPropagator struct { - injectors []Propagator - extractors []Propagator + injectors []Propagator + extractors []Propagator + injectorNames string + extractorsNames string + onlyExtractFirst bool // value of DD_TRACE_PROPAGATION_EXTRACT_FIRST } -// getPropagators returns a list of propagators based on the list found in the -// given environment variable. If the list doesn't contain any valid values the +// getPropagators returns a list of propagators based on ps, which is a comma seperated +// list of propagators. If the list doesn't contain any valid values, the // default propagator will be returned. Any invalid values in the list will log // a warning and be ignored. -func getPropagators(cfg *PropagatorConfig, env string) []Propagator { +func getPropagators(cfg *PropagatorConfig, ps string) ([]Propagator, string) { dd := &propagator{cfg} - ps := os.Getenv(env) - defaultPs := []Propagator{dd} + defaultPs := []Propagator{dd, &propagatorW3c{}} + defaultPsName := "datadog,tracecontext" if cfg.B3 { defaultPs = append(defaultPs, &propagatorB3{}) + defaultPsName += ",b3" } if ps == "" { - return defaultPs + if prop := os.Getenv(headerPropagationStyle); prop != "" { + ps = prop // use the generic DD_TRACE_PROPAGATION_STYLE if set + } else { + return defaultPs, defaultPsName // no env set, so use default from configuration + } + } + ps = strings.ToLower(ps) + if ps == "none" { + return nil, "" } var list []Propagator + var listNames []string if cfg.B3 { list = append(list, &propagatorB3{}) + listNames = append(listNames, "b3") } for _, v := range strings.Split(ps, ",") { - switch strings.ToLower(v) { + switch v := strings.ToLower(v); v { case "datadog": list = append(list, dd) - case "b3": + listNames = append(listNames, v) + case "tracecontext": + list = append(list, &propagatorW3c{}) + listNames = append(listNames, v) + case "b3", "b3multi": if !cfg.B3 { // propagatorB3 hasn't already been added, add a new one. list = append(list, &propagatorB3{}) + listNames = append(listNames, v) } + case "b3 single header": + list = append(list, &propagatorB3SingleHeader{}) + listNames = append(listNames, v) + case "none": + log.Warn("Propagator \"none\" has no effect when combined with other propagators. " + + "To disable the propagator, set to `none`") default: log.Warn("unrecognized propagator: %s\n", v) } } if len(list) == 0 { - // return the default - return defaultPs + return defaultPs, defaultPsName // no valid propagators, so return default } - return list + return list, strings.Join(listNames, ",") } // Inject defines the Propagator to propagate SpanContext data @@ -215,21 +263,70 @@ func (p *chainedPropagator) Inject(spanCtx ddtrace.SpanContext, carrier interfac return nil } -// Extract implements Propagator. +// Extract implements Propagator. This method will attempt to extract the context +// based on the precedence order of the propagators. Generally, the first valid +// trace context that could be extracted will be returned, and other extractors will +// be ignored. However, the W3C tracestate header value will always be extracted and +// stored in the local trace context even if a previous propagator has already succeeded +// so long as the trace-ids match. func (p *chainedPropagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + var ctx ddtrace.SpanContext for _, v := range p.extractors { - ctx, err := v.Extract(carrier) if ctx != nil { - // first extractor returns - log.Debug("Extracted span context: %#v", ctx) - return ctx, nil + // A local trace context has already been extracted. + p, isW3C := v.(*propagatorW3c) + if !isW3C { + continue // Ignore other propagators. + } + p.propagateTracestate(ctx.(*spanContext), carrier) + break } - if err == ErrSpanContextNotFound { - continue + var err error + ctx, err = v.Extract(carrier) + if ctx != nil { + if p.onlyExtractFirst { + // Return early if the customer configured that only the first successful + // extraction should occur. + return ctx, nil + } + } else if err != ErrSpanContextNotFound { + return nil, err } - return nil, err } - return nil, ErrSpanContextNotFound + if ctx == nil { + return nil, ErrSpanContextNotFound + } + log.Debug("Extracted span context: %#v", ctx) + return ctx, nil +} + +// propagateTracestate will add the tracestate propagating tag to the given +// *spanContext. The W3C trace context will be extracted from the provided +// carrier. The trace id of this W3C trace context must match the trace id +// provided by the given *spanContext. If it matches, then the tracestate +// will be re-composed based on the composition of the given *spanContext, +// but will include the non-DD vendors in the W3C trace context's tracestate. +func (p *propagatorW3c) propagateTracestate(ctx *spanContext, carrier interface{}) { + w3cCtx, _ := p.Extract(carrier) + if w3cCtx == nil { + return // It's not valid, so ignore it. + } + if ctx.TraceID() != w3cCtx.TraceID() { + return // The trace-ids must match. + } + if w3cCtx.(*spanContext).trace == nil { + return // this shouldn't happen, since it should have a propagating tag already + } + if ctx.trace == nil { + ctx.trace = newTrace() + } + // Get the tracestate header from extracted w3C context, and propagate + // it to the span context that will be returned. + // Note: Other trace context fields like sampling priority, propagated tags, + // and origin will remain unchanged. + ts := w3cCtx.(*spanContext).trace.propagatingTag(tracestateHeader) + priority, _ := ctx.SamplingPriority() + setPropagatingTag(ctx, tracestateHeader, composeTracestate(ctx, priority, ts)) } // propagator implements Propagator and injects/extracts span contexts @@ -249,22 +346,28 @@ func (p *propagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) er func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error { ctx, ok := spanCtx.(*spanContext) - if !ok || ctx.traceID == 0 || ctx.spanID == 0 { + if !ok || ctx.traceID.Empty() || ctx.spanID == 0 { return ErrInvalidSpanContext } // propagate the TraceID and the current active SpanID - writer.Set(p.cfg.TraceHeader, strconv.FormatUint(ctx.traceID, 10)) + if ctx.traceID.HasUpper() { + setPropagatingTag(ctx, keyTraceID128, ctx.traceID.UpperHex()) + } else if ctx.trace != nil { + ctx.trace.unsetPropagatingTag(keyTraceID128) + } + writer.Set(p.cfg.TraceHeader, strconv.FormatUint(ctx.traceID.Lower(), 10)) writer.Set(p.cfg.ParentHeader, strconv.FormatUint(ctx.spanID, 10)) - if sp, ok := ctx.samplingPriority(); ok { + if sp, ok := ctx.SamplingPriority(); ok { writer.Set(p.cfg.PriorityHeader, strconv.Itoa(sp)) } if ctx.origin != "" { writer.Set(originHeader, ctx.origin) } - // propagate OpenTracing baggage - for k, v := range ctx.baggage { + ctx.ForeachBaggageItem(func(k, v string) bool { + // Propagate OpenTracing baggage. writer.Set(p.cfg.BaggagePrefix+k, v) - } + return true + }) if p.cfg.MaxTagsHeaderLen <= 0 { return nil } @@ -280,19 +383,22 @@ func (p *propagator) marshalPropagatingTags(ctx *spanContext) string { if ctx.trace == nil { return "" } - ctx.trace.mu.Lock() - defer ctx.trace.mu.Unlock() - for k, v := range ctx.trace.propagatingTags { + + var properr string + ctx.trace.iteratePropagatingTags(func(k, v string) bool { + if k == tracestateHeader || k == traceparentHeader { + return true // don't propagate W3C headers with the DD propagator + } if err := isValidPropagatableTag(k, v); err != nil { log.Warn("Won't propagate tag '%s': %v", k, err.Error()) - ctx.trace.setTag(keyPropagationError, "encoding_error") - continue + properr = "encoding_error" + return true } - if sb.Len()+len(k)+len(v) > p.cfg.MaxTagsHeaderLen { + if tagLen := sb.Len() + len(k) + len(v); tagLen > p.cfg.MaxTagsHeaderLen { sb.Reset() - log.Warn("Won't propagate tag: maximum trace tags header len (%d) reached.", p.cfg.MaxTagsHeaderLen) - ctx.trace.setTag(keyPropagationError, "inject_max_size") - break + log.Warn("Won't propagate tag: length is (%d) which exceeds the maximum len of (%d).", tagLen, p.cfg.MaxTagsHeaderLen) + properr = "inject_max_size" + return false } if sb.Len() > 0 { sb.WriteByte(',') @@ -300,6 +406,10 @@ func (p *propagator) marshalPropagatingTags(ctx *spanContext) string { sb.WriteString(k) sb.WriteByte('=') sb.WriteString(v) + return true + }) + if properr != "" { + ctx.trace.setTag(keyPropagationError, properr) } return sb.String() } @@ -320,10 +430,12 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, key := strings.ToLower(k) switch key { case p.cfg.TraceHeader: - ctx.traceID, err = parseUint64(v) + var lowerTid uint64 + lowerTid, err = parseUint64(v) if err != nil { return ErrSpanContextCorrupted } + ctx.traceID.SetLower(lowerTid) case p.cfg.ParentHeader: ctx.spanID, err = parseUint64(v) if err != nil { @@ -334,7 +446,7 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, if err != nil { return ErrSpanContextCorrupted } - ctx.setSamplingPriority(priority, samplernames.Upstream, math.NaN()) + ctx.setSamplingPriority(priority, samplernames.Unknown) case originHeader: ctx.origin = v case traceTagsHeader: @@ -349,36 +461,65 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, if err != nil { return nil, err } - if ctx.traceID == 0 || (ctx.spanID == 0 && ctx.origin != "synthetics") { + if ctx.trace != nil { + tid := ctx.trace.propagatingTag(keyTraceID128) + if err := validateTID(tid); err != nil { + log.Debug("Invalid hex traceID: %s", err) + ctx.trace.unsetPropagatingTag(keyTraceID128) + } else if err := ctx.traceID.SetUpperFromHex(tid); err != nil { + log.Debug("Attempted to set an invalid hex traceID: %s", err) + ctx.trace.unsetPropagatingTag(keyTraceID128) + } + } + if ctx.traceID.Empty() || (ctx.spanID == 0 && ctx.origin != "synthetics") { return nil, ErrSpanContextNotFound } return &ctx, nil } +func validateTID(tid string) error { + if len(tid) != 16 { + return fmt.Errorf("invalid length: %q", tid) + } + if !isValidID(tid) { + return fmt.Errorf("malformed: %q", tid) + } + return nil +} + // unmarshalPropagatingTags unmarshals tags from v into ctx func unmarshalPropagatingTags(ctx *spanContext, v string) { if ctx.trace == nil { ctx.trace = newTrace() } - ctx.trace.mu.Lock() - defer ctx.trace.mu.Unlock() if len(v) > propagationExtractMaxSize { log.Warn("Did not extract %s, size limit exceeded: %d. Incoming tags will not be propagated further.", traceTagsHeader, propagationExtractMaxSize) ctx.trace.setTag(keyPropagationError, "extract_max_size") return } - var err error - ctx.trace.propagatingTags, err = parsePropagatableTraceTags(v) + tags, err := parsePropagatableTraceTags(v) if err != nil { log.Warn("Did not extract %s: %v. Incoming tags will not be propagated further.", traceTagsHeader, err.Error()) ctx.trace.setTag(keyPropagationError, "decoding_error") } + ctx.trace.replacePropagatingTags(tags) +} + +// setPropagatingTag adds the key value pair to the map of propagating tags on the trace, +// creating the map if one is not initialized. +func setPropagatingTag(ctx *spanContext, k, v string) { + if ctx.trace == nil { + // extractors initialize a new spanContext, so the trace might be nil + ctx.trace = newTrace() + } + ctx.trace.setPropagatingTag(k, v) } const ( b3TraceIDHeader = "x-b3-traceid" b3SpanIDHeader = "x-b3-spanid" b3SampledHeader = "x-b3-sampled" + b3SingleHeader = "b3" ) // propagatorB3 implements Propagator and injects/extracts span contexts @@ -396,12 +537,20 @@ func (p *propagatorB3) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) func (*propagatorB3) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error { ctx, ok := spanCtx.(*spanContext) - if !ok || ctx.traceID == 0 || ctx.spanID == 0 { + if !ok || ctx.traceID.Empty() || ctx.spanID == 0 { return ErrInvalidSpanContext } - writer.Set(b3TraceIDHeader, fmt.Sprintf("%016x", ctx.traceID)) + if !ctx.traceID.HasUpper() { // 64-bit trace id + writer.Set(b3TraceIDHeader, fmt.Sprintf("%016x", ctx.traceID.Lower())) + } else { // 128-bit trace id + var w3Cctx ddtrace.SpanContextW3C + if w3Cctx, ok = spanCtx.(ddtrace.SpanContextW3C); !ok { + return ErrInvalidSpanContext + } + writer.Set(b3TraceIDHeader, w3Cctx.TraceID128()) + } writer.Set(b3SpanIDHeader, fmt.Sprintf("%016x", ctx.spanID)) - if p, ok := ctx.samplingPriority(); ok { + if p, ok := ctx.SamplingPriority(); ok { if p >= ext.PriorityAutoKeep { writer.Set(b3SampledHeader, "1") } else { @@ -427,12 +576,8 @@ func (*propagatorB3) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, key := strings.ToLower(k) switch key { case b3TraceIDHeader: - if len(v) > 16 { - v = v[len(v)-16:] - } - ctx.traceID, err = strconv.ParseUint(v, 16, 64) - if err != nil { - return ErrSpanContextCorrupted + if err := extractTraceID128(&ctx, v); err != nil { + return nil } case b3SpanIDHeader: ctx.spanID, err = strconv.ParseUint(v, 16, 64) @@ -444,7 +589,7 @@ func (*propagatorB3) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, if err != nil { return ErrSpanContextCorrupted } - ctx.setSamplingPriority(priority, samplernames.Upstream, math.NaN()) + ctx.setSamplingPriority(priority, samplernames.Unknown) default: } return nil @@ -452,8 +597,489 @@ func (*propagatorB3) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, if err != nil { return nil, err } - if ctx.traceID == 0 || ctx.spanID == 0 { + if ctx.traceID.Empty() || ctx.spanID == 0 { return nil, ErrSpanContextNotFound } return &ctx, nil } + +// propagatorB3 implements Propagator and injects/extracts span contexts +// using B3 headers. Only TextMap carriers are supported. +type propagatorB3SingleHeader struct{} + +func (p *propagatorB3SingleHeader) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error { + switch c := carrier.(type) { + case TextMapWriter: + return p.injectTextMap(spanCtx, c) + default: + return ErrInvalidCarrier + } +} + +func (*propagatorB3SingleHeader) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error { + ctx, ok := spanCtx.(*spanContext) + if !ok || ctx.traceID.Empty() || ctx.spanID == 0 { + return ErrInvalidSpanContext + } + sb := strings.Builder{} + var traceID string + if !ctx.traceID.HasUpper() { // 64-bit trace id + traceID = fmt.Sprintf("%016x", ctx.traceID.Lower()) + } else { // 128-bit trace id + var w3Cctx ddtrace.SpanContextW3C + if w3Cctx, ok = spanCtx.(ddtrace.SpanContextW3C); !ok { + return ErrInvalidSpanContext + } + traceID = w3Cctx.TraceID128() + } + sb.WriteString(fmt.Sprintf("%s-%016x", traceID, ctx.spanID)) + if p, ok := ctx.SamplingPriority(); ok { + if p >= ext.PriorityAutoKeep { + sb.WriteString("-1") + } else { + sb.WriteString("-0") + } + } + writer.Set(b3SingleHeader, sb.String()) + return nil +} + +func (p *propagatorB3SingleHeader) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + switch c := carrier.(type) { + case TextMapReader: + return p.extractTextMap(c) + default: + return nil, ErrInvalidCarrier + } +} + +func (*propagatorB3SingleHeader) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) { + var ctx spanContext + err := reader.ForeachKey(func(k, v string) error { + var err error + key := strings.ToLower(k) + switch key { + case b3SingleHeader: + b3Parts := strings.Split(v, "-") + if len(b3Parts) >= 2 { + if err = extractTraceID128(&ctx, b3Parts[0]); err != nil { + return err + } + ctx.spanID, err = strconv.ParseUint(b3Parts[1], 16, 64) + if err != nil { + return ErrSpanContextCorrupted + } + if len(b3Parts) >= 3 { + switch b3Parts[2] { + case "": + break + case "1", "d": // Treat 'debug' traces as priority 1 + ctx.setSamplingPriority(1, samplernames.Unknown) + case "0": + ctx.setSamplingPriority(0, samplernames.Unknown) + default: + return ErrSpanContextCorrupted + } + } + } else { + return ErrSpanContextCorrupted + } + default: + } + return nil + }) + if err != nil { + return nil, err + } + if ctx.traceID.Empty() || ctx.spanID == 0 { + return nil, ErrSpanContextNotFound + } + return &ctx, nil +} + +const ( + traceparentHeader = "traceparent" + tracestateHeader = "tracestate" +) + +// propagatorW3c implements Propagator and injects/extracts span contexts +// using W3C tracecontext/traceparent headers. Only TextMap carriers are supported. +type propagatorW3c struct{} + +func (p *propagatorW3c) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error { + switch c := carrier.(type) { + case TextMapWriter: + return p.injectTextMap(spanCtx, c) + default: + return ErrInvalidCarrier + } +} + +// injectTextMap propagates span context attributes into the writer, +// in the format of the traceparentHeader and tracestateHeader. +// traceparentHeader encodes W3C Trace Propagation version, 128-bit traceID, +// spanID, and a flags field, which supports 8 unique flags. +// The current specification only supports a single flag called sampled, +// which is equal to 00000001 when no other flag is present. +// tracestateHeader is a comma-separated list of list-members with a = format, +// where each list-member is managed by a vendor or instrumentation library. +func (*propagatorW3c) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error { + ctx, ok := spanCtx.(*spanContext) + if !ok || ctx.traceID.Empty() || ctx.spanID == 0 { + return ErrInvalidSpanContext + } + flags := "" + p, ok := ctx.SamplingPriority() + if ok && p >= ext.PriorityAutoKeep { + flags = "01" + } else { + flags = "00" + } + + var traceID string + if ctx.traceID.HasUpper() { + setPropagatingTag(ctx, keyTraceID128, ctx.traceID.UpperHex()) + if w3Cctx, ok := spanCtx.(ddtrace.SpanContextW3C); ok { + traceID = w3Cctx.TraceID128() + } + } else { + traceID = fmt.Sprintf("%032x", ctx.traceID) + if ctx.trace != nil { + ctx.trace.unsetPropagatingTag(keyTraceID128) + } + } + writer.Set(traceparentHeader, fmt.Sprintf("00-%s-%016x-%v", traceID, ctx.spanID, flags)) + // if context priority / origin / tags were updated after extraction, + // or the tracestateHeader doesn't start with `dd=` + // we need to recreate tracestate + if ctx.updated || + (ctx.trace != nil && !strings.HasPrefix(ctx.trace.propagatingTag(tracestateHeader), "dd=")) || + ctx.trace.propagatingTagsLen() == 0 { + writer.Set(tracestateHeader, composeTracestate(ctx, p, ctx.trace.propagatingTag(tracestateHeader))) + } else { + writer.Set(tracestateHeader, ctx.trace.propagatingTag(tracestateHeader)) + } + return nil +} + +var ( + // keyRgx is used to sanitize the keys of the datadog propagating tags. + // Disallowed characters are comma (reserved as a list-member separator), + // equals (reserved for list-member key-value separator), + // space and characters outside the ASCII range 0x20 to 0x7E. + // Disallowed characters must be replaced with the underscore. + keyRgx = regexp.MustCompile(",|=|[^\\x20-\\x7E]+") + + // valueRgx is used to sanitize the values of the datadog propagating tags. + // Disallowed characters are comma (reserved as a list-member separator), + // semi-colon (reserved for separator between entries in the dd list-member), + // tilde (reserved, will represent 0x3D (equals) in the encoded tag value, + // and characters outside the ASCII range 0x20 to 0x7E. + // Equals character must be encoded with a tilde. + // Other disallowed characters must be replaced with the underscore. + valueRgx = regexp.MustCompile(",|;|~|[^\\x20-\\x7E]+") + + // originRgx is used to sanitize the value of the datadog origin tag. + // Disallowed characters are comma (reserved as a list-member separator), + // semi-colon (reserved for separator between entries in the dd list-member), + // equals (reserved for list-member key-value separator), + // and characters outside the ASCII range 0x21 to 0x7E. + // Equals character must be encoded with a tilde. + // Other disallowed characters must be replaced with the underscore. + originRgx = regexp.MustCompile(",|~|;|[^\\x21-\\x7E]+") +) + +const ( + asciiLowerA = 97 + asciiLowerF = 102 + asciiZero = 48 + asciiNine = 57 +) + +// isValidID is used to verify that the input is a valid hex string. +// This is an equivalent check to the regexp ^[a-f0-9]+$ +// In benchmarks, this function is roughly 10x faster than the equivalent +// regexp, which is why we split it out. +// isValidID is applicable for both trace and span IDs. +func isValidID(id string) bool { + if len(id) == 0 { + return false + } + + for _, c := range id { + ascii := int(c) + if ascii < asciiZero || ascii > asciiLowerF || (ascii > asciiNine && ascii < asciiLowerA) { + return false + } + } + + return true +} + +// composeTracestate creates a tracestateHeader from the spancontext. +// The Datadog tracing library is only responsible for managing the list member with key dd, +// which holds the values of the sampling decision(`s:`), origin(`o:`), +// and propagated tags prefixed with `t.`(e.g. _dd.p.usr.id:usr_id tag will become `t.usr.id:usr_id`). +func composeTracestate(ctx *spanContext, priority int, oldState string) string { + var b strings.Builder + b.Grow(128) + b.WriteString(fmt.Sprintf("dd=s:%d", priority)) + listLength := 1 + + if ctx.origin != "" { + oWithSub := originRgx.ReplaceAllString(ctx.origin, "_") + b.WriteString(fmt.Sprintf(";o:%s", + strings.ReplaceAll(oWithSub, "=", "~"))) + } + + ctx.trace.iteratePropagatingTags(func(k, v string) bool { + if !strings.HasPrefix(k, "_dd.p.") { + return true + } + // Datadog propagating tags must be appended to the tracestateHeader + // with the `t.` prefix. Tag value must have all `=` signs replaced with a tilde (`~`). + tag := fmt.Sprintf("t.%s:%s", + keyRgx.ReplaceAllString(k[len("_dd.p."):], "_"), + strings.ReplaceAll(valueRgx.ReplaceAllString(v, "_"), "=", "~")) + if b.Len()+len(tag) > 256 { + return false + } + b.WriteString(";") + b.WriteString(tag) + return true + }) + // the old state is split by vendors, must be concatenated with a `,` + if len(oldState) == 0 { + return b.String() + } + for _, s := range strings.Split(strings.Trim(oldState, " \t"), ",") { + if strings.HasPrefix(s, "dd=") { + continue + } + listLength++ + // if the resulting tracestateHeader exceeds 32 list-members, + // remove the rightmost list-member(s) + if listLength > 32 { + break + } + b.WriteString("," + strings.Trim(s, " \t")) + } + return b.String() +} + +func (p *propagatorW3c) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + switch c := carrier.(type) { + case TextMapReader: + return p.extractTextMap(c) + default: + return nil, ErrInvalidCarrier + } +} + +func (*propagatorW3c) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) { + var parentHeader string + var stateHeader string + var ctx spanContext + // to avoid parsing tracestate header(s) if traceparent is invalid + if err := reader.ForeachKey(func(k, v string) error { + key := strings.ToLower(k) + switch key { + case traceparentHeader: + if parentHeader != "" { + return ErrSpanContextCorrupted + } + parentHeader = v + case tracestateHeader: + stateHeader = v + default: + if strings.HasPrefix(key, DefaultBaggageHeaderPrefix) { + ctx.setBaggageItem(strings.TrimPrefix(key, DefaultBaggageHeaderPrefix), v) + } + } + return nil + }); err != nil { + return nil, err + } + if err := parseTraceparent(&ctx, parentHeader); err != nil { + return nil, err + } + parseTracestate(&ctx, stateHeader) + return &ctx, nil +} + +// parseTraceparent attempts to parse traceparentHeader which describes the position +// of the incoming request in its trace graph in a portable, fixed-length format. +// The format of the traceparentHeader is `-` separated string with in the +// following format: `version-traceId-spanID-flags`, with an optional `-` if version > 0. +// where: +// - version - represents the version of the W3C Tracecontext Propagation format in hex format. +// - traceId - represents the propagated traceID in the format of 32 hex-encoded digits. +// - spanID - represents the propagated spanID (parentID) in the format of 16 hex-encoded digits. +// - flags - represents the propagated flags in the format of 2 hex-encoded digits, and supports 8 unique flags. +// Example value of HTTP `traceparent` header: `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`, +// Currently, Go tracer doesn't support 128-bit traceIDs, so the full traceID (32 hex-encoded digits) must be +// stored into a field that is accessible from the span’s context. TraceId will be parsed from the least significant 16 +// hex-encoded digits into a 64-bit number. +func parseTraceparent(ctx *spanContext, header string) error { + nonWordCutset := "_-\t \n" + header = strings.ToLower(strings.Trim(header, "\t -")) + headerLen := len(header) + if headerLen == 0 { + return ErrSpanContextNotFound + } + if headerLen < 55 { + return ErrSpanContextCorrupted + } + parts := strings.SplitN(header, "-", 5) // 5 because we expect 4 required + 1 optional substrings + if len(parts) < 4 { + return ErrSpanContextCorrupted + } + version := strings.Trim(parts[0], nonWordCutset) + if len(version) != 2 { + return ErrSpanContextCorrupted + } + v, err := strconv.ParseUint(version, 16, 64) + if err != nil || v == 255 { + // version 255 (0xff) is invalid + return ErrSpanContextCorrupted + } + if v == 0 && headerLen != 55 { + // The header length in v0 has to be 55. + // It's allowed to be longer in other versions. + return ErrSpanContextCorrupted + } + // parsing traceID + fullTraceID := strings.Trim(parts[1], nonWordCutset) + if len(fullTraceID) != 32 { + return ErrSpanContextCorrupted + } + // checking that the entire TraceID is a valid hex string + if !isValidID(fullTraceID) { + return ErrSpanContextCorrupted + } + if ctx.trace != nil { + // Ensure that the 128-bit trace id tag doesn't propagate + ctx.trace.unsetPropagatingTag(keyTraceID128) + } + if err := extractTraceID128(ctx, fullTraceID); err != nil { + return err + } + // parsing spanID + spanID := strings.Trim(parts[2], nonWordCutset) + if len(spanID) != 16 { + return ErrSpanContextCorrupted + } + if !isValidID(spanID) { + return ErrSpanContextCorrupted + } + if ctx.spanID, err = strconv.ParseUint(spanID, 16, 64); err != nil { + return ErrSpanContextCorrupted + } + if ctx.spanID == 0 { + return ErrSpanContextNotFound + } + // parsing flags + flags := parts[3] + f, err := strconv.ParseInt(flags, 16, 8) + if err != nil { + return ErrSpanContextCorrupted + } + ctx.setSamplingPriority(int(f)&0x1, samplernames.Unknown) + return nil +} + +// parseTracestate attempts to parse tracestateHeader which is a list +// with up to 32 comma-separated (,) list-members. +// An example value would be: `vendorname1=opaqueValue1,vendorname2=opaqueValue2,dd=s:1;o:synthetics`, +// Where `dd` list contains values that would be in x-datadog-tags as well as those needed for propagation information. +// The keys to the “dd“ values have been shortened as follows to save space: +// `sampling_priority` = `s` +// `origin` = `o` +// `_dd.p.` prefix = `t.` +func parseTracestate(ctx *spanContext, header string) { + if header == "" { + // The W3C spec says tracestate can be empty but should avoid sending it. + // https://www.w3.org/TR/trace-context-1/#tracestate-header-field-values + return + } + // if multiple headers are present, they must be combined and stored + setPropagatingTag(ctx, tracestateHeader, header) + combined := strings.Split(strings.Trim(header, "\t "), ",") + for _, group := range combined { + if !strings.HasPrefix(group, "dd=") { + continue + } + ddMembers := strings.Split(group[len("dd="):], ";") + dropDM := false + for _, member := range ddMembers { + keyVal := strings.SplitN(member, ":", 2) + if len(keyVal) != 2 { + continue + } + key, val := keyVal[0], keyVal[1] + if key == "o" { + ctx.origin = strings.ReplaceAll(val, "~", "=") + } else if key == "s" { + stateP, err := strconv.Atoi(val) + if err != nil { + // If the tracestate priority is absent, + // we rely on the traceparent sampled flag + // set in the parseTraceparent function. + continue + } + // The sampling priority and decision maker values are set based on + // the specification in the internal W3C context propagation RFC. + // See the document for more details. + parentP, _ := ctx.SamplingPriority() + if (parentP == 1 && stateP > 0) || (parentP == 0 && stateP <= 0) { + // As extracted from tracestate + ctx.setSamplingPriority(stateP, samplernames.Unknown) + } + if parentP == 1 && stateP <= 0 { + // Auto keep (1) and set the decision maker to default + ctx.setSamplingPriority(1, samplernames.Default) + } + if parentP == 0 && stateP > 0 { + // Auto drop (0) and drop the decision maker + ctx.setSamplingPriority(0, samplernames.Unknown) + dropDM = true + } + } else if strings.HasPrefix(key, "t.dm") { + if ctx.trace.hasPropagatingTag(keyDecisionMaker) || dropDM { + continue + } + setPropagatingTag(ctx, keyDecisionMaker, val) + } else if strings.HasPrefix(key, "t.") { + keySuffix := key[len("t."):] + val = strings.ReplaceAll(val, "~", "=") + setPropagatingTag(ctx, "_dd.p."+keySuffix, val) + } + } + } +} + +// extractTraceID128 extracts the trace id from v and populates the traceID +// field, and the traceID128 field (if applicable) of the provided ctx, +// returning an error if v is invalid. +func extractTraceID128(ctx *spanContext, v string) error { + if len(v) > 32 { + v = v[len(v)-32:] + } + v = strings.TrimLeft(v, "0") + var err error + if len(v) <= 16 { // 64-bit trace id + var tid uint64 + tid, err = strconv.ParseUint(v, 16, 64) + ctx.traceID.SetLower(tid) + } else { // 128-bit trace id + idUpper := v[:len(v)-16] + ctx.traceID.SetUpperFromHex(idUpper) + var l uint64 + l, err = strconv.ParseUint(v[len(idUpper):], 16, 64) + ctx.traceID.SetLower(l) + } + if err != nil { + return ErrSpanContextCorrupted + } + return nil +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time.go index 86ac9d125..3afe8fb18 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time.go @@ -10,7 +10,8 @@ package tracer import "time" +// nowTime returns the current time, as computed by Time.Now(). +var nowTime = func() time.Time { return time.Now() } + // now returns the current UNIX time in nanoseconds, as computed by Time.UnixNano(). -func now() int64 { - return time.Now().UnixNano() -} +var now = func() int64 { return time.Now().UnixNano() } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time_windows.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time_windows.go index 0eaa37e85..f1ecd4f90 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time_windows.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/time_windows.go @@ -26,15 +26,23 @@ func lowPrecisionNow() int64 { return time.Now().UnixNano() } -var now func() int64 +// We use this method of initializing now over an init function due to dependency issues. The init +// function may run after other declarations, such as that in payload_test:19, which results in a +// nil dereference panic. +var now func() int64 = func() func() int64 { + if err := windows.LoadGetSystemTimePreciseAsFileTime(); err != nil { + log.Warn("Unable to load high precison timer, defaulting to time.Now()") + return lowPrecisionNow + } else { + return highPrecisionNow + } +}() -// If GetSystemTimePreciseAsFileTime is not available we default to the less -// precise implementation based on time.Now() -func init() { +var nowTime func() time.Time = func() func() time.Time { if err := windows.LoadGetSystemTimePreciseAsFileTime(); err != nil { log.Warn("Unable to load high precison timer, defaulting to time.Now()") - now = lowPrecisionNow + return func() time.Time { return time.Unix(0, lowPrecisionNow()) } } else { - now = highPrecisionNow + return func() time.Time { return time.Unix(0, highPrecisionNow()) } } -} +}() diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go index 6b4437d7b..c8cf41d2d 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go @@ -7,6 +7,7 @@ package tracer import ( gocontext "context" + "encoding/binary" "os" "runtime/pprof" rt "runtime/trace" @@ -18,8 +19,16 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" + globalinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec" + appsecConfig "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config" + "gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams" + "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" + "gopkg.in/DataDog/dd-trace-go.v1/internal/hostname" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig" + "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof" "github.com/DataDog/datadog-agent/pkg/obfuscate" @@ -46,8 +55,8 @@ type tracer struct { // destination, such as the Trace Agent or Datadog Forwarder. traceWriter traceWriter - // out receives finishedTrace with spans to be added to the payload. - out chan *finishedTrace + // out receives chunk with spans to be added to the payload. + out chan *chunk // flush receives a channel onto which it will confirm after a flush has been // triggered and completed. @@ -66,17 +75,17 @@ type tracer struct { prioritySampling *prioritySampler // pid of the process - pid string + pid int // These integers track metrics about spans and traces as they are started, // finished, and dropped - spansStarted, spansFinished, tracesDropped int64 + spansStarted, spansFinished, tracesDropped uint32 // Records the number of dropped P0 traces and spans. - droppedP0Traces, droppedP0Spans uint64 + droppedP0Traces, droppedP0Spans uint32 // partialTrace the number of partially dropped traces. - partialTraces uint64 + partialTraces uint32 // rulesSampling holds an instance of the rules sampler used to apply either trace sampling, // or single span sampling rules on spans. These are user-defined @@ -87,6 +96,18 @@ type tracer struct { // obfuscator holds the obfuscator used to obfuscate resources in aggregated stats. // obfuscator may be nil if disabled. obfuscator *obfuscate.Obfuscator + + // statsd is used for tracking metrics associated with the runtime and the tracer. + statsd globalinternal.StatsdClient + + // dataStreams processes data streams monitoring information + dataStreams *datastreams.Processor + + // abandonedSpansDebugger specifies where and how potentially abandoned spans are stored + // when abandoned spans debugging is enabled. + abandonedSpansDebugger *abandonedSpansDebugger + + statsCarrier *globalinternal.StatsCarrier } const ( @@ -118,14 +139,42 @@ func Start(opts ...StartOption) { if internal.Testing { return // mock tracer active } + defer telemetry.Time(telemetry.NamespaceGeneral, "init_time", nil, true)() t := newTracer(opts...) - if !t.config.enabled { + if !t.config.enabled.current { + // TODO: instrumentation telemetry client won't get started + // if tracing is disabled, but we still want to capture this + // telemetry information. Will be fixed when the tracer and profiler + // share control of the global telemetry client. return } internal.SetGlobalTracer(t) if t.config.logStartup { logStartup(t) } + if t.dataStreams != nil { + t.dataStreams.Start() + } + // Start AppSec with remote configuration + cfg := remoteconfig.DefaultClientConfig() + cfg.AgentURL = t.config.agentURL.String() + cfg.AppVersion = t.config.version + cfg.Env = t.config.env + cfg.HTTP = t.config.httpClient + cfg.ServiceName = t.config.serviceName + if err := t.startRemoteConfig(cfg); err != nil { + log.Warn("Remote config startup error: %s", err) + } + + // start instrumentation telemetry unless it is disabled through the + // DD_INSTRUMENTATION_TELEMETRY_ENABLED env var + startTelemetry(t.config) + + // appsec.Start() may use the telemetry client to report activation, so it is + // important this happens _AFTER_ startTelemetry() has been called, so the + // client is appropriately configured. + appsec.Start(appsecConfig.WithRCConfig(cfg)) + _ = t.hostname() // Prime the hostname cache } // Stop stops the started tracer. Subsequent calls are valid but become no-op. @@ -168,16 +217,13 @@ func SetUser(s Span, id string, opts ...UserMonitoringOption) { if s == nil { return } - sp, ok := s.(*span) - if !ok || sp.context == nil { + sp, ok := s.(interface { + SetUser(string, ...UserMonitoringOption) + }) + if !ok { return } - sp = sp.context.trace.root - var cfg UserMonitoringConfig - for _, fn := range opts { - fn(&cfg) - } - sp.setUser(id, cfg) + sp.SetUser(id, opts...) } // payloadQueueSize is the buffer size of the trace channel. @@ -186,11 +232,15 @@ const payloadQueueSize = 1000 func newUnstartedTracer(opts ...StartOption) *tracer { c := newConfig(opts...) sampler := newPrioritySampler() + statsd, err := newStatsdClient(c) + if err != nil { + log.Warn("Runtime and health metrics disabled: %v", err) + } var writer traceWriter if c.logToStdout { - writer = newLogTraceWriter(c) + writer = newLogTraceWriter(c, statsd) } else { - writer = newAgentTraceWriter(c, sampler) + writer = newAgentTraceWriter(c, sampler, statsd) } traces, spans, err := samplingRulesFromEnv() if err != nil { @@ -202,15 +252,29 @@ func newUnstartedTracer(opts ...StartOption) *tracer { if spans != nil { c.spanRules = spans } + globalRate := globalSampleRate() + rulesSampler := newRulesSampler(c.traceRules, c.spanRules, globalRate) + c.traceSampleRate = newDynamicConfig("trace_sample_rate", globalRate, rulesSampler.traces.setGlobalSampleRate, equal[float64]) + var dataStreamsProcessor *datastreams.Processor + if c.dataStreamsMonitoringEnabled { + dataStreamsProcessor = datastreams.NewProcessor(statsd, c.env, c.serviceName, c.version, c.agentURL, c.httpClient, func() bool { + f := loadAgentFeatures(c.logToStdout, c.agentURL, c.httpClient) + return f.DataStreams + }) + } + var statsCarrier *globalinternal.StatsCarrier + if c.contribStats { + statsCarrier = globalinternal.NewStatsCarrier(statsd) + } t := &tracer{ config: c, traceWriter: writer, - out: make(chan *finishedTrace, payloadQueueSize), + out: make(chan *chunk, payloadQueueSize), stop: make(chan struct{}), flush: make(chan chan<- struct{}), - rulesSampling: newRulesSampler(c.traceRules, c.spanRules), + rulesSampling: rulesSampler, prioritySampling: sampler, - pid: strconv.Itoa(os.Getpid()), + pid: os.Getpid(), stats: newConcentrator(c, defaultStatsBucketSize), obfuscator: obfuscate.NewObfuscator(obfuscate.Config{ SQL: obfuscate.SQLConfig{ @@ -221,14 +285,22 @@ func newUnstartedTracer(opts ...StartOption) *tracer { Cache: c.agent.HasFlag("sql_cache"), }, }), + statsd: statsd, + dataStreams: dataStreamsProcessor, + statsCarrier: statsCarrier, } return t } +// newTracer creates a new no-op tracer for testing. +// NOTE: This function does NOT set the global tracer, which is required for +// most finish span/flushing operations to work as expected. If you are calling +// span.Finish and/or expecting flushing to work, you must call +// internal.SetGlobalTracer(...) with the tracer provided by this function. func newTracer(opts ...StartOption) *tracer { t := newUnstartedTracer(opts...) c := t.config - t.config.statsd.Incr("datadog.tracer.started", nil, 1) + t.statsd.Incr("datadog.tracer.started", nil, 1) if c.runtimeMetrics { log.Debug("Runtime metrics enabled.") t.wg.Add(1) @@ -237,6 +309,11 @@ func newTracer(opts ...StartOption) *tracer { t.reportRuntimeMetrics(defaultMetricsReportInterval) }() } + if c.debugAbandonedSpans { + log.Info("Abandoned spans logs enabled.") + t.abandonedSpansDebugger = newAbandonedSpansDebugger() + t.abandonedSpansDebugger.Start(t.config.spanTimeout) + } t.wg.Add(1) go func() { defer t.wg.Done() @@ -254,7 +331,10 @@ func newTracer(opts ...StartOption) *tracer { t.reportHealthMetrics(statsInterval) }() t.stats.Start() - appsec.Start() + if sc := t.statsCarrier; sc != nil { + sc.Start() + globalconfig.SetStatsCarrier(sc) + } return t } @@ -264,13 +344,16 @@ func newTracer(opts ...StartOption) *tracer { // use case described below. // // Flush is of use in Lambda environments, where starting and stopping -// the tracer on each invokation may create too much latency. In this +// the tracer on each invocation may create too much latency. In this // scenario, a tracer may be started and stopped by the parent process -// whereas the invokation can make use of Flush to ensure any created spans +// whereas the invocation can make use of Flush to ensure any created spans // reach the agent. func Flush() { if t, ok := internal.GetGlobalTracer().(*tracer); ok { t.flushSync() + if t.dataStreams != nil { + t.dataStreams.Flush() + } } } @@ -287,19 +370,21 @@ func (t *tracer) worker(tick <-chan time.Time) { for { select { case trace := <-t.out: - t.sampleFinishedTrace(trace) + t.sampleChunk(trace) if len(trace.spans) != 0 { t.traceWriter.add(trace.spans) } case <-tick: - t.config.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:scheduled"}, 1) + t.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:scheduled"}, 1) t.traceWriter.flush() case done := <-t.flush: - t.config.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:invoked"}, 1) + t.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:invoked"}, 1) t.traceWriter.flush() + t.statsd.Flush() + t.stats.flushAndSend(time.Now(), withCurrentBucket) // TODO(x): In reality, the traceWriter.flush() call is not synchronous - // when using the agent traceWriter. However, this functionnality is used + // when using the agent traceWriter. However, this functionality is used // in Lambda so for that purpose this mechanism should suffice. done <- struct{}{} @@ -310,7 +395,7 @@ func (t *tracer) worker(tick <-chan time.Time) { for { select { case trace := <-t.out: - t.sampleFinishedTrace(trace) + t.sampleChunk(trace) if len(trace.spans) != 0 { t.traceWriter.add(trace.spans) } @@ -323,39 +408,45 @@ func (t *tracer) worker(tick <-chan time.Time) { } } -// finishedTrace holds information about a trace that has finished, including its spans. -type finishedTrace struct { +// chunk holds information about a trace chunk to be flushed, including its spans. +// The chunk may be a fully finished local trace chunk, or only a portion of the local trace chunk in the case of +// partial flushing. +type chunk struct { spans []*span - decision samplingDecision + willSend bool // willSend indicates whether the trace will be sent to the agent. } -// sampleFinishedTrace applies single-span sampling to the provided trace, which is considered to be finished. -func (t *tracer) sampleFinishedTrace(info *finishedTrace) { - if info.decision == decisionKeep { - return - } - if !t.rulesSampling.HasSpanRules() { - info.spans = nil - return +// sampleChunk applies single-span sampling to the provided trace. +func (t *tracer) sampleChunk(c *chunk) { + if len(c.spans) > 0 { + if p, ok := c.spans[0].context.SamplingPriority(); ok && p > 0 { + // The trace is kept, no need to run single span sampling rules. + return + } } - // if trace sampling decision is drop, we still want to send single spans - // unless there are no single span sampling rules defined var kept []*span - for _, span := range info.spans { - if t.rulesSampling.SampleSpan(span) { - kept = append(kept, span) + if t.rulesSampling.HasSpanRules() { + // Apply sampling rules to individual spans in the trace. + for _, span := range c.spans { + if t.rulesSampling.SampleSpan(span) { + kept = append(kept, span) + } + } + if len(kept) > 0 && len(kept) < len(c.spans) { + // Some spans in the trace were kept, so a partial trace will be sent. + atomic.AddUint32(&t.partialTraces, 1) } } - atomic.AddUint64(&t.droppedP0Spans, uint64(len(info.spans)-len(kept))) - info.spans = kept if len(kept) == 0 { - atomic.AddUint64(&t.droppedP0Traces, 1) - return // no spans matched the rules and were sampled + atomic.AddUint32(&t.droppedP0Traces, 1) + } + atomic.AddUint32(&t.droppedP0Spans, uint32(len(c.spans)-len(kept))) + if !c.willSend { + c.spans = kept } - atomic.AddUint64(&t.partialTraces, 1) } -func (t *tracer) pushTrace(trace *finishedTrace) { +func (t *tracer) pushChunk(trace *chunk) { select { case <-t.stop: return @@ -370,6 +461,9 @@ func (t *tracer) pushTrace(trace *finishedTrace) { // StartSpan creates, starts, and returns a new Span with the given `operationName`. func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOption) ddtrace.Span { + if !t.config.enabled.current { + return internal.NoopSpan{} + } var opts ddtrace.StartSpanConfig for _, fn := range options { fn(&opts) @@ -393,6 +487,11 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt // applyPPROFLabels() below. pprofContext = ctx.span.pprofCtxActive } + } else if p, ok := opts.Parent.(ddtrace.SpanContextW3C); ok { + context = &spanContext{ + traceID: p.TraceID128Bytes(), + spanID: p.SpanID(), + } } } if pprofContext == nil { @@ -407,7 +506,7 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt } id := opts.SpanID if id == 0 { - id = random.Uint64() + id = generateSpanID(startTime) } // span defaults span := &span{ @@ -417,17 +516,20 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt SpanID: id, TraceID: id, Start: startTime, - taskEnd: startExecutionTracerTask(operationName), noDebugStack: t.config.noDebugStack, } + for _, link := range opts.SpanLinks { + span.SpanLinks = append(span.SpanLinks, link) + } + if t.config.hostname != "" { span.setMeta(keyHostname, t.config.hostname) } if context != nil { // this is a child span - span.TraceID = context.traceID + span.TraceID = context.traceID.Lower() span.ParentID = context.spanID - if p, ok := context.samplingPriority(); ok { + if p, ok := context.SamplingPriority(); ok { span.setMetric(keySamplingPriority, float64(p)) } if context.span != nil { @@ -444,21 +546,15 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt } } span.context = newSpanContext(span, context) - if context == nil || context.span == nil { - // this is either a root span or it has a remote parent, we should add the PID. - span.setMeta(ext.Pid, t.pid) - if _, ok := opts.Tags[ext.ServiceName]; !ok && t.config.runtimeMetrics { - // this is a root span in the global service; runtime metrics should - // be linked to it: - span.setMeta("language", "go") - } - } + span.setMetric(ext.Pid, float64(t.pid)) + span.setMeta("language", "go") + // add tags from options for k, v := range opts.Tags { span.SetTag(k, v) } // add global tags - for k, v := range t.config.globalTags { + for k, v := range t.config.globalTags.get() { span.SetTag(k, v) } if t.config.serviceMappings != nil { @@ -466,7 +562,12 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt span.Service = newSvc } } - if context == nil || context.span == nil || context.span.Service != span.Service { + isRootSpan := context == nil || context.span == nil + if isRootSpan { + traceprof.SetProfilerRootTags(span) + span.setMetric(keySpanAttributeSchemaVersion, float64(t.config.spanAttributeSchemaVersion)) + } + if isRootSpan || context.span.Service != span.Service { span.setMetric(keyTopLevel, 1) // all top level spans are measured. So the measured tag is redundant. delete(span.Metrics, keyMeasured) @@ -479,10 +580,11 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt if t.config.env != "" { span.setMeta(ext.Environment, t.config.env) } - if _, ok := span.context.samplingPriority(); !ok { + if _, ok := span.context.SamplingPriority(); !ok { // if not already sampled or a brand new trace, sample it t.sample(span) } + pprofContext, span.taskEnd = startExecutionTracerTask(pprofContext, span) if t.config.profilerHotspots || t.config.profilerEndpoints { t.applyPPROFLabels(pprofContext, span) } @@ -496,25 +598,48 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt log.Debug("Started Span: %v, Operation: %s, Resource: %s, Tags: %v, %v", span, span.Name, span.Resource, span.Meta, span.Metrics) } + if t.config.debugAbandonedSpans { + select { + case t.abandonedSpansDebugger.In <- newAbandonedSpanCandidate(span, false): + // ok + default: + log.Error("Abandoned spans channel full, disregarding span.") + } + } return span } +// generateSpanID returns a random uint64 that has been XORd with the startTime. +// This is done to get around the 32-bit random seed limitation that may create collisions if there is a large number +// of go services all generating spans. +func generateSpanID(startTime int64) uint64 { + return random.Uint64() ^ uint64(startTime) +} + // applyPPROFLabels applies pprof labels for the profiler's code hotspots and // endpoint filtering feature to span. When span finishes, any pprof labels -// found in ctx are restored. +// found in ctx are restored. Additionally, this func informs the profiler how +// many times each endpoint is called. func (t *tracer) applyPPROFLabels(ctx gocontext.Context, span *span) { var labels []string if t.config.profilerHotspots { + // allocate the max-length slice to avoid growing it later + labels = make([]string, 0, 6) labels = append(labels, traceprof.SpanID, strconv.FormatUint(span.SpanID, 10)) } // nil checks might not be needed, but better be safe than sorry - if span.context.trace != nil && span.context.trace.root != nil { - localRootSpan := span.context.trace.root + if localRootSpan := span.root(); localRootSpan != nil { if t.config.profilerHotspots { labels = append(labels, traceprof.LocalRootSpanID, strconv.FormatUint(localRootSpan.SpanID, 10)) } if t.config.profilerEndpoints && spanResourcePIISafe(localRootSpan) { labels = append(labels, traceprof.TraceEndpoint, localRootSpan.Resource) + if span == localRootSpan { + // Inform the profiler of endpoint hits. This is used for the unit of + // work feature. We can't use APM stats for this since the stats don't + // have enough cardinality (e.g. runtime-id tags are missing). + traceprof.GlobalEndpointCounter().Inc(localRootSpan.Resource) + } } } if len(labels) > 0 { @@ -535,22 +660,63 @@ func spanResourcePIISafe(s *span) bool { func (t *tracer) Stop() { t.stopOnce.Do(func() { close(t.stop) - t.config.statsd.Incr("datadog.tracer.stopped", nil, 1) + t.statsd.Incr("datadog.tracer.stopped", nil, 1) }) + t.abandonedSpansDebugger.Stop() t.stats.Stop() t.wg.Wait() t.traceWriter.stop() - t.config.statsd.Close() + t.statsd.Close() + if t.dataStreams != nil { + t.dataStreams.Stop() + } + if t.statsCarrier != nil { + t.statsCarrier.Stop() + } appsec.Stop() + remoteconfig.Stop() } // Inject uses the configured or default TextMap Propagator. func (t *tracer) Inject(ctx ddtrace.SpanContext, carrier interface{}) error { + if !t.config.enabled.current { + return nil + } + t.updateSampling(ctx) return t.config.propagator.Inject(ctx, carrier) } +// updateSampling runs trace sampling rules on the context, since properties like resource / tags +// could change and impact the result of sampling. This must be done once before context is propagated. +func (t *tracer) updateSampling(ctx ddtrace.SpanContext) { + sctx, ok := ctx.(*spanContext) + if sctx == nil || !ok { + return + } + // without this check some mock spans tests fail + if t.rulesSampling == nil || sctx.trace == nil || sctx.trace.root == nil { + return + } + // want to avoid locking the entire trace from a span for long. + // if SampleTrace successfully samples the trace, + // it will lock the span and the trace mutexes in span.setSamplingPriorityLocked + // and trace.setSamplingPriority respectively, so we can't rely on those mutexes. + if sctx.trace.isLocked() { + // trace sampling decision already taken and locked, no re-sampling shall occur + return + } + + // if sampling was successful, need to lock the trace to prevent further re-sampling + if t.rulesSampling.SampleTrace(sctx.trace.root) { + sctx.trace.setLocked(true) + } +} + // Extract uses the configured or default TextMap Propagator. func (t *tracer) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + if !t.config.enabled.current { + return internal.NoopSpanContext{}, nil + } return t.config.propagator.Extract(carrier) } @@ -559,28 +725,66 @@ const sampleRateMetricKey = "_sample_rate" // Sample samples a span with the internal sampler. func (t *tracer) sample(span *span) { - if _, ok := span.context.samplingPriority(); ok { + if _, ok := span.context.SamplingPriority(); ok { // sampling decision was already made return } sampler := t.config.sampler if !sampler.Sample(span) { span.context.trace.drop() + span.context.trace.setSamplingPriority(ext.PriorityAutoReject, samplernames.RuleRate) return } if rs, ok := sampler.(RateSampler); ok && rs.Rate() < 1 { span.setMetric(sampleRateMetricKey, rs.Rate()) } - if t.rulesSampling.SampleTrace(span) { + if t.rulesSampling.SampleTraceGlobalRate(span) { return } t.prioritySampling.apply(span) } -func startExecutionTracerTask(name string) func() { +func startExecutionTracerTask(ctx gocontext.Context, span *span) (gocontext.Context, func()) { if !rt.IsEnabled() { - return func() {} + return ctx, func() {} + } + span.goExecTraced = true + // Task name is the resource (operationName) of the span, e.g. + // "POST /foo/bar" (http) or "/foo/pkg.Method" (grpc). + taskName := span.Resource + // If the resource could contain PII (e.g. SQL query that's not using bind + // arguments), play it safe and just use the span type as the taskName, + // e.g. "sql". + if !spanResourcePIISafe(span) { + taskName = span.Type + } + end := noopTaskEnd + if !globalinternal.IsExecutionTraced(ctx) { + var task *rt.Task + ctx, task = rt.NewTask(ctx, taskName) + end = task.End + } else { + // We only want to skip task creation for this particular span, + // not necessarily for child spans which can come from different + // integrations. So update this context to be "not" execution + // traced so that derived contexts used by child spans don't get + // skipped. + ctx = globalinternal.WithExecutionNotTraced(ctx) + } + var b [8]byte + binary.LittleEndian.PutUint64(b[:], span.SpanID) + // TODO: can we make string(b[:]) not allocate? e.g. with unsafe + // shenanigans? rt.Log won't retain the message string, though perhaps + // we can't assume that will always be the case. + rt.Log(ctx, "datadog.uint64_span_id", string(b[:])) + return ctx, end +} + +func noopTaskEnd() {} + +func (t *tracer) hostname() string { + if !t.config.enableHostnameDetection { + return "" } - _, task := rt.NewTask(gocontext.TODO(), name) - return task.End + return hostname.Get() } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go index 85da05dd6..d6cac1c1f 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go @@ -11,6 +11,7 @@ import ( "io" "net" "net/http" + "net/url" "os" "runtime" "strconv" @@ -56,6 +57,7 @@ const ( defaultHostname = "localhost" defaultPort = "8126" defaultAddress = defaultHostname + ":" + defaultPort + defaultURL = "http://" + defaultAddress defaultHTTPTimeout = 2 * time.Second // defines the current timeout before giving up with the send process traceCountHeader = "X-Datadog-Trace-Count" // header containing the number of traces in the payload ) @@ -79,16 +81,13 @@ type httpTransport struct { } // newTransport returns a new Transport implementation that sends traces to a -// trace agent running on the given hostname and port, using a given -// *http.Client. If the zero values for hostname and port are provided, -// the default values will be used ("localhost" for hostname, and "8126" for -// port). If client is nil, a default is used. +// trace agent at the given url, using a given *http.Client. // // In general, using this method is only necessary if you have a trace agent // running on a non-default port, if it's located on another machine, or when // otherwise needing to customize the transport layer, for instance when using // a unix domain socket. -func newHTTPTransport(addr string, client *http.Client) *httpTransport { +func newHTTPTransport(url string, client *http.Client) *httpTransport { // initialize the default EncoderPool with Encoder headers defaultHeaders := map[string]string{ "Datadog-Meta-Lang": "go", @@ -100,9 +99,12 @@ func newHTTPTransport(addr string, client *http.Client) *httpTransport { if cid := internal.ContainerID(); cid != "" { defaultHeaders["Datadog-Container-ID"] = cid } + if eid := internal.EntityID(); eid != "" { + defaultHeaders["Datadog-Entity-ID"] = eid + } return &httpTransport{ - traceURL: fmt.Sprintf("http://%s/v0.4/traces", addr), - statsURL: fmt.Sprintf("http://%s/v0.6/stats", addr), + traceURL: fmt.Sprintf("%s/v0.4/traces", url), + statsURL: fmt.Sprintf("%s/v0.6/stats", url), client: client, headers: defaultHeaders, } @@ -151,10 +153,10 @@ func (t *httpTransport) send(p *payload) (body io.ReadCloser, err error) { if t.config.canComputeStats() { req.Header.Set("Datadog-Client-Computed-Stats", "yes") } - droppedTraces := int(atomic.SwapUint64(&t.droppedP0Traces, 0)) - partialTraces := int(atomic.SwapUint64(&t.partialTraces, 0)) - droppedSpans := int(atomic.SwapUint64(&t.droppedP0Spans, 0)) - if stats := t.config.statsd; stats != nil { + droppedTraces := int(atomic.SwapUint32(&t.droppedP0Traces, 0)) + partialTraces := int(atomic.SwapUint32(&t.partialTraces, 0)) + droppedSpans := int(atomic.SwapUint32(&t.droppedP0Spans, 0)) + if stats := t.statsd; stats != nil { stats.Count("datadog.tracer.dropped_p0_traces", int64(droppedTraces), []string{fmt.Sprintf("partial:%s", strconv.FormatBool(partialTraces > 0))}, 1) stats.Count("datadog.tracer.dropped_p0_spans", int64(droppedSpans), nil, 1) @@ -188,7 +190,7 @@ func (t *httpTransport) endpoint() string { // resolveAgentAddr resolves the given agent address and fills in any missing host // and port using the defaults. Some environment variable settings will // take precedence over configuration. -func resolveAgentAddr() string { +func resolveAgentAddr() *url.URL { var host, port string if v := os.Getenv("DD_AGENT_HOST"); v != "" { host = v @@ -196,11 +198,20 @@ func resolveAgentAddr() string { if v := os.Getenv("DD_TRACE_AGENT_PORT"); v != "" { port = v } + if _, err := os.Stat(defaultSocketAPM); host == "" && port == "" && err == nil { + return &url.URL{ + Scheme: "unix", + Path: defaultSocketAPM, + } + } if host == "" { host = defaultHostname } if port == "" { port = defaultPort } - return fmt.Sprintf("%s:%s", host, port) + return &url.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s:%s", host, port), + } } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go index 865318ce3..67ee16149 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/util.go @@ -84,7 +84,7 @@ func isValidPropagatableTag(k, v string) error { return fmt.Errorf("value length must be greater than zero") } for _, ch := range v { - if ch < 32 || ch > 126 || ch == '=' || ch == ',' { + if ch < 32 || ch > 126 || ch == ',' { return fmt.Errorf("value contains an invalid character %d", ch) } } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/writer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/writer.go index 58798a7a7..877c8ada2 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/writer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/writer.go @@ -16,6 +16,7 @@ import ( "sync" "time" + globalinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" ) @@ -46,30 +47,34 @@ type agentTraceWriter struct { // prioritySampling is the prioritySampler into which agentTraceWriter will // read sampling rates sent by the agent prioritySampling *prioritySampler + + // statsd is used to send metrics + statsd globalinternal.StatsdClient } -func newAgentTraceWriter(c *config, s *prioritySampler) *agentTraceWriter { +func newAgentTraceWriter(c *config, s *prioritySampler, statsdClient globalinternal.StatsdClient) *agentTraceWriter { return &agentTraceWriter{ config: c, payload: newPayload(), climit: make(chan struct{}, concurrentConnectionLimit), prioritySampling: s, + statsd: statsdClient, } } func (h *agentTraceWriter) add(trace []*span) { if err := h.payload.push(trace); err != nil { - h.config.statsd.Incr("datadog.tracer.traces_dropped", []string{"reason:encoding_error"}, 1) + h.statsd.Incr("datadog.tracer.traces_dropped", []string{"reason:encoding_error"}, 1) log.Error("Error encoding msgpack: %v", err) } if h.payload.size() > payloadSizeLimit { - h.config.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:size"}, 1) + h.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:size"}, 1) h.flush() } } func (h *agentTraceWriter) stop() { - h.config.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:shutdown"}, 1) + h.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:shutdown"}, 1) h.flush() h.wg.Wait() } @@ -85,23 +90,39 @@ func (h *agentTraceWriter) flush() { h.payload = newPayload() go func(p *payload) { defer func(start time.Time) { + // Once the payload has been used, clear the buffer for garbage + // collection to avoid a memory leak when references to this object + // may still be kept by faulty transport implementations or the + // standard library. See dd-trace-go#976 + p.clear() + <-h.climit + h.statsd.Timing("datadog.tracer.flush_duration", time.Since(start), nil, 1) h.wg.Done() - h.config.statsd.Timing("datadog.tracer.flush_duration", time.Since(start), nil, 1) }(time.Now()) - size, count := p.size(), p.itemCount() - log.Debug("Sending payload: size: %d traces: %d\n", size, count) - rc, err := h.config.transport.send(p) - if err != nil { - h.config.statsd.Count("datadog.tracer.traces_dropped", int64(count), []string{"reason:send_failed"}, 1) - log.Error("lost %d traces: %v", count, err) - } else { - h.config.statsd.Count("datadog.tracer.flush_bytes", int64(size), nil, 1) - h.config.statsd.Count("datadog.tracer.flush_traces", int64(count), nil, 1) - if err := h.prioritySampling.readRatesJSON(rc); err != nil { - h.config.statsd.Incr("datadog.tracer.decode_error", nil, 1) + + var count, size int + var err error + for attempt := 0; attempt <= h.config.sendRetries; attempt++ { + size, count = p.size(), p.itemCount() + log.Debug("Sending payload: size: %d traces: %d\n", size, count) + var rc io.ReadCloser + rc, err = h.config.transport.send(p) + if err == nil { + log.Debug("sent traces after %d attempts", attempt+1) + h.statsd.Count("datadog.tracer.flush_bytes", int64(size), nil, 1) + h.statsd.Count("datadog.tracer.flush_traces", int64(count), nil, 1) + if err := h.prioritySampling.readRatesJSON(rc); err != nil { + h.statsd.Incr("datadog.tracer.decode_error", nil, 1) + } + return } + log.Error("failure sending traces (attempt %d), will retry: %v", attempt+1, err) + p.reset() + time.Sleep(time.Millisecond) } + h.statsd.Count("datadog.tracer.traces_dropped", int64(count), []string{"reason:send_failed"}, 1) + log.Error("lost %d traces: %v", count, err) }(oldp) } @@ -116,12 +137,14 @@ type logTraceWriter struct { buf bytes.Buffer hasTraces bool w io.Writer + statsd globalinternal.StatsdClient } -func newLogTraceWriter(c *config) *logTraceWriter { +func newLogTraceWriter(c *config, statsdClient globalinternal.StatsdClient) *logTraceWriter { w := &logTraceWriter{ config: c, w: logWriter, + statsd: statsdClient, } w.resetBuffer() return w @@ -290,7 +313,7 @@ func (h *logTraceWriter) add(trace []*span) { n, err := h.writeTrace(trace) if err != nil { log.Error("Lost a trace: %s", err.cause) - h.config.statsd.Count("datadog.tracer.traces_dropped", 1, []string{"reason:" + err.dropReason}, 1) + h.statsd.Count("datadog.tracer.traces_dropped", 1, []string{"reason:" + err.dropReason}, 1) return } trace = trace[n:] @@ -303,7 +326,7 @@ func (h *logTraceWriter) add(trace []*span) { } func (h *logTraceWriter) stop() { - h.config.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:shutdown"}, 1) + h.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:shutdown"}, 1) h.flush() } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled_build_tag.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/active_span_key.go similarity index 53% rename from vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled_build_tag.go rename to vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/active_span_key.go index e0e4e8174..090150a58 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/waf_disabled_build_tag.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/active_span_key.go @@ -3,10 +3,9 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -// Build when CGO is enabled but the target OS or architecture are not supported -//go:build !appsec -// +build !appsec +package internal -package waf +type contextKey struct{} -var disabledReason = "the waf is disabled due to missing go build tag appsec" +// ActiveSpanKey is used to set tracer context on a context.Context objects with a unique key +var ActiveSpanKey = contextKey{} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/agent.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/agent.go new file mode 100644 index 000000000..c8f835166 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/agent.go @@ -0,0 +1,36 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022 Datadog, Inc. + +package internal + +import ( + "net/url" + "os" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +// AgentURLFromEnv determines the trace agent URL from environment variable +// DD_TRACE_AGENT_URL. If the determined value is valid and the scheme is +// supported (unix, http or https), it will return an *url.URL. Otherwise, +// it returns nil. +func AgentURLFromEnv() *url.URL { + agentURL := os.Getenv("DD_TRACE_AGENT_URL") + if agentURL == "" { + return nil + } + u, err := url.Parse(agentURL) + if err != nil { + log.Warn("Failed to parse DD_TRACE_AGENT_URL: %v", err) + return nil + } + switch u.Scheme { + case "unix", "http", "https": + return u + default: + log.Warn("Unsupported protocol %q in Agent URL %q. Must be one of: http, https, unix.", u.Scheme, agentURL) + return nil + } +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec.go index bf41e1589..ecc1d59f0 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec.go @@ -3,14 +3,16 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -//go:build appsec -// +build appsec - package appsec import ( + "fmt" "sync" + "github.com/DataDog/appsec-internal-go/limiter" + appsecLog "github.com/DataDog/appsec-internal-go/log" + waf "github.com/DataDog/go-libddwaf/v2" + "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config" "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" ) @@ -20,30 +22,79 @@ import ( func Enabled() bool { mu.RLock() defer mu.RUnlock() - return activeAppSec != nil + return activeAppSec != nil && activeAppSec.started } // Start AppSec when enabled is enabled by both using the appsec build tag and // setting the environment variable DD_APPSEC_ENABLED to true. -func Start() { - enabled, err := isEnabled() +func Start(opts ...config.StartOption) { + telemetry := newAppsecTelemetry() + defer telemetry.emit() + + // AppSec can start either: + // 1. Manually thanks to DD_APPSEC_ENABLED + // 2. Remotely when DD_APPSEC_ENABLED is undefined + // Note: DD_APPSEC_ENABLED=false takes precedence over remote configuration + // and enforces to have AppSec disabled. + enabled, set, err := config.IsEnabled() if err != nil { logUnexpectedStartError(err) return } - if !enabled { + if set { + telemetry.addEnvConfig("DD_APPSEC_ENABLED", enabled) + } + + // Check if AppSec is explicitly disabled + if set && !enabled { log.Debug("appsec: disabled by the configuration: set the environment variable DD_APPSEC_ENABLED to true to enable it") return } - cfg, err := newConfig() + // Check whether libddwaf - required for Threats Detection - is ok or not + if ok, err := waf.Health(); !ok { + // We need to avoid logging an error to APM tracing users who don't necessarily intend to enable appsec + if set { + // DD_APPSEC_ENABLED is explicitly set so we log an error + log.Error("appsec: threats detection cannot be enabled for the following reasons: %vappsec: no security activities will be collected. Please contact support at https://docs.datadoghq.com/help/ for help.", err) + } else { + // DD_APPSEC_ENABLED is not set so we cannot know what the intent is here, we must log a + // debug message instead to avoid showing an error to APM-tracing-only users. + log.Debug("appsec: remote activation of threats detection cannot be enabled for the following reasons: %v", err) + } + return + } + + // From this point we know that AppSec is either enabled or can be enabled through remote config + cfg, err := config.NewConfig() if err != nil { logUnexpectedStartError(err) return } + for _, opt := range opts { + opt(cfg) + } appsec := newAppSec(cfg) - if err := appsec.start(); err != nil { + + // Start the remote configuration client + log.Debug("appsec: starting the remote configuration client") + if err := appsec.startRC(); err != nil { + log.Error("appsec: Remote config: disabled due to an instanciation error: %v", err) + } + + if !set { + // AppSec is not enforced by the env var and can be enabled through remote config + log.Debug("appsec: %s is not set, appsec won't start until activated through remote configuration", config.EnvEnabled) + if err := appsec.enableRemoteActivation(); err != nil { + // ASM is not enabled and can't be enabled through remote configuration. Nothing more can be done. + logUnexpectedStartError(err) + appsec.stopRC() + return + } + log.Debug("appsec: awaiting for possible remote activation") + } else if err := appsec.start(telemetry); err != nil { // AppSec is specifically enabled logUnexpectedStartError(err) + appsec.stopRC() return } setActiveAppSec(appsec) @@ -68,38 +119,91 @@ func setActiveAppSec(a *appsec) { mu.Lock() defer mu.Unlock() if activeAppSec != nil { + activeAppSec.stopRC() activeAppSec.stop() } activeAppSec = a } type appsec struct { - cfg *config - unregisterWAF dyngo.UnregisterFunc - limiter *TokenTicker + cfg *config.Config + limiter *limiter.TokenTicker + wafHandle *wafHandle + started bool } -func newAppSec(cfg *config) *appsec { +func newAppSec(cfg *config.Config) *appsec { return &appsec{ cfg: cfg, } } // Start AppSec by registering its security protections according to the configured the security rules. -func (a *appsec) start() error { - // Register the WAF operation event listener - a.limiter = NewTokenTicker(int64(a.cfg.traceRateLimit), int64(a.cfg.traceRateLimit)) +func (a *appsec) start(telemetry *appsecTelemetry) error { + // Load the waf to catch early errors if any + if ok, err := waf.Load(); err != nil { + // 1. If there is an error and the loading is not ok: log as an unexpected error case and quit appsec + // Note that we assume here that the test for the unsupported target has been done before calling + // this method, so it is now considered an error for this method + if !ok { + return fmt.Errorf("error while loading libddwaf: %w", err) + } + // 2. If there is an error and the loading is ok: log as an informative error where appsec can be used + log.Error("appsec: non-critical error while loading libddwaf: %v", err) + } + + a.limiter = limiter.NewTokenTicker(a.cfg.TraceRateLimit, a.cfg.TraceRateLimit) a.limiter.Start() - unregisterWAF, err := registerWAF(a.cfg.rules, a.cfg.wafTimeout, a.limiter, &a.cfg.obfuscator) - if err != nil { + + // Register the WAF operation event listener + if err := a.swapWAF(a.cfg.RulesManager.Latest); err != nil { return err } - a.unregisterWAF = unregisterWAF + + a.enableRCBlocking() + + a.started = true + log.Info("appsec: up and running") + + // TODO: log the config like the APM tracer does but we first need to define + // an user-friendly string representation of our config and its sources + + telemetry.setEnabled() return nil } // Stop AppSec by unregistering the security protections. func (a *appsec) stop() { - a.unregisterWAF() + if !a.started { + return + } + telemetry := newAppsecTelemetry() + defer telemetry.emit() + + a.started = false + // Disable RC blocking first so that the following is guaranteed not to be concurrent anymore. + a.disableRCBlocking() + + // Disable the currently applied instrumentation + dyngo.SwapRootOperation(nil) + if a.wafHandle != nil { + a.wafHandle.Close() + a.wafHandle = nil + } + // TODO: block until no more requests are using dyngo operations + a.limiter.Stop() } + +func init() { + appsecLog.SetBackend(appsecLog.Backend{ + Debug: log.Debug, + Info: log.Info, + Warn: log.Warn, + Errorf: func(s string, a ...any) error { + err := fmt.Errorf(s, a...) + log.Error(err.Error()) + return err + }, + }) +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec_disabled.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec_disabled.go deleted file mode 100644 index 12039e035..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/appsec_disabled.go +++ /dev/null @@ -1,38 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -//go:build !appsec -// +build !appsec - -package appsec - -import "gopkg.in/DataDog/dd-trace-go.v1/internal/log" - -// Enabled returns true when AppSec is up and running. Meaning that the appsec build tag is enabled, the env var -// DD_APPSEC_ENABLED is set to true, and the tracer is started. -func Enabled() bool { - return false -} - -// Start AppSec when enabled is enabled by both using the appsec build tag and -// setting the environment variable DD_APPSEC_ENABLED to true. -func Start() { - if enabled, err := isEnabled(); err != nil { - // Something went wrong while checking the DD_APPSEC_ENABLED configuration - log.Error("appsec: error while checking if appsec is enabled: %v", err) - } else if enabled { - // The user is willing to enabled appsec but didn't have the build tag - log.Info("appsec: enabled by the configuration but has not been activated during the compilation: please add the go build tag `appsec` to your build options to enable it") - } else { - // The user is not willing to start appsec, a simple debug log is enough - log.Debug("appsec: not been not enabled during the compilation: please add the go build tag `appsec` to your build options to enable it") - } -} - -// Stop AppSec. -func Stop() {} - -// Static rule stubs when disabled. -const staticRecommendedRule = "" diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config.go deleted file mode 100644 index c3eb670fb..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config.go +++ /dev/null @@ -1,169 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -package appsec - -import ( - "fmt" - "os" - "regexp" - "strconv" - "time" - "unicode" - "unicode/utf8" - - "gopkg.in/DataDog/dd-trace-go.v1/internal/log" -) - -const ( - enabledEnvVar = "DD_APPSEC_ENABLED" - rulesEnvVar = "DD_APPSEC_RULES" - wafTimeoutEnvVar = "DD_APPSEC_WAF_TIMEOUT" - traceRateLimitEnvVar = "DD_APPSEC_TRACE_RATE_LIMIT" - obfuscatorKeyEnvVar = "DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP" - obfuscatorValueEnvVar = "DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP" -) - -const ( - defaultWAFTimeout = 4 * time.Millisecond - defaultTraceRate = 100 // up to 100 appsec traces/s - defaultObfuscatorKeyRegex = `(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?)key)|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization` - defaultObfuscatorValueRegex = `(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\s*=[^;]|"\s*:\s*"[^"]+")|bearer\s+[a-z0-9\._\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\w=-]+\.ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}` -) - -// config is the AppSec configuration. -type config struct { - // rules loaded via the env var DD_APPSEC_RULES. When not set, the builtin rules will be used. - rules []byte - // Maximum WAF execution time - wafTimeout time.Duration - // AppSec trace rate limit (traces per second). - traceRateLimit uint - // Obfuscator configuration parameters - obfuscator ObfuscatorConfig -} - -// ObfuscatorConfig wraps the key and value regexp to be passed to the WAF to perform obfuscation. -type ObfuscatorConfig struct { - KeyRegex string - ValueRegex string -} - -// isEnabled returns true when appsec is enabled when the environment variable -// DD_APPSEC_ENABLED is set to true. -func isEnabled() (bool, error) { - enabledStr := os.Getenv(enabledEnvVar) - if enabledStr == "" { - return false, nil - } - enabled, err := strconv.ParseBool(enabledStr) - if err != nil { - return false, fmt.Errorf("could not parse %s value `%s` as a boolean value", enabledEnvVar, enabledStr) - } - return enabled, nil -} - -func newConfig() (*config, error) { - rules, err := readRulesConfig() - if err != nil { - return nil, err - } - return &config{ - rules: rules, - wafTimeout: readWAFTimeoutConfig(), - traceRateLimit: readRateLimitConfig(), - obfuscator: readObfuscatorConfig(), - }, nil -} - -func readWAFTimeoutConfig() (timeout time.Duration) { - timeout = defaultWAFTimeout - value := os.Getenv(wafTimeoutEnvVar) - if value == "" { - return - } - - // Check if the value ends with a letter, which means the user has - // specified their own time duration unit(s) such as 1s200ms. - // Otherwise, default to microseconds. - if lastRune, _ := utf8.DecodeLastRuneInString(value); !unicode.IsLetter(lastRune) { - value += "us" // Add the default microsecond time-duration suffix - } - - parsed, err := time.ParseDuration(value) - if err != nil { - logEnvVarParsingError(wafTimeoutEnvVar, value, err, timeout) - return - } - if parsed <= 0 { - logUnexpectedEnvVarValue(wafTimeoutEnvVar, parsed, "expecting a strictly positive duration", timeout) - return - } - return parsed -} - -func readRateLimitConfig() (rate uint) { - rate = defaultTraceRate - value := os.Getenv(traceRateLimitEnvVar) - if value == "" { - return rate - } - parsed, err := strconv.ParseUint(value, 10, 0) - if err != nil { - logEnvVarParsingError(traceRateLimitEnvVar, value, err, rate) - return - } - if rate == 0 { - logUnexpectedEnvVarValue(traceRateLimitEnvVar, parsed, "expecting a value strictly greater than 0", rate) - return - } - return uint(parsed) -} - -func readObfuscatorConfig() ObfuscatorConfig { - keyRE := readObfuscatorConfigRegexp(obfuscatorKeyEnvVar, defaultObfuscatorKeyRegex) - valueRE := readObfuscatorConfigRegexp(obfuscatorValueEnvVar, defaultObfuscatorValueRegex) - return ObfuscatorConfig{KeyRegex: keyRE, ValueRegex: valueRE} -} - -func readObfuscatorConfigRegexp(name, defaultValue string) string { - val, present := os.LookupEnv(name) - if !present { - log.Debug("appsec: %s not defined, starting with the default obfuscator regular expression", name) - return defaultValue - } - if _, err := regexp.Compile(val); err != nil { - log.Error("appsec: could not compile the configured obfuscator regular expression `%s=%s`. Using the default value instead", name, val) - return defaultValue - } - log.Debug("appsec: starting with the configured obfuscator regular expression %s", name) - return val -} - -func readRulesConfig() (rules []byte, err error) { - rules = []byte(staticRecommendedRule) - filepath := os.Getenv(rulesEnvVar) - if filepath == "" { - log.Info("appsec: starting with the default recommended security rules") - return rules, nil - } - buf, err := os.ReadFile(filepath) - if err != nil { - if os.IsNotExist(err) { - log.Error("appsec: could not find the rules file in path %s: %v.", filepath, err) - } - return nil, err - } - log.Info("appsec: starting with the security rules from file %s", filepath) - return buf, nil -} - -func logEnvVarParsingError(name, value string, err error, defaultValue interface{}) { - log.Error("appsec: could not parse the env var %s=%s as a duration: %v. Using default value %v.", name, value, err, defaultValue) -} - -func logUnexpectedEnvVarValue(name string, value interface{}, reason string, defaultValue interface{}) { - log.Error("appsec: unexpected configuration value of %s=%v: %s. Using default value %v.", name, value, reason, defaultValue) -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/config.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/config.go new file mode 100644 index 000000000..b660d7a02 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/config.go @@ -0,0 +1,81 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package config + +import ( + "fmt" + "os" + "strconv" + "time" + + internal "github.com/DataDog/appsec-internal-go/appsec" + "gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig" +) + +// EnvEnabled is the env var used to enable/disable appsec +const EnvEnabled = "DD_APPSEC_ENABLED" + +// StartOption is used to customize the AppSec configuration when invoked with appsec.Start() +type StartOption func(c *Config) + +// Config is the AppSec configuration. +type Config struct { + // rules loaded via the env var DD_APPSEC_RULES. When not set, the builtin rules will be used + // and live-updated with remote configuration. + RulesManager *RulesManager + // Maximum WAF execution time + WAFTimeout time.Duration + // AppSec trace rate limit (traces per second). + TraceRateLimit int64 + // Obfuscator configuration + Obfuscator internal.ObfuscatorConfig + // APISec configuration + APISec internal.APISecConfig + // RC is the remote configuration client used to receive product configuration updates. Nil if RC is disabled (default) + RC *remoteconfig.ClientConfig +} + +// WithRCConfig sets the AppSec remote config client configuration to the specified cfg +func WithRCConfig(cfg remoteconfig.ClientConfig) StartOption { + return func(c *Config) { + c.RC = &cfg + } +} + +// IsEnabled returns true when appsec is enabled when the environment variable +// DD_APPSEC_ENABLED is set to true. +// It also returns whether the env var is actually set in the env or not. +func IsEnabled() (enabled bool, set bool, err error) { + enabledStr, set := os.LookupEnv(EnvEnabled) + if enabledStr == "" { + return false, set, nil + } else if enabled, err = strconv.ParseBool(enabledStr); err != nil { + return false, set, fmt.Errorf("could not parse %s value `%s` as a boolean value", EnvEnabled, enabledStr) + } + + return enabled, set, nil +} + +// NewConfig returns a fresh appsec configuration read from the env +func NewConfig() (*Config, error) { + rules, err := internal.RulesFromEnv() + if err != nil { + return nil, err + } + + r, err := NewRulesManeger(rules) + if err != nil { + return nil, err + } + + return &Config{ + RulesManager: r, + WAFTimeout: internal.WAFTimeoutFromEnv(), + TraceRateLimit: int64(internal.RateLimitFromEnv()), + Obfuscator: internal.NewObfuscatorConfig(), + APISec: internal.NewAPISecConfig(), + }, nil +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/rules_manager.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/rules_manager.go new file mode 100644 index 000000000..46d22b389 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config/rules_manager.go @@ -0,0 +1,167 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023 Datadog, Inc. + +package config + +import ( + "encoding/json" + "fmt" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + + rules "github.com/DataDog/appsec-internal-go/appsec" + rc "github.com/DataDog/datadog-agent/pkg/remoteconfig/state" +) + +type ( + // RulesManager is used to build a full rules file from a combination of rules fragments + // The `Base` fragment is the default rules (either local or received through ASM_DD), + // and the `Edits` fragments each represent a remote configuration update that affects the rules. + // `BasePath` is either empty if the local Base rules are used, or holds the path of the ASM_DD config. + RulesManager struct { + Latest RulesFragment + Base RulesFragment + BasePath string + Edits map[string]RulesFragment + } + // RulesFragment can represent a full ruleset or a fragment of it. + RulesFragment struct { + Version string `json:"version,omitempty"` + Metadata interface{} `json:"metadata,omitempty"` + Rules []interface{} `json:"rules,omitempty"` + Overrides []interface{} `json:"rules_override,omitempty"` + Exclusions []interface{} `json:"exclusions,omitempty"` + RulesData []RuleDataEntry `json:"rules_data,omitempty"` + Actions []ActionEntry `json:"actions,omitempty"` + CustomRules []interface{} `json:"custom_rules,omitempty"` + Processors []interface{} `json:"processors,omitempty"` + Scanners []interface{} `json:"scanners,omitempty"` + } + + // RuleDataEntry represents an entry in the "rules_data" top level field of a rules file + RuleDataEntry rc.ASMDataRuleData + // RulesData is a slice of RulesDataEntry + RulesData struct { + RulesData []RuleDataEntry `json:"rules_data"` + } + + // ActionEntry represents an entry in the "actions" top level field of a rules file + ActionEntry struct { + ID string `json:"id"` + Type string `json:"type"` + Parameters struct { + StatusCode int `json:"status_code"` + GRPCStatusCode *int `json:"grpc_status_code,omitempty"` + Type string `json:"type,omitempty"` + Location string `json:"location,omitempty"` + } `json:"parameters,omitempty"` + } +) + +// DefaultRulesFragment returns a RulesFragment created using the default static recommended rules +func DefaultRulesFragment() RulesFragment { + var f RulesFragment + if err := json.Unmarshal([]byte(rules.StaticRecommendedRules), &f); err != nil { + log.Debug("appsec: error unmarshalling default rules: %v", err) + } + return f +} + +func (f *RulesFragment) clone() (clone RulesFragment) { + clone.Version = f.Version + clone.Metadata = f.Metadata + clone.Overrides = cloneSlice(f.Overrides) + clone.Exclusions = cloneSlice(f.Exclusions) + clone.RulesData = cloneSlice(f.RulesData) + clone.CustomRules = cloneSlice(f.CustomRules) + clone.Processors = cloneSlice(f.Processors) + clone.Scanners = cloneSlice(f.Scanners) + // TODO (Francois Mazeau): copy more fields once we handle them + return +} + +func cloneSlice[T any](slice []T) []T { + // TODO: use slices.Clone once go1.21 is the min supported go runtime. + clone := make([]T, len(slice), cap(slice)) + copy(clone, slice) + return clone +} + +// NewRulesManeger initializes and returns a new RulesManager using the provided rules. +// If no rules are provided (nil), the default rules are used instead. +// If the provided rules are invalid, an error is returned +func NewRulesManeger(rules []byte) (*RulesManager, error) { + var f RulesFragment + if rules == nil { + f = DefaultRulesFragment() + log.Debug("appsec: RulesManager: using default rules configuration") + } else if err := json.Unmarshal(rules, &f); err != nil { + log.Debug("appsec: cannot create RulesManager from specified rules") + return nil, err + } + return &RulesManager{ + Latest: f, + Base: f, + Edits: map[string]RulesFragment{}, + }, nil +} + +// Clone returns a duplicate of the current rules manager object +func (r *RulesManager) Clone() (clone RulesManager) { + clone.Edits = make(map[string]RulesFragment, len(r.Edits)) + for k, v := range r.Edits { + clone.Edits[k] = v + } + clone.BasePath = r.BasePath + clone.Base = r.Base.clone() + clone.Latest = r.Latest.clone() + return +} + +// AddEdit appends the configuration to the map of edits in the rules manager +func (r *RulesManager) AddEdit(cfgPath string, f RulesFragment) { + r.Edits[cfgPath] = f +} + +// RemoveEdit deletes the configuration associated to `cfgPath` in the edits slice +func (r *RulesManager) RemoveEdit(cfgPath string) { + delete(r.Edits, cfgPath) +} + +// ChangeBase sets a new rules fragment base for the rules manager +func (r *RulesManager) ChangeBase(f RulesFragment, basePath string) { + r.Base = f + r.BasePath = basePath +} + +// Compile compiles the RulesManager fragments together stores the result in r.Latest +func (r *RulesManager) Compile() { + if r.Base.Rules == nil || len(r.Base.Rules) == 0 { + r.Base = DefaultRulesFragment() + } + r.Latest = r.Base + + // Simply concatenate the content of each top level rule field as specified in our RFCs + for _, v := range r.Edits { + r.Latest.Overrides = append(r.Latest.Overrides, v.Overrides...) + r.Latest.Exclusions = append(r.Latest.Exclusions, v.Exclusions...) + r.Latest.Actions = append(r.Latest.Actions, v.Actions...) + r.Latest.RulesData = append(r.Latest.RulesData, v.RulesData...) + r.Latest.CustomRules = append(r.Latest.CustomRules, v.CustomRules...) + r.Latest.Processors = append(r.Latest.Processors, v.Processors...) + r.Latest.Scanners = append(r.Latest.Scanners, v.Scanners...) + } +} + +// Raw returns a compact json version of the rules +func (r *RulesManager) Raw() []byte { + data, _ := json.Marshal(r.Latest) + return data +} + +// String returns the string representation of the Latest compiled json rules. +func (r *RulesManager) String() string { + return fmt.Sprintf("%+v", r.Latest) +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/common.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/common.go deleted file mode 100644 index 4ccada178..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/common.go +++ /dev/null @@ -1,131 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2022 Datadog, Inc. - -// Package instrumentation holds code commonly used between all instrumentation declinations (currently httpsec/grpcsec). -package instrumentation - -import ( - "encoding/json" - "fmt" - "sync" - - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" - "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" -) - -// TagsHolder wraps a map holding tags. The purpose of this struct is to be used by composition in an Operation -// to allow said operation to handle tags addition/retrieval. See httpsec/http.go and grpcsec/grpc.go. -type TagsHolder struct { - tags map[string]interface{} - mu sync.Mutex -} - -// NewTagsHolder returns a new instance of a TagsHolder struct. -func NewTagsHolder() TagsHolder { - return TagsHolder{tags: map[string]interface{}{}} -} - -// AddTag adds the key/value pair to the tags map -func (m *TagsHolder) AddTag(k string, v interface{}) { - m.mu.Lock() - defer m.mu.Unlock() - m.tags[k] = v -} - -// Tags returns the tags map -func (m *TagsHolder) Tags() map[string]interface{} { - return m.tags -} - -// SecurityEventsHolder is a wrapper around a thread safe security events slice. The purpose of this struct is to be -// used by composition in an Operation to allow said operation to handle security events addition/retrieval. -// See httpsec/http.go and grpcsec/grpc.go. -type SecurityEventsHolder struct { - events []json.RawMessage - mu sync.Mutex -} - -// AddSecurityEvents adds the security events to the collected events list. -// Thread safe. -func (s *SecurityEventsHolder) AddSecurityEvents(events ...json.RawMessage) { - s.mu.Lock() - defer s.mu.Unlock() - s.events = append(s.events, events...) -} - -// Events returns the list of stored events. -func (s *SecurityEventsHolder) Events() []json.RawMessage { - return s.events -} - -// SetTags fills the span tags using the key/value pairs found in `tags` -func SetTags(span ddtrace.Span, tags map[string]interface{}) { - for k, v := range tags { - span.SetTag(k, v) - } -} - -// SetEventSpanTags sets the security event span tags into the service entry span. -func SetEventSpanTags(span ddtrace.Span, events []json.RawMessage) error { - // Set the appsec event span tag - val, err := makeEventTagValue(events) - if err != nil { - return err - } - span.SetTag("_dd.appsec.json", string(val)) - // Keep this span due to the security event - // - // This is a workaround to tell the tracer that the trace was kept by AppSec. - // Passing any other value than `appsec.SamplerAppSec` has no effect. - // Customers should use `span.SetTag(ext.ManualKeep, true)` pattern - // to keep the trace, manually. - span.SetTag(ext.ManualKeep, samplernames.AppSec) - span.SetTag("_dd.origin", "appsec") - // Set the appsec.event tag needed by the appsec backend - span.SetTag("appsec.event", true) - return nil -} - -// Create the value of the security event tag. -// TODO(Julio-Guerra): a future libddwaf version should return something -// avoiding us the following events concatenation logic which currently -// involves unserializing the top-level JSON arrays to concatenate them -// together. -// TODO(Julio-Guerra): avoid serializing the json in the request hot path -func makeEventTagValue(events []json.RawMessage) (json.RawMessage, error) { - var v interface{} - if l := len(events); l == 1 { - // eventTag is the structure to use in the `_dd.appsec.json` span tag. - // In this case of 1 event, it already is an array as expected. - type eventTag struct { - Triggers json.RawMessage `json:"triggers"` - } - v = eventTag{Triggers: events[0]} - } else { - // eventTag is the structure to use in the `_dd.appsec.json` span tag. - // With more than one event, we need to concatenate the arrays together - // (ie. convert [][]json.RawMessage into []json.RawMessage). - type eventTag struct { - Triggers []json.RawMessage `json:"triggers"` - } - concatenated := make([]json.RawMessage, 0, l) // at least len(events) - for _, event := range events { - // Unmarshal the top level array - var tmp []json.RawMessage - if err := json.Unmarshal(event, &tmp); err != nil { - return nil, fmt.Errorf("unexpected error while unserializing the appsec event `%s`: %v", string(event), err) - } - concatenated = append(concatenated, tmp...) - } - v = eventTag{Triggers: concatenated} - } - - tag, err := json.Marshal(v) - if err != nil { - return nil, fmt.Errorf("unexpected error while serializing the appsec event span tag: %v", err) - } - return tag, nil -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/grpc.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/grpc.go deleted file mode 100644 index 8eb2c638f..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/grpc.go +++ /dev/null @@ -1,175 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -// Package grpcsec is the gRPC instrumentation API and contract for AppSec -// defining an abstract run-time representation of gRPC handlers. -// gRPC integrations must use this package to enable AppSec features for gRPC, -// which listens to this package's operation events. -package grpcsec - -import ( - "encoding/json" - "reflect" - - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation" -) - -// Abstract gRPC server handler operation definitions. It is based on two -// operations allowing to describe every type of RPC: the HandlerOperation type -// which represents the RPC handler, and the ReceiveOperation type which -// represents the messages the RPC handler receives during its lifetime. -// This means that the ReceiveOperation(s) will happen within the -// HandlerOperation. -// Every type of RPC, unary, client streaming, server streaming, and -// bidirectional streaming RPCs, can be all represented with a HandlerOperation -// having one or several ReceiveOperation. -// The send operation is not required for now and therefore not defined, which -// means that server and bidirectional streaming RPCs currently have the same -// run-time representation as unary and client streaming RPCs. -type ( - // HandlerOperation represents a gRPC server handler operation. - // It must be created with StartHandlerOperation() and finished with its - // Finish() method. - // Security events observed during the operation lifetime should be added - // to the operation using its AddSecurityEvent() method. - HandlerOperation struct { - dyngo.Operation - instrumentation.TagsHolder - instrumentation.SecurityEventsHolder - } - // HandlerOperationArgs is the grpc handler arguments. - HandlerOperationArgs struct { - // Message received by the gRPC handler. - // Corresponds to the address `grpc.server.request.metadata`. - Metadata map[string][]string - } - // HandlerOperationRes is the grpc handler results. Empty as of today. - HandlerOperationRes struct{} - - // ReceiveOperation type representing an gRPC server handler operation. It must - // be created with StartReceiveOperation() and finished with its Finish(). - ReceiveOperation struct { - dyngo.Operation - } - // ReceiveOperationArgs is the gRPC handler receive operation arguments - // Empty as of today. - ReceiveOperationArgs struct{} - // ReceiveOperationRes is the gRPC handler receive operation results which - // contains the message the gRPC handler received. - ReceiveOperationRes struct { - // Message received by the gRPC handler. - // Corresponds to the address `grpc.server.request.message`. - Message interface{} - } -) - -// TODO(Julio-Guerra): create a go-generate tool to generate the types, vars and methods below - -// StartHandlerOperation starts an gRPC server handler operation, along with the -// given arguments and parent operation, and emits a start event up in the -// operation stack. When parent is nil, the operation is linked to the global -// root operation. -func StartHandlerOperation(args HandlerOperationArgs, parent dyngo.Operation) *HandlerOperation { - op := &HandlerOperation{ - Operation: dyngo.NewOperation(parent), - TagsHolder: instrumentation.NewTagsHolder(), - } - dyngo.StartOperation(op, args) - return op -} - -// Finish the gRPC handler operation, along with the given results, and emit a -// finish event up in the operation stack. -func (op *HandlerOperation) Finish(res HandlerOperationRes) []json.RawMessage { - dyngo.FinishOperation(op, res) - return op.Events() -} - -// gRPC handler operation's start and finish event callback function types. -type ( - // OnHandlerOperationStart function type, called when an gRPC handler - // operation starts. - OnHandlerOperationStart func(*HandlerOperation, HandlerOperationArgs) - // OnHandlerOperationFinish function type, called when an gRPC handler - // operation finishes. - OnHandlerOperationFinish func(*HandlerOperation, HandlerOperationRes) -) - -var ( - handlerOperationArgsType = reflect.TypeOf((*HandlerOperationArgs)(nil)).Elem() - handlerOperationResType = reflect.TypeOf((*HandlerOperationRes)(nil)).Elem() -) - -// ListenedType returns the type a OnHandlerOperationStart event listener -// listens to, which is the HandlerOperationArgs type. -func (OnHandlerOperationStart) ListenedType() reflect.Type { return handlerOperationArgsType } - -// Call the underlying event listener function by performing the type-assertion -// on v whose type is the one returned by ListenedType(). -func (f OnHandlerOperationStart) Call(op dyngo.Operation, v interface{}) { - f(op.(*HandlerOperation), v.(HandlerOperationArgs)) -} - -// ListenedType returns the type a OnHandlerOperationFinish event listener -// listens to, which is the HandlerOperationRes type. -func (OnHandlerOperationFinish) ListenedType() reflect.Type { return handlerOperationResType } - -// Call the underlying event listener function by performing the type-assertion -// on v whose type is the one returned by ListenedType(). -func (f OnHandlerOperationFinish) Call(op dyngo.Operation, v interface{}) { - f(op.(*HandlerOperation), v.(HandlerOperationRes)) -} - -// StartReceiveOperation starts a receive operation of a gRPC handler, along -// with the given arguments and parent operation, and emits a start event up in -// the operation stack. When parent is nil, the operation is linked to the -// global root operation. -func StartReceiveOperation(args ReceiveOperationArgs, parent dyngo.Operation) ReceiveOperation { - op := ReceiveOperation{Operation: dyngo.NewOperation(parent)} - dyngo.StartOperation(op, args) - return op -} - -// Finish the gRPC handler operation, along with the given results, and emits a -// finish event up in the operation stack. -func (op ReceiveOperation) Finish(res ReceiveOperationRes) { - dyngo.FinishOperation(op, res) -} - -// gRPC receive operation's start and finish event callback function types. -type ( - // OnReceiveOperationStart function type, called when a gRPC receive - // operation starts. - OnReceiveOperationStart func(ReceiveOperation, ReceiveOperationArgs) - // OnReceiveOperationFinish function type, called when a grpc receive - // operation finishes. - OnReceiveOperationFinish func(ReceiveOperation, ReceiveOperationRes) -) - -var ( - receiveOperationArgsType = reflect.TypeOf((*ReceiveOperationArgs)(nil)).Elem() - receiveOperationResType = reflect.TypeOf((*ReceiveOperationRes)(nil)).Elem() -) - -// ListenedType returns the type a OnHandlerOperationStart event listener -// listens to, which is the HandlerOperationArgs type. -func (OnReceiveOperationStart) ListenedType() reflect.Type { return receiveOperationArgsType } - -// Call the underlying event listener function by performing the type-assertion -// on v whose type is the one returned by ListenedType(). -func (f OnReceiveOperationStart) Call(op dyngo.Operation, v interface{}) { - f(op.(ReceiveOperation), v.(ReceiveOperationArgs)) -} - -// ListenedType returns the type a OnHandlerOperationFinish event listener -// listens to, which is the HandlerOperationRes type. -func (OnReceiveOperationFinish) ListenedType() reflect.Type { return receiveOperationResType } - -// Call the underlying event listener function by performing the type-assertion -// on v whose type is the one returned by ListenedType(). -func (f OnReceiveOperationFinish) Call(op dyngo.Operation, v interface{}) { - f(op.(ReceiveOperation), v.(ReceiveOperationRes)) -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/tags.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/tags.go deleted file mode 100644 index 845ab3a04..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/grpcsec/tags.go +++ /dev/null @@ -1,44 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -package grpcsec - -import ( - "encoding/json" - "net" - - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec" - "gopkg.in/DataDog/dd-trace-go.v1/internal/log" -) - -// SetSecurityEventTags sets the AppSec-specific span tags when a security event -// occurred into the service entry span. -func SetSecurityEventTags(span ddtrace.Span, events []json.RawMessage, addr net.Addr, md map[string][]string) { - if err := setSecurityEventTags(span, events, addr, md); err != nil { - log.Error("appsec: %v", err) - } -} - -func setSecurityEventTags(span ddtrace.Span, events []json.RawMessage, addr net.Addr, md map[string][]string) error { - if err := instrumentation.SetEventSpanTags(span, events); err != nil { - return err - } - var ip string - switch actual := addr.(type) { - case *net.UDPAddr: - ip = actual.IP.String() - case *net.TCPAddr: - ip = actual.IP.String() - } - if ip != "" { - span.SetTag("network.client.ip", ip) - } - for h, v := range httpsec.NormalizeHTTPHeaders(md) { - span.SetTag("grpc.metadata."+h, v) - } - return nil -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/http.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/http.go deleted file mode 100644 index 2630f71c4..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/http.go +++ /dev/null @@ -1,258 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -// Package httpsec defines is the HTTP instrumentation API and contract for -// AppSec. It defines an abstract representation of HTTP handlers, along with -// helper functions to wrap (aka. instrument) standard net/http handlers. -// HTTP integrations must use this package to enable AppSec features for HTTP, -// which listens to this package's operation events. -package httpsec - -import ( - "context" - "encoding/json" - "net" - "net/http" - "reflect" - "strings" - - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation" - "gopkg.in/DataDog/dd-trace-go.v1/internal/log" -) - -// Abstract HTTP handler operation definition. -type ( - // HandlerOperationArgs is the HTTP handler operation arguments. - HandlerOperationArgs struct { - // RequestURI corresponds to the address `server.request.uri.raw` - RequestURI string - // Headers corresponds to the address `server.request.headers.no_cookies` - Headers map[string][]string - // Cookies corresponds to the address `server.request.cookies` - Cookies map[string][]string - // Query corresponds to the address `server.request.query` - Query map[string][]string - // PathParams corresponds to the address `server.request.path_params` - PathParams map[string]string - } - - // HandlerOperationRes is the HTTP handler operation results. - HandlerOperationRes struct { - // Status corresponds to the address `server.response.status`. - Status int - } - - // SDKBodyOperationArgs is the SDK body operation arguments. - SDKBodyOperationArgs struct { - // Body corresponds to the address `server.request.body`. - Body interface{} - } - - // SDKBodyOperationRes is the SDK body operation results. - SDKBodyOperationRes struct{} -) - -// MonitorParsedBody starts and finishes the SDK body operation. -// This function should not be called when AppSec is disabled in order to -// get preciser error logs. -func MonitorParsedBody(ctx context.Context, body interface{}) { - if parent := fromContext(ctx); parent != nil { - op := StartSDKBodyOperation(parent, SDKBodyOperationArgs{Body: body}) - op.Finish() - } else { - log.Error("appsec: parsed http body monitoring ignored: could not find the http handler instrumentation metadata in the request context: the request handler is not being monitored by a middleware function or the provided context is not the expected request context") - } -} - -// WrapHandler wraps the given HTTP handler with the abstract HTTP operation defined by HandlerOperationArgs and -// HandlerOperationRes. -func WrapHandler(handler http.Handler, span ddtrace.Span, pathParams map[string]string) http.Handler { - SetAppSecTags(span) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - args := MakeHandlerOperationArgs(r, pathParams) - ctx, op := StartOperation(r.Context(), args) - r = r.WithContext(ctx) - defer func() { - var status int - if mw, ok := w.(interface{ Status() int }); ok { - status = mw.Status() - } - events := op.Finish(HandlerOperationRes{Status: status}) - instrumentation.SetTags(span, op.Tags()) - if len(events) == 0 { - return - } - - remoteIP, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - remoteIP = r.RemoteAddr - } - SetSecurityEventTags(span, events, remoteIP, args.Headers, w.Header()) - }() - handler.ServeHTTP(w, r) - }) -} - -// MakeHandlerOperationArgs creates the HandlerOperationArgs out of a standard -// http.Request along with the given current span. It returns an empty structure -// when appsec is disabled. -func MakeHandlerOperationArgs(r *http.Request, pathParams map[string]string) HandlerOperationArgs { - headers := make(http.Header, len(r.Header)) - for k, v := range r.Header { - k := strings.ToLower(k) - if k == "cookie" { - // Do not include cookies in the request headers - continue - } - headers[k] = v - } - cookies := makeCookies(r) // TODO(Julio-Guerra): avoid actively parsing the cookies thanks to dynamic instrumentation - headers["host"] = []string{r.Host} - return HandlerOperationArgs{ - RequestURI: r.RequestURI, - Headers: headers, - Cookies: cookies, - Query: r.URL.Query(), // TODO(Julio-Guerra): avoid actively parsing the query values thanks to dynamic instrumentation - PathParams: pathParams, - } -} - -// Return the map of parsed cookies if any and following the specification of -// the rule address `server.request.cookies`. -func makeCookies(r *http.Request) map[string][]string { - parsed := r.Cookies() - if len(parsed) == 0 { - return nil - } - cookies := make(map[string][]string, len(parsed)) - for _, c := range parsed { - cookies[c.Name] = append(cookies[c.Name], c.Value) - } - return cookies -} - -// TODO(Julio-Guerra): create a go-generate tool to generate the types, vars and methods below - -// Operation type representing an HTTP operation. It must be created with -// StartOperation() and finished with its Finish(). -type ( - Operation struct { - dyngo.Operation - instrumentation.TagsHolder - instrumentation.SecurityEventsHolder - } - - // SDKBodyOperation type representing an SDK body. It must be created with - // StartSDKBodyOperation() and finished with its Finish() method. - SDKBodyOperation struct { - dyngo.Operation - } - - contextKey struct{} -) - -// StartOperation starts an HTTP handler operation, along with the given -// context and arguments and emits a start event up in the operation stack. -// The operation is linked to the global root operation since an HTTP operation -// is always expected to be first in the operation stack. -func StartOperation(ctx context.Context, args HandlerOperationArgs) (context.Context, *Operation) { - op := &Operation{ - Operation: dyngo.NewOperation(nil), - TagsHolder: instrumentation.NewTagsHolder(), - } - newCtx := context.WithValue(ctx, contextKey{}, op) - dyngo.StartOperation(op, args) - return newCtx, op -} - -func fromContext(ctx context.Context) *Operation { - // Avoid a runtime panic in case of type-assertion error by collecting the 2 return values - op, _ := ctx.Value(contextKey{}).(*Operation) - return op -} - -// Finish the HTTP handler operation, along with the given results and emits a -// finish event up in the operation stack. -func (op *Operation) Finish(res HandlerOperationRes) []json.RawMessage { - dyngo.FinishOperation(op, res) - return op.Events() -} - -// StartSDKBodyOperation starts the SDKBody operation and emits a start event -func StartSDKBodyOperation(parent *Operation, args SDKBodyOperationArgs) *SDKBodyOperation { - op := &SDKBodyOperation{Operation: dyngo.NewOperation(parent)} - dyngo.StartOperation(op, args) - return op -} - -// Finish finishes the SDKBody operation and emits a finish event -func (op *SDKBodyOperation) Finish() { - dyngo.FinishOperation(op, SDKBodyOperationRes{}) -} - -// HTTP handler operation's start and finish event callback function types. -type ( - // OnHandlerOperationStart function type, called when an HTTP handler - // operation starts. - OnHandlerOperationStart func(*Operation, HandlerOperationArgs) - // OnHandlerOperationFinish function type, called when an HTTP handler - // operation finishes. - OnHandlerOperationFinish func(*Operation, HandlerOperationRes) - // OnSDKBodyOperationStart function type, called when an SDK body - // operation starts. - OnSDKBodyOperationStart func(*SDKBodyOperation, SDKBodyOperationArgs) - // OnSDKBodyOperationFinish function type, called when an SDK body - // operation finishes. - OnSDKBodyOperationFinish func(*SDKBodyOperation, SDKBodyOperationRes) -) - -var ( - handlerOperationArgsType = reflect.TypeOf((*HandlerOperationArgs)(nil)).Elem() - handlerOperationResType = reflect.TypeOf((*HandlerOperationRes)(nil)).Elem() - sdkBodyOperationArgsType = reflect.TypeOf((*SDKBodyOperationArgs)(nil)).Elem() - sdkBodyOperationResType = reflect.TypeOf((*SDKBodyOperationRes)(nil)).Elem() -) - -// ListenedType returns the type a OnHandlerOperationStart event listener -// listens to, which is the HandlerOperationArgs type. -func (OnHandlerOperationStart) ListenedType() reflect.Type { return handlerOperationArgsType } - -// Call calls the underlying event listener function by performing the -// type-assertion on v whose type is the one returned by ListenedType(). -func (f OnHandlerOperationStart) Call(op dyngo.Operation, v interface{}) { - f(op.(*Operation), v.(HandlerOperationArgs)) -} - -// ListenedType returns the type a OnHandlerOperationFinish event listener -// listens to, which is the HandlerOperationRes type. -func (OnHandlerOperationFinish) ListenedType() reflect.Type { return handlerOperationResType } - -// Call calls the underlying event listener function by performing the -// type-assertion on v whose type is the one returned by ListenedType(). -func (f OnHandlerOperationFinish) Call(op dyngo.Operation, v interface{}) { - f(op.(*Operation), v.(HandlerOperationRes)) -} - -// ListenedType returns the type a OnSDKBodyOperationStart event listener -// listens to, which is the SDKBodyOperationStartArgs type. -func (OnSDKBodyOperationStart) ListenedType() reflect.Type { return sdkBodyOperationArgsType } - -// Call calls the underlying event listener function by performing the -// type-assertion on v whose type is the one returned by ListenedType(). -func (f OnSDKBodyOperationStart) Call(op dyngo.Operation, v interface{}) { - f(op.(*SDKBodyOperation), v.(SDKBodyOperationArgs)) -} - -// ListenedType returns the type a OnSDKBodyOperationFinish event listener -// listens to, which is the SDKBodyOperationRes type. -func (OnSDKBodyOperationFinish) ListenedType() reflect.Type { return sdkBodyOperationResType } - -// Call calls the underlying event listener function by performing the -// type-assertion on v whose type is the one returned by ListenedType(). -func (f OnSDKBodyOperationFinish) Call(op dyngo.Operation, v interface{}) { - f(op.(*SDKBodyOperation), v.(SDKBodyOperationRes)) -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/tags.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/tags.go deleted file mode 100644 index c2f870661..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec/tags.go +++ /dev/null @@ -1,81 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -package httpsec - -import ( - "encoding/json" - "sort" - "strings" - - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation" -) - -// SetAppSecTags sets the AppSec-specific span tags that are expected to be in -// the web service entry span (span of type `web`) when AppSec is enabled. -func SetAppSecTags(span ddtrace.Span) { - span.SetTag("_dd.appsec.enabled", 1) - span.SetTag("_dd.runtime_family", "go") -} - -// SetSecurityEventTags sets the AppSec-specific span tags when a security event occurred into the service entry span. -func SetSecurityEventTags(span ddtrace.Span, events []json.RawMessage, remoteIP string, headers, respHeaders map[string][]string) { - instrumentation.SetEventSpanTags(span, events) - span.SetTag("network.client.ip", remoteIP) - for h, v := range NormalizeHTTPHeaders(headers) { - span.SetTag("http.request.headers."+h, v) - } - for h, v := range NormalizeHTTPHeaders(respHeaders) { - span.SetTag("http.response.headers."+h, v) - } -} - -// List of HTTP headers we collect and send. -var collectedHTTPHeaders = [...]string{ - "host", - "x-forwarded-for", - "x-client-ip", - "x-real-ip", - "x-forwarded", - "x-cluster-client-ip", - "forwarded-for", - "forwarded", - "via", - "true-client-ip", - "content-length", - "content-type", - "content-encoding", - "content-language", - "forwarded", - "user-agent", - "accept", - "accept-encoding", - "accept-language", -} - -func init() { - // Required by sort.SearchStrings - sort.Strings(collectedHTTPHeaders[:]) -} - -// NormalizeHTTPHeaders returns the HTTP headers following Datadog's -// normalization format. -func NormalizeHTTPHeaders(headers map[string][]string) (normalized map[string]string) { - if len(headers) == 0 { - return nil - } - normalized = make(map[string]string) - for k, v := range headers { - k = strings.ToLower(k) - if i := sort.SearchStrings(collectedHTTPHeaders[:], k); i < len(collectedHTTPHeaders) && collectedHTTPHeaders[i] == k { - normalized[k] = strings.Join(v, ",") - } - } - if len(normalized) == 0 { - return nil - } - return normalized -} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/operation.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/operation.go index de8b57b4a..e16d357e9 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/operation.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/operation.go @@ -21,12 +21,11 @@ package dyngo import ( - "reflect" - "sort" "sync" - "sync/atomic" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + + "go.uber.org/atomic" ) // Operation interface type allowing to register event listeners to the @@ -34,53 +33,46 @@ import ( // operation once it finishes so that it no longer can be called on finished // operations. type Operation interface { - // On allows to register an event listener to the operation. The event - // listener will be removed from the operation once it finishes. - On(EventListener) - - // Parent return the parent operation. It returns nil for the root - // operation. + // Parent returns the parent operation, or nil for the root operation. Parent() Operation - // emitEvent emits the event to listeners of the given argsType and calls - // them with the given op and v values. - // emitEvent is a private method implemented by the operation struct type so - // that no other package can define it. - emitEvent(argsType reflect.Type, op Operation, v interface{}) - - // register the given event listeners and return the unregistration - // function allowing to remove the event listener from the operation. - // register is a private method implemented by the operation struct type so - // that no other package can define it. - register(...EventListener) UnregisterFunc - - // finish the operation. This method allows to pass the operation value to - // use to emit the finish event. - // finish is a private method implemented by the operation struct type so - // that no other package can define it. - finish(op Operation, results interface{}) + // unwrap is an internal method guaranteeing only *operation implements Operation. + unwrap() *operation } -// EventListener interface allowing to identify the Go type listened to and -// dispatch calls to the underlying event listener function. -type EventListener interface { - // ListenedType returns the Go type the event listener listens to. - ListenedType() reflect.Type - // Call the underlying event listener function. The type of the value v - // is the type the event listener listens to, according to the type - // returned by ListenedType(). - Call(op Operation, v interface{}) +// ArgOf marks a particular type as being the argument type of a given operation +// type. This allows this type to be listened to by an operation start listener. +// This removes the possibility of incorrectly pairing an operation and payload +// when setting up listeners, as it allows compiler-assisted coherence checks. +type ArgOf[O Operation] interface { + IsArgOf(O) } -// UnregisterFunc is a function allowing to unregister from an operation the -// previously registered event listeners. -type UnregisterFunc func() - -var rootOperation = newOperation(nil) +// ResultOf marks a particular type as being the result type of a given +// operation. This allows this type to be listened to by an operation finish +// listener. +// This removes the possibility of incorrectly pairing an operation and payload +// when setting up listeners, as it allows compiler-assisted coherence checks. +type ResultOf[O Operation] interface { + IsResultOf(O) +} -// Register global operation event listeners to listen to. -func Register(listeners ...EventListener) UnregisterFunc { - return rootOperation.register(listeners...) +// EventListener interface allowing to identify the Go type listened to and +// dispatch calls to the underlying event listener function. +type EventListener[O Operation, T any] func(O, T) + +// Atomic *Operation so we can atomically read or swap it. +var rootOperation atomic.Pointer[Operation] + +// SwapRootOperation allows to atomically swap the current root operation with +// the given new one. Concurrent uses of the old root operation on already +// existing and running operation are still valid. +func SwapRootOperation(new Operation) { + rootOperation.Swap(&new) + // Note: calling Finish(old, ...) could result into mem leaks because + // some finish event listeners, possibly releasing memory and resources, + // wouldn't be called anymore (because Finish() disables the operation and + // removes the event listeners). } // operation structure allowing to subscribe to operation events and to @@ -88,81 +80,92 @@ func Register(listeners ...EventListener) UnregisterFunc { // bubble-up the operation stack, which allows listening to future events that // might happen in the operation lifetime. type operation struct { - parent Operation + parent *operation eventRegister + dataBroadcaster disabled bool mu sync.RWMutex } -// NewOperation creates and returns a new operationIt must be started by calling -// StartOperation, and finished by calling FinishOperation. The returned -// operation should be used in wrapper types to provide statically typed start -// and finish functions. The following example shows how to wrap an operation -// so that its functions are statically typed (instead of dyngo's interface{} -// values): -// package mypackage -// import "dyngo" -// type ( -// MyOperation struct { -// dyngo.Operation -// } -// MyOperationArgs { /* ... */ } -// MyOperationRes { /* ... */ } -// ) -// func StartOperation(args MyOperationArgs, parent dyngo.Operation) MyOperation { -// op := MyOperation{Operation: dyngo.NewOperation(parent)} -// dyngo.StartOperation(op, args) -// return op -// } -// func (op MyOperation) Finish(res MyOperationRes) { -// dyngo.FinishOperation(op, res) -// } +func (o *operation) Parent() Operation { + return o.parent +} + +// This is the one true Operation implementation! +func (o *operation) unwrap() *operation { return o } + +// NewRootOperation creates and returns a new root operation, with no parent +// operation. Root operations are meant to be the top-level operation of an +// operation stack, therefore receiving all the operation events. It allows to +// prepare a new set of event listeners, to then atomically swap it with the +// current one. +func NewRootOperation() Operation { + return &operation{parent: nil} +} + +// NewOperation creates and returns a new operation. It must be started by calling +// StartOperation, and finished by calling Finish. The returned operation should +// be used in wrapper types to provide statically typed start and finish +// functions. The following example shows how to wrap an operation so that its +// functions are statically typed (instead of dyngo's interface{} values): +// +// package mypackage +// import "dyngo" +// type ( +// MyOperation struct { +// dyngo.Operation +// } +// MyOperationArgs { /* ... */ } +// MyOperationRes { /* ... */ } +// ) +// func StartOperation(args MyOperationArgs, parent dyngo.Operation) MyOperation { +// op := MyOperation{Operation: dyngo.NewOperation(parent)} +// dyngo.StartOperation(op, args) +// return op +// } +// func (op MyOperation) Finish(res MyOperationRes) { +// dyngo.FinishOperation(op, res) +// } func NewOperation(parent Operation) Operation { if parent == nil { - parent = rootOperation + if ptr := rootOperation.Load(); ptr != nil { + parent = *ptr + } } - return newOperation(parent) + var parentOp *operation + if parent != nil { + parentOp = parent.unwrap() + } + return &operation{parent: parentOp} } // StartOperation starts a new operation along with its arguments and emits a // start event with the operation arguments. -func StartOperation(op Operation, args interface{}) { - argsType := reflect.TypeOf(args) +func StartOperation[O Operation, E ArgOf[O]](op O, args E) { // Bubble-up the start event starting from the parent operation as you can't // listen for your own start event - for current := op.Parent(); current != nil; current = current.Parent() { - current.emitEvent(argsType, op, args) + for current := op.unwrap().parent; current != nil; current = current.parent { + emitEvent(¤t.eventRegister, op, args) } } -func newOperation(parent Operation) *operation { - return &operation{parent: parent} -} - -// Parent return the parent operation. It returns nil for the root operation. -func (o *operation) Parent() Operation { - return o.parent -} - // FinishOperation finishes the operation along with its results and emits a // finish event with the operation results. // The operation is then disabled and its event listeners removed. -func FinishOperation(op Operation, results interface{}) { - op.finish(op, results) -} +func FinishOperation[O Operation, E ResultOf[O]](op O, results E) { + o := op.unwrap() + defer o.disable() // This will need the RLock below to be released... -func (o *operation) finish(op Operation, results interface{}) { - // Defer the call to o.disable() first so that the RWMutex gets unlocked first - defer o.disable() o.mu.RLock() defer o.mu.RUnlock() // Deferred and stacked on top of the previously deferred call to o.disable() + if o.disabled { return } - resType := reflect.TypeOf(results) - for current := op; current != nil; current = current.Parent() { - current.emitEvent(resType, op, results) + + for current := o; current != nil; current = current.parent { + emitEvent(¤t.eventRegister, op, results) } } @@ -170,125 +173,134 @@ func (o *operation) finish(op Operation, results interface{}) { func (o *operation) disable() { o.mu.Lock() defer o.mu.Unlock() + if o.disabled { return } + o.disabled = true o.eventRegister.clear() } -// Register allows to register the given event listeners to the operation. An -// unregistration function is returned allowing to unregister the event -// listeners from the operation. -func (o *operation) register(l ...EventListener) UnregisterFunc { - // eventRegisterIndex allows to lookup for the event listener in the event register. - type eventRegisterIndex struct { - key reflect.Type - id eventListenerID - } +// On registers and event listener that will be called when the operation +// begins. +func On[O Operation, E ArgOf[O]](op Operation, l EventListener[O, E]) { + o := op.unwrap() + o.mu.RLock() defer o.mu.RUnlock() if o.disabled { - return func() {} + return } - indices := make([]eventRegisterIndex, len(l)) - for i, l := range l { - if l == nil { - continue - } - key := l.ListenedType() - id := o.eventRegister.add(key, l) - indices[i] = eventRegisterIndex{ - key: key, - id: id, - } + addEventListener(&o.eventRegister, l) +} + +// OnFinish registers an event listener that will be called when the operation +// finishes. +func OnFinish[O Operation, E ResultOf[O]](op Operation, l EventListener[O, E]) { + o := op.unwrap() + + o.mu.RLock() + defer o.mu.RUnlock() + if o.disabled { + return } - return func() { - for _, ix := range indices { - o.eventRegister.remove(ix.key, ix.id) - } + addEventListener(&o.eventRegister, l) +} + +func OnData[T any](op Operation, l DataListener[T]) { + o := op.unwrap() + + o.mu.RLock() + defer o.mu.RUnlock() + if o.disabled { + return } + addDataListener(&o.dataBroadcaster, l) } -// On registers the event listener. The difference with the Register() is that -// it doesn't return a function closure, which avoids unnecessary allocations -// For example: -// op.On(MyOperationStart(func (op MyOperation, args MyOperationArgs) { -// // ... -// })) -func (o *operation) On(l EventListener) { +// EmitData sends a data event up the operation stack. Listeners will be matched +// based on `T`. Callers may need to manually specify T when the static type of +// the value is more specific that the intended data event type. +func EmitData[T any](op Operation, data T) { + o := op.unwrap() + o.mu.RLock() defer o.mu.RUnlock() if o.disabled { return } - o.eventRegister.add(l.ListenedType(), l) + // Bubble up the data to the stack of operations. Contrary to events, + // we also send the data to ourselves since SDK operations are leaf operations + // that both emit and listen for data (errors). + for current := o; current != nil; current = current.parent { + emitData(¤t.dataBroadcaster, data) + } } type ( // eventRegister implements a thread-safe list of event listeners. eventRegister struct { - mu sync.RWMutex listeners eventListenerMap + mu sync.RWMutex } // eventListenerMap is the map of event listeners. The list of listeners are // indexed by the operation argument or result type the event listener // expects. - eventListenerMap map[reflect.Type][]eventListenerMapEntry - eventListenerMapEntry struct { - id eventListenerID - listener EventListener + eventListenerMap map[any][]any + + typeID[T any] struct{} + + dataBroadcaster struct { + listeners dataListenerMap + mu sync.RWMutex } - // eventListenerID is the unique ID of an event when registering it. It - // allows to find it back and remove it from the list of event listeners - // when unregistering it. - eventListenerID uint32 + DataListener[T any] func(T) + dataListenerMap map[any][]any ) -// lastID is the last event listener ID that was given to the latest event -// listener. -var lastID eventListenerID +func addDataListener[T any](b *dataBroadcaster, l DataListener[T]) { + b.mu.Lock() + defer b.mu.Unlock() -// nextID atomically increments lastID and returns the new event listener ID to -// use. -func nextID() eventListenerID { - return eventListenerID(atomic.AddUint32((*uint32)(&lastID), 1)) + if b.listeners == nil { + b.listeners = make(dataListenerMap) + } + key := typeID[DataListener[T]]{} + b.listeners[key] = append(b.listeners[key], l) } -func (r *eventRegister) add(key reflect.Type, l EventListener) eventListenerID { - r.mu.Lock() - defer r.mu.Unlock() - if r.listeners == nil { - r.listeners = make(eventListenerMap) +func (b *dataBroadcaster) clear() { + b.mu.Lock() + defer b.mu.Unlock() + b.listeners = nil +} + +func emitData[T any](b *dataBroadcaster, v T) { + defer func() { + if r := recover(); r != nil { + log.Error("appsec: recovered from an unexpected panic from an event listener: %+v", r) + } + }() + b.mu.RLock() + defer b.mu.RUnlock() + + for _, listener := range b.listeners[typeID[DataListener[T]]{}] { + listener.(DataListener[T])(v) } - // id is computed when the lock is exclusively taken so that we know - // listeners are added in incremental id order. - // This allows to use the optimized sort.Search() function to remove the - // entry. - id := nextID() - r.listeners[key] = append(r.listeners[key], eventListenerMapEntry{ - id: id, - listener: l, - }) - return id } -func (r *eventRegister) remove(key reflect.Type, id eventListenerID) { +func addEventListener[O Operation, T any](r *eventRegister, l EventListener[O, T]) { r.mu.Lock() defer r.mu.Unlock() + if r.listeners == nil { - return - } - listeners := r.listeners[key] - length := len(listeners) - i := sort.Search(length, func(i int) bool { - return listeners[i].id >= id - }) - if i < length && listeners[i].id == id { - r.listeners[key] = append(listeners[:i], listeners[i+1:]...) + r.listeners = make(eventListenerMap, 2) } + key := typeID[EventListener[O, T]]{} + r.listeners[key] = append(r.listeners[key], l) } func (r *eventRegister) clear() { @@ -297,7 +309,7 @@ func (r *eventRegister) clear() { r.listeners = nil } -func (r *eventRegister) emitEvent(key reflect.Type, op Operation, v interface{}) { +func emitEvent[O Operation, T any](r *eventRegister, op O, v T) { defer func() { if r := recover(); r != nil { log.Error("appsec: recovered from an unexpected panic from an event listener: %+v", r) @@ -305,7 +317,8 @@ func (r *eventRegister) emitEvent(key reflect.Type, op Operation, v interface{}) }() r.mu.RLock() defer r.mu.RUnlock() - for _, e := range r.listeners[key] { - e.listener.Call(op, v) + + for _, listener := range r.listeners[typeID[EventListener[O, T]]{}] { + listener.(EventListener[O, T])(op, v) } } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/actions.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/actions.go new file mode 100644 index 000000000..c082f2542 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/actions.go @@ -0,0 +1,138 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022 Datadog, Inc. + +package sharedsec + +import ( + _ "embed" // Blank import + "errors" + "net/http" + "os" + "strings" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +// blockedTemplateJSON is the default JSON template used to write responses for blocked requests +// +//go:embed blocked-template.json +var blockedTemplateJSON []byte + +// blockedTemplateHTML is the default HTML template used to write responses for blocked requests +// +//go:embed blocked-template.html +var blockedTemplateHTML []byte + +const ( + envBlockedTemplateHTML = "DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML" + envBlockedTemplateJSON = "DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON" +) + +func init() { + for env, template := range map[string]*[]byte{envBlockedTemplateJSON: &blockedTemplateJSON, envBlockedTemplateHTML: &blockedTemplateHTML} { + if path, ok := os.LookupEnv(env); ok { + if t, err := os.ReadFile(path); err != nil { + log.Error("Could not read template at %s: %v", path, err) + } else { + *template = t + } + } + + } +} + +type ( + // Action represents a WAF action. + // It holds the HTTP and gRPC handlers to be used instead of the regular + // request handler when said action is executed. + Action struct { + http http.Handler + grpc GRPCWrapper + blocking bool + } + + // Actions represents a set of action bindings to an action name. + Actions map[string]*Action + + // GRPCWrapper is an opaque prototype abstraction for a gRPC handler (to avoid importing grpc) + // that takes metadata as input and returns a status code and an error + // TODO: rely on strongly typed actions (with the actual grpc types) by introducing WAF constructors + // living in the contrib packages, along with their dependencies - something like `appsec.RegisterWAFConstructor(newGRPCWAF)` + // Such constructors would receive the full appsec config and rules, so that they would be able to build + // specific blocking actions. + GRPCWrapper func(map[string][]string) (uint32, error) +) + +// Blocking returns true if the action object represents a request blocking action +func (a *Action) Blocking() bool { + return a.blocking +} + +// NewBlockHandler creates, initializes and returns a new BlockRequestAction +func NewBlockHandler(status int, template string) http.Handler { + htmlHandler := newBlockRequestHandler(status, "text/html", blockedTemplateHTML) + jsonHandler := newBlockRequestHandler(status, "application/json", blockedTemplateJSON) + switch template { + case "json": + return jsonHandler + case "html": + return htmlHandler + default: + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h := jsonHandler + hdr := r.Header.Get("Accept") + htmlIdx := strings.Index(hdr, "text/html") + jsonIdx := strings.Index(hdr, "application/json") + // Switch to html handler if text/html comes before application/json in the Accept header + if htmlIdx != -1 && (jsonIdx == -1 || htmlIdx < jsonIdx) { + h = htmlHandler + } + h.ServeHTTP(w, r) + }) + } +} + +func newBlockRequestHandler(status int, ct string, payload []byte) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", ct) + w.WriteHeader(status) + w.Write(payload) + }) +} + +func newGRPCBlockHandler(status int) GRPCWrapper { + return func(_ map[string][]string) (uint32, error) { + return uint32(status), errors.New("Request blocked") + } +} + +// NewBlockRequestAction creates an action for the "block" action type +func NewBlockRequestAction(httpStatus, grpcStatus int, template string) *Action { + return &Action{ + http: NewBlockHandler(httpStatus, template), + grpc: newGRPCBlockHandler(grpcStatus), + blocking: true, + } +} + +// NewRedirectRequestAction creates an action for the "redirect" action type +func NewRedirectRequestAction(status int, loc string) *Action { + return &Action{ + http: http.RedirectHandler(loc, status), + // gRPC is not handled by our SRB RFCs so far + // Use the default block handler for now + grpc: newGRPCBlockHandler(10), + } +} + +// HTTP returns the HTTP handler linked to the action object +func (a *Action) HTTP() http.Handler { + return a.http +} + +// GRPC returns the gRPC handler linked to the action object +func (a *Action) GRPC() GRPCWrapper { + return a.grpc +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.html b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.html new file mode 100644 index 000000000..b43edd96d --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.html @@ -0,0 +1 @@ +You've been blocked

Sorry, you cannot access this page. Please contact the customer service team.

\ No newline at end of file diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.json b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.json new file mode 100644 index 000000000..885d766c1 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/blocked-template.json @@ -0,0 +1 @@ +{"errors":[{"title":"You've been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/shared.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/shared.go new file mode 100644 index 000000000..715afc45c --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter/sharedsec/shared.go @@ -0,0 +1,71 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023 Datadog, Inc. + +package sharedsec + +import ( + "context" + "reflect" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo" + "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/listener" + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +type ( + // UserIDOperation type representing a call to appsec.SetUser(). It gets both created and destroyed in a single + // call to ExecuteUserIDOperation + UserIDOperation struct { + dyngo.Operation + } + // UserIDOperationArgs is the user ID operation arguments. + UserIDOperationArgs struct { + UserID string + } + // UserIDOperationRes is the user ID operation results. + UserIDOperationRes struct{} + + // OnUserIDOperationStart function type, called when a user ID + // operation starts. + OnUserIDOperationStart func(operation *UserIDOperation, args UserIDOperationArgs) +) + +var userIDOperationArgsType = reflect.TypeOf((*UserIDOperationArgs)(nil)).Elem() + +// ExecuteUserIDOperation starts and finishes the UserID operation by emitting a dyngo start and finish events +// An error is returned if the user associated to that operation must be blocked +func ExecuteUserIDOperation(parent dyngo.Operation, args UserIDOperationArgs) error { + var err error + op := &UserIDOperation{Operation: dyngo.NewOperation(parent)} + dyngo.OnData(op, func(e error) { err = e }) + dyngo.StartOperation(op, args) + dyngo.FinishOperation(op, UserIDOperationRes{}) + return err +} + +// ListenedType returns the type a OnUserIDOperationStart event listener +// listens to, which is the UserIDOperationStartArgs type. +func (OnUserIDOperationStart) ListenedType() reflect.Type { return userIDOperationArgsType } + +// Call the underlying event listener function by performing the type-assertion +// on v whose type is the one returned by ListenedType(). +func (f OnUserIDOperationStart) Call(op dyngo.Operation, v interface{}) { + f(op.(*UserIDOperation), v.(UserIDOperationArgs)) +} + +// MonitorUser starts and finishes a UserID operation. +// A call to the WAF is made to check the user ID and an error is returned if the +// user should be blocked. The return value is nil otherwise. +func MonitorUser(ctx context.Context, userID string) error { + if parent, ok := ctx.Value(listener.ContextKey{}).(dyngo.Operation); ok { + return ExecuteUserIDOperation(parent, UserIDOperationArgs{UserID: userID}) + } + log.Error("appsec: user ID monitoring ignored: could not find the http handler instrumentation metadata in the request context: the request handler is not being monitored by a middleware function or the provided context is not the expected request context") + return nil + +} + +func (UserIDOperationArgs) IsArgOf(*UserIDOperation) {} +func (UserIDOperationRes) IsResultOf(*UserIDOperation) {} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/listener/listener.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/listener/listener.go new file mode 100644 index 000000000..f1668152e --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/listener/listener.go @@ -0,0 +1,31 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +// Package listener provides functions and types used to listen to AppSec +// instrumentation events produced by code usintrumented using the functions and +// types found in gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/emitter. +package listener + +import waf "github.com/DataDog/go-libddwaf/v2" + +// ContextKey is used as a key to store operations in the request's context (gRPC/HTTP) +type ContextKey struct{} + +// AddressSet is a set of WAF addresses. +type AddressSet map[string]struct{} + +// FilterAddressSet filters the supplied `supported` address set to only include +// entries referenced by the supplied waf.Handle. +func FilterAddressSet(supported AddressSet, handle *waf.Handle) AddressSet { + result := make(AddressSet, len(supported)) + + for _, addr := range handle.Addresses() { + if _, found := supported[addr]; found { + result[addr] = struct{}{} + } + } + + return result +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/remoteconfig.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/remoteconfig.go new file mode 100644 index 000000000..0db534d38 --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/remoteconfig.go @@ -0,0 +1,415 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022 Datadog, Inc. + +package appsec + +import ( + "encoding/json" + "errors" + "fmt" + "os" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config" + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig" + + internal "github.com/DataDog/appsec-internal-go/appsec" + rc "github.com/DataDog/datadog-agent/pkg/remoteconfig/state" +) + +func genApplyStatus(ack bool, err error) rc.ApplyStatus { + status := rc.ApplyStatus{ + State: rc.ApplyStateUnacknowledged, + } + if err != nil { + status.State = rc.ApplyStateError + status.Error = err.Error() + } else if ack { + status.State = rc.ApplyStateAcknowledged + } + + return status +} + +func statusesFromUpdate(u remoteconfig.ProductUpdate, ack bool, err error) map[string]rc.ApplyStatus { + statuses := make(map[string]rc.ApplyStatus, len(u)) + for path := range u { + statuses[path] = genApplyStatus(ack, err) + } + return statuses +} + +func mergeMaps[K comparable, V any](m1 map[K]V, m2 map[K]V) map[K]V { + for key, value := range m2 { + m1[key] = value + } + return m1 +} + +// combineRCRulesUpdates updates the state of the given RulesManager with the combination of all the provided rules updates +func combineRCRulesUpdates(r *config.RulesManager, updates map[string]remoteconfig.ProductUpdate) (statuses map[string]rc.ApplyStatus, err error) { + // Spare some re-allocations (but there may still be some because 1 update may contain N configs) + statuses = make(map[string]rc.ApplyStatus, len(updates)) + // Set the default statuses for all updates to unacknowledged + for _, u := range updates { + statuses = mergeMaps(statuses, statusesFromUpdate(u, false, nil)) + } + +updateLoop: + // Process rules related updates + for p, u := range updates { + if u != nil && len(u) == 0 { + continue + } + switch p { + case rc.ProductASMData: + // Merge all rules data entries together and store them as a RulesManager edit entry + rulesData, status := mergeRulesData(u) + statuses = mergeMaps(statuses, status) + r.AddEdit("asmdata", config.RulesFragment{RulesData: rulesData}) + case rc.ProductASMDD: + var ( + removalFound = false + newBasePath string + newBaseData []byte + ) + for path, data := range u { + if data == nil && removalFound { + err = errors.New("more than one config removal received for ASM_DD") + } else if data != nil && newBaseData != nil { + err = errors.New("more than one config switch received for ASM_DD") + } + // Already seen a removal or an update, return an error + if err != nil { + statuses = mergeMaps(statuses, statusesFromUpdate(u, true, err)) + break updateLoop + } + + if data == nil { + removalFound = true + continue + } + + // Save the new base path and data and only make the update after cycle through all received configs + // This makes sure that we don't update the ruleset in case the update is invalid (ex: several non nil configs) + newBasePath = path + newBaseData = data + } + // update with data = nil means the config was removed, so we switch back to the default rules + // only happens if no update was found, otherwise it could revert the switch to the new base rules + if newBaseData == nil { + if removalFound { + log.Debug("appsec: Remote config: ASM_DD config removed. Switching back to default rules") + r.ChangeBase(config.DefaultRulesFragment(), "") + statuses = mergeMaps(statuses, statusesFromUpdate(u, true, nil)) + } + continue + } + + // Switch the base rules of the RulesManager if the config received through ASM_DD is valid + var newBase config.RulesFragment + if err := json.Unmarshal(newBaseData, &newBase); err != nil { + log.Debug("appsec: Remote config: could not unmarshall ASM_DD rules: %v", err) + statuses[newBasePath] = genApplyStatus(true, err) + break updateLoop + } + log.Debug("appsec: Remote config: switching to %s as the base rules file", newBasePath) + r.ChangeBase(newBase, newBasePath) + statuses[newBasePath] = genApplyStatus(true, nil) + case rc.ProductASM: + // Store each config received through ASM as an edit entry in the RulesManager + // Those entries will get merged together when the final rules are compiled + // If a config gets removed, the RulesManager edit entry gets removed as well + for path, data := range u { + log.Debug("appsec: Remote config: processing the %s ASM config", path) + if data == nil { + log.Debug("appsec: Remote config: ASM config %s was removed", path) + r.RemoveEdit(path) + continue + } + var f config.RulesFragment + if err = json.Unmarshal(data, &f); err != nil { + log.Debug("appsec: Remote config: error processing ASM config %s: %v", path, err) + statuses[path] = genApplyStatus(true, err) + break updateLoop + } + r.AddEdit(path, f) + } + default: + log.Debug("appsec: Remote config: ignoring unsubscribed product %s", p) + } + } + + // Set all statuses to ack if no error occured + if err == nil { + for _, u := range updates { + statuses = mergeMaps(statuses, statusesFromUpdate(u, true, nil)) + } + } + + return statuses, err + +} + +// onRemoteActivation is the RC callback called when an update is received for ASM_FEATURES +func (a *appsec) onRemoteActivation(updates map[string]remoteconfig.ProductUpdate) map[string]rc.ApplyStatus { + statuses := map[string]rc.ApplyStatus{} + if u, ok := updates[rc.ProductASMFeatures]; ok { + statuses = a.handleASMFeatures(u) + } + return statuses + +} + +// onRCRulesUpdate is the RC callback called when security rules related RC updates are available +func (a *appsec) onRCRulesUpdate(updates map[string]remoteconfig.ProductUpdate) map[string]rc.ApplyStatus { + // If appsec was deactivated through RC, stop here + if !a.started { + return map[string]rc.ApplyStatus{} + } + + // Create a new local RulesManager + r := a.cfg.RulesManager.Clone() + statuses, err := combineRCRulesUpdates(&r, updates) + if err != nil { + log.Debug("appsec: Remote config: not applying any updates because of error: %v", err) + return statuses + } + + // Compile the final rules once all updates have been processed and no error occurred + r.Compile() + log.Debug("appsec: Remote config: final compiled rules: %s", r.String()) + + // If an error occurs while updating the WAF handle, don't swap the RulesManager and propagate the error + // to all config statuses since we can't know which config is the faulty one + if err = a.swapWAF(r.Latest); err != nil { + log.Error("appsec: Remote config: could not apply the new security rules: %v", err) + for k := range statuses { + statuses[k] = genApplyStatus(true, err) + } + return statuses + } + // Replace the RulesManager with the new one holding the new state + a.cfg.RulesManager = &r + + return statuses +} + +// handleASMFeatures deserializes an ASM_FEATURES configuration received through remote config +// and starts/stops appsec accordingly. +func (a *appsec) handleASMFeatures(u remoteconfig.ProductUpdate) map[string]rc.ApplyStatus { + statuses := statusesFromUpdate(u, false, nil) + if l := len(u); l > 1 { + log.Error("appsec: Remote config: %d configs received for ASM_FEATURES. Expected one at most, returning early", l) + return statuses + } + for path, raw := range u { + var data rc.ASMFeaturesData + status := rc.ApplyStatus{State: rc.ApplyStateAcknowledged} + var err error + log.Debug("appsec: Remote config: processing %s", path) + + // A nil config means ASM was disabled, and we stopped receiving the config file + // Don't ack the config in this case and return early + if raw == nil { + log.Debug("appsec: Remote config: Stopping AppSec") + a.stop() + return statuses + } + if err = json.Unmarshal(raw, &data); err != nil { + log.Error("appsec: Remote config: error while unmarshalling %s: %v. Configuration won't be applied.", path, err) + } else if data.ASM.Enabled && !a.started { + log.Debug("appsec: Remote config: Starting AppSec") + telemetry := newAppsecTelemetry() + defer telemetry.emit() + if err = a.start(telemetry); err != nil { + log.Error("appsec: Remote config: error while processing %s. Configuration won't be applied: %v", path, err) + } + } else if !data.ASM.Enabled && a.started { + log.Debug("appsec: Remote config: Stopping AppSec") + a.stop() + } + if err != nil { + status = genApplyStatus(false, err) + } + statuses[path] = status + } + + return statuses +} + +func mergeRulesData(u remoteconfig.ProductUpdate) ([]config.RuleDataEntry, map[string]rc.ApplyStatus) { + // Following the RFC, merging should only happen when two rules data with the same ID and same Type are received + // allRulesData[ID][Type] will return the rules data of said id and type, if it exists + allRulesData := make(map[string]map[string]config.RuleDataEntry) + statuses := statusesFromUpdate(u, true, nil) + + for path, raw := range u { + log.Debug("appsec: Remote config: processing %s", path) + + // A nil config means ASM_DATA was disabled, and we stopped receiving the config file + // Don't ack the config in this case + if raw == nil { + log.Debug("appsec: remote config: %s disabled", path) + statuses[path] = genApplyStatus(false, nil) + continue + } + + var rulesData config.RulesData + if err := json.Unmarshal(raw, &rulesData); err != nil { + log.Debug("appsec: Remote config: error while unmarshalling payload for %s: %v. Configuration won't be applied.", path, err) + statuses[path] = genApplyStatus(false, err) + continue + } + + // Check each entry against allRulesData to see if merging is necessary + for _, ruleData := range rulesData.RulesData { + if allRulesData[ruleData.ID] == nil { + allRulesData[ruleData.ID] = make(map[string]config.RuleDataEntry) + } + if data, ok := allRulesData[ruleData.ID][ruleData.Type]; ok { + // Merge rules data entries with the same ID and Type + data.Data = mergeRulesDataEntries(data.Data, ruleData.Data) + allRulesData[ruleData.ID][ruleData.Type] = data + } else { + allRulesData[ruleData.ID][ruleData.Type] = ruleData + } + } + } + + // Aggregate all the rules data before passing it over to the WAF + var rulesData []config.RuleDataEntry + for _, m := range allRulesData { + for _, data := range m { + rulesData = append(rulesData, data) + } + } + return rulesData, statuses +} + +// mergeRulesDataEntries merges two slices of rules data entries together, removing duplicates and +// only keeping the longest expiration values for similar entries. +func mergeRulesDataEntries(entries1, entries2 []rc.ASMDataRuleDataEntry) []rc.ASMDataRuleDataEntry { + // There will be at most len(entries1) + len(entries2) entries in the merge map + mergeMap := make(map[string]int64, len(entries1)+len(entries2)) + + for _, entry := range entries1 { + mergeMap[entry.Value] = entry.Expiration + } + // Replace the entry only if the new expiration timestamp goes later than the current one + // If no expiration timestamp was provided (default to 0), then the data doesn't expire + for _, entry := range entries2 { + if exp, ok := mergeMap[entry.Value]; !ok || entry.Expiration == 0 || entry.Expiration > exp { + mergeMap[entry.Value] = entry.Expiration + } + } + // Create the final slice and return it + entries := make([]rc.ASMDataRuleDataEntry, 0, len(mergeMap)) + for val, exp := range mergeMap { + entries = append(entries, rc.ASMDataRuleDataEntry{Value: val, Expiration: exp}) + } + return entries +} + +func (a *appsec) startRC() error { + if a.cfg.RC != nil { + return remoteconfig.Start(*a.cfg.RC) + } + return nil +} + +func (a *appsec) stopRC() { + if a.cfg.RC != nil { + remoteconfig.Stop() + } +} + +func (a *appsec) registerRCProduct(p string) error { + if a.cfg.RC == nil { + return fmt.Errorf("no valid remote configuration client") + } + return remoteconfig.RegisterProduct(p) +} + +func (a *appsec) registerRCCapability(c remoteconfig.Capability) error { + if a.cfg.RC == nil { + return fmt.Errorf("no valid remote configuration client") + } + return remoteconfig.RegisterCapability(c) +} + +func (a *appsec) unregisterRCCapability(c remoteconfig.Capability) error { + if a.cfg.RC == nil { + log.Debug("appsec: Remote config: no valid remote configuration client") + return nil + } + return remoteconfig.UnregisterCapability(c) +} + +func (a *appsec) enableRemoteActivation() error { + if a.cfg.RC == nil { + return fmt.Errorf("no valid remote configuration client") + } + err := a.registerRCProduct(rc.ProductASMFeatures) + if err != nil { + return err + } + err = a.registerRCCapability(remoteconfig.ASMActivation) + if err != nil { + return err + } + return remoteconfig.RegisterCallback(a.onRemoteActivation) +} + +var blockingCapabilities = [...]remoteconfig.Capability{ + remoteconfig.ASMUserBlocking, + remoteconfig.ASMRequestBlocking, + remoteconfig.ASMIPBlocking, + remoteconfig.ASMDDRules, + remoteconfig.ASMExclusions, + remoteconfig.ASMCustomRules, + remoteconfig.ASMCustomBlockingResponse, + remoteconfig.ASMTrustedIPs, +} + +func (a *appsec) enableRCBlocking() { + if a.cfg.RC == nil { + log.Debug("appsec: Remote config: no valid remote configuration client") + return + } + + products := []string{rc.ProductASM, rc.ProductASMDD, rc.ProductASMData} + for _, p := range products { + if err := a.registerRCProduct(p); err != nil { + log.Debug("appsec: Remote config: couldn't register product %s: %v", p, err) + } + } + + if err := remoteconfig.RegisterCallback(a.onRCRulesUpdate); err != nil { + log.Debug("appsec: Remote config: couldn't register callback: %v", err) + } + + if _, isSet := os.LookupEnv(internal.EnvRules); !isSet { + for _, c := range blockingCapabilities { + if err := a.registerRCCapability(c); err != nil { + log.Debug("appsec: Remote config: couldn't register capability %v: %v", c, err) + } + } + } +} + +func (a *appsec) disableRCBlocking() { + if a.cfg.RC == nil { + return + } + for _, c := range blockingCapabilities { + if err := a.unregisterRCCapability(c); err != nil { + log.Debug("appsec: Remote config: couldn't unregister capability %v: %v", c, err) + } + } + if err := remoteconfig.UnregisterCallback(a.onRCRulesUpdate); err != nil { + log.Debug("appsec: Remote config: couldn't unregister callback: %v", err) + } +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/rule.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/rule.go deleted file mode 100644 index e912b93b6..000000000 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/rule.go +++ /dev/null @@ -1,13 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. - -//go:build appsec -// +build appsec - -package appsec - -// Static recommended AppSec rule 1.3.1 -// Source: https://github.com/DataDog/appsec-event-rules/blob/1.3.1/build/recommended.json -const staticRecommendedRule = "{\"version\":\"2.2\",\"metadata\":{\"rules_version\":\"1.3.1\"},\"rules\":[{\"id\":\"crs-913-110\",\"name\":\"Acunetix\",\"tags\":{\"type\":\"security_scanner\",\"crs_id\":\"913110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\"}],\"list\":[\"acunetix-product\",\"(acunetix web vulnerability scanner\",\"acunetix-scanning-agreement\",\"acunetix-user-agreement\",\"md5(acunetix_wvs_security_test)\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-913-120\",\"name\":\"Known security scanner filename/argument\",\"tags\":{\"type\":\"security_scanner\",\"crs_id\":\"913120\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"}],\"list\":[\"/.adsensepostnottherenonobook\",\"/hello.html\",\"/actsensepostnottherenonotive\",\"/acunetix-wvs-test-for-some-inexistent-file\",\"/antidisestablishmentarianism\",\"/appscan_fingerprint/mac_address\",\"/arachni-\",\"/cybercop\",\"/nessus_is_probing_you_\",\"/nessustest\",\"/netsparker-\",\"/rfiinc.txt\",\"/thereisnowaythat-you-canbethere\",\"/w3af/remotefileinclude.html\",\"appscan_fingerprint\",\"w00tw00t.at.isc.sans.dfind\",\"w00tw00t.at.blackhats.romanian.anti-sec\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-920-260\",\"name\":\"Unicode Full/Half Width Abuse Attack Attempt\",\"tags\":{\"type\":\"http_protocol_violation\",\"crs_id\":\"920260\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"\\\\%u[fF]{2}[0-9a-fA-F]{2}\",\"options\":{\"case_sensitive\":true,\"min_length\":6}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-921-110\",\"name\":\"HTTP Request Smuggling Attack\",\"tags\":{\"type\":\"http_protocol_violation\",\"crs_id\":\"921110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"}],\"regex\":\"(?:get|post|head|options|connect|put|delete|trace|track|patch|propfind|propatch|mkcol|copy|move|lock|unlock)\\\\s+[^\\\\s]+\\\\s+http/\\\\d\",\"options\":{\"case_sensitive\":true,\"min_length\":12}},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-921-140\",\"name\":\"HTTP Header Injection Attack via headers\",\"tags\":{\"type\":\"http_protocol_violation\",\"crs_id\":\"921140\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\"}],\"regex\":\"[\\\\n\\\\r]\",\"options\":{\"case_sensitive\":true,\"min_length\":1}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-921-160\",\"name\":\"HTTP Header Injection Attack via payload (CR/LF and header-name detected)\",\"tags\":{\"type\":\"http_protocol_violation\",\"crs_id\":\"921160\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.path_params\"}],\"regex\":\"[\\\\n\\\\r]+(?:\\\\s|location|refresh|(?:set-)?cookie|(?:x-)?(?:forwarded-(?:for|host|server)|host|via|remote-ip|remote-addr|originating-IP))\\\\s*:\",\"options\":{\"case_sensitive\":true,\"min_length\":3}},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-930-100\",\"name\":\"Obfuscated Path Traversal Attack (/../)\",\"tags\":{\"type\":\"lfi\",\"crs_id\":\"930100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"},{\"address\":\"server.request.headers.no_cookies\"}],\"regex\":\"(?:\\\\x5c|(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|/))(?:%(?:(?:f(?:(?:c%80|8)%8)?0%8|e)0%80%ae|2(?:(?:5(?:c0%25a|2))?e|%45)|u(?:(?:002|ff0)e|2024)|%32(?:%(?:%6|4)5|E)|c0(?:%[256aef]e|\\\\.))|\\\\.(?:%0[01]|\\\\?)?|\\\\?\\\\.?|0x2e){2}(?:\\\\x5c|(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|/))\",\"options\":{\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[\"normalizePath\"]},{\"id\":\"crs-930-110\",\"name\":\"Simple Path Traversal Attack (/../)\",\"tags\":{\"type\":\"lfi\",\"crs_id\":\"930110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"},{\"address\":\"server.request.headers.no_cookies\"}],\"regex\":\"(?:(?:^|[\\\\\\\\/])\\\\.\\\\.[\\\\\\\\/]|[\\\\\\\\/]\\\\.\\\\.(?:[\\\\\\\\/]|$))\",\"options\":{\"case_sensitive\":true,\"min_length\":3}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-930-120\",\"name\":\"OS File Access Attempt\",\"tags\":{\"type\":\"lfi\",\"crs_id\":\"930120\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"/.htaccess\",\"/.htdigest\",\"/.htpasswd\",\"/.addressbook\",\"/.aptitude/config\",\"/.bash_config\",\"/.bash_history\",\"/.bash_logout\",\"/.bash_profile\",\"/.bashrc\",\".cache/notify-osd.log\",\".config/odesk/odesk team.conf\",\"/.cshrc\",\"/.dockerignore\",\".drush/\",\"/.eslintignore\",\"/.fbcindex\",\"/.forward\",\"/.git\",\".git/\",\"/.gitattributes\",\"/.gitconfig\",\".gnupg/\",\".hplip/hplip.conf\",\"/.ksh_history\",\"/.lesshst\",\".lftp/\",\"/.lhistory\",\"/.lldb-history\",\".local/share/mc/\",\"/.lynx_cookies\",\"/.my.cnf\",\"/.mysql_history\",\"/.nano_history\",\"/.node_repl_history\",\"/.pearrc\",\"/.php_history\",\"/.pinerc\",\".pki/\",\"/.proclog\",\"/.procmailrc\",\"/.psql_history\",\"/.python_history\",\"/.rediscli_history\",\"/.rhistory\",\"/.rhosts\",\"/.sh_history\",\"/.sqlite_history\",\".ssh/authorized_keys\",\".ssh/config\",\".ssh/id_dsa\",\".ssh/id_dsa.pub\",\".ssh/id_rsa\",\".ssh/id_rsa.pub\",\".ssh/identity\",\".ssh/identity.pub\",\".ssh/known_hosts\",\".subversion/auth\",\".subversion/config\",\".subversion/servers\",\".tconn/tconn.conf\",\"/.tcshrc\",\".vidalia/vidalia.conf\",\"/.viminfo\",\"/.vimrc\",\"/.www_acl\",\"/.wwwacl\",\"/.xauthority\",\"/.zhistory\",\"/.zshrc\",\"/.zsh_history\",\"/.nsconfig\",\"etc/redis.conf\",\"etc/redis-sentinel.conf\",\"etc/php.ini\",\"bin/php.ini\",\"etc/httpd/php.ini\",\"usr/lib/php.ini\",\"usr/lib/php/php.ini\",\"usr/local/etc/php.ini\",\"usr/local/lib/php.ini\",\"usr/local/php/lib/php.ini\",\"usr/local/php4/lib/php.ini\",\"usr/local/php5/lib/php.ini\",\"usr/local/apache/conf/php.ini\",\"etc/php4.4/fcgi/php.ini\",\"etc/php4/apache/php.ini\",\"etc/php4/apache2/php.ini\",\"etc/php5/apache/php.ini\",\"etc/php5/apache2/php.ini\",\"etc/php/php.ini\",\"etc/php/php4/php.ini\",\"etc/php/apache/php.ini\",\"etc/php/apache2/php.ini\",\"web/conf/php.ini\",\"usr/local/zend/etc/php.ini\",\"opt/xampp/etc/php.ini\",\"var/local/www/conf/php.ini\",\"etc/php/cgi/php.ini\",\"etc/php4/cgi/php.ini\",\"etc/php5/cgi/php.ini\",\"home2/bin/stable/apache/php.ini\",\"home/bin/stable/apache/php.ini\",\"etc/httpd/conf.d/php.conf\",\"php5/php.ini\",\"php4/php.ini\",\"php/php.ini\",\"windows/php.ini\",\"winnt/php.ini\",\"apache/php/php.ini\",\"xampp/apache/bin/php.ini\",\"netserver/bin/stable/apache/php.ini\",\"volumes/macintosh_hd1/usr/local/php/lib/php.ini\",\"etc/mono/1.0/machine.config\",\"etc/mono/2.0/machine.config\",\"etc/mono/2.0/web.config\",\"etc/mono/config\",\"usr/local/cpanel/logs/stats_log\",\"usr/local/cpanel/logs/access_log\",\"usr/local/cpanel/logs/error_log\",\"usr/local/cpanel/logs/license_log\",\"usr/local/cpanel/logs/login_log\",\"var/cpanel/cpanel.config\",\"var/log/sw-cp-server/error_log\",\"usr/local/psa/admin/logs/httpsd_access_log\",\"usr/local/psa/admin/logs/panel.log\",\"var/log/sso/sso.log\",\"usr/local/psa/admin/conf/php.ini\",\"etc/sw-cp-server/applications.d/plesk.conf\",\"usr/local/psa/admin/conf/site_isolation_settings.ini\",\"usr/local/sb/config\",\"etc/sw-cp-server/applications.d/00-sso-cpserver.conf\",\"etc/sso/sso_config.ini\",\"etc/mysql/conf.d/old_passwords.cnf\",\"var/log/mysql/mysql-bin.log\",\"var/log/mysql/mysql-bin.index\",\"var/log/mysql/data/mysql-bin.index\",\"var/log/mysql.log\",\"var/log/mysql.err\",\"var/log/mysqlderror.log\",\"var/log/mysql/mysql.log\",\"var/log/mysql/mysql-slow.log\",\"var/log/mysql-bin.index\",\"var/log/data/mysql-bin.index\",\"var/mysql.log\",\"var/mysql-bin.index\",\"var/data/mysql-bin.index\",\"program files/mysql/mysql server 5.0/data/{host}.err\",\"program files/mysql/mysql server 5.0/data/mysql.log\",\"program files/mysql/mysql server 5.0/data/mysql.err\",\"program files/mysql/mysql server 5.0/data/mysql-bin.log\",\"program files/mysql/mysql server 5.0/data/mysql-bin.index\",\"program files/mysql/data/{host}.err\",\"program files/mysql/data/mysql.log\",\"program files/mysql/data/mysql.err\",\"program files/mysql/data/mysql-bin.log\",\"program files/mysql/data/mysql-bin.index\",\"mysql/data/{host}.err\",\"mysql/data/mysql.log\",\"mysql/data/mysql.err\",\"mysql/data/mysql-bin.log\",\"mysql/data/mysql-bin.index\",\"usr/local/mysql/data/mysql.log\",\"usr/local/mysql/data/mysql.err\",\"usr/local/mysql/data/mysql-bin.log\",\"usr/local/mysql/data/mysql-slow.log\",\"usr/local/mysql/data/mysqlderror.log\",\"usr/local/mysql/data/{host}.err\",\"usr/local/mysql/data/mysql-bin.index\",\"var/lib/mysql/my.cnf\",\"etc/mysql/my.cnf\",\"etc/my.cnf\",\"program files/mysql/mysql server 5.0/my.ini\",\"program files/mysql/mysql server 5.0/my.cnf\",\"program files/mysql/my.ini\",\"program files/mysql/my.cnf\",\"mysql/my.ini\",\"mysql/my.cnf\",\"mysql/bin/my.ini\",\"var/postgresql/log/postgresql.log\",\"var/log/postgresql/postgresql.log\",\"var/log/postgres/pg_backup.log\",\"var/log/postgres/postgres.log\",\"var/log/postgresql.log\",\"var/log/pgsql/pgsql.log\",\"var/log/postgresql/postgresql-8.1-main.log\",\"var/log/postgresql/postgresql-8.3-main.log\",\"var/log/postgresql/postgresql-8.4-main.log\",\"var/log/postgresql/postgresql-9.0-main.log\",\"var/log/postgresql/postgresql-9.1-main.log\",\"var/log/pgsql8.log\",\"var/log/postgresql/postgres.log\",\"var/log/pgsql_log\",\"var/log/postgresql/main.log\",\"var/log/cron/var/log/postgres.log\",\"usr/internet/pgsql/data/postmaster.log\",\"usr/local/pgsql/data/postgresql.log\",\"usr/local/pgsql/data/pg_log\",\"postgresql/log/pgadmin.log\",\"var/lib/pgsql/data/postgresql.conf\",\"var/postgresql/db/postgresql.conf\",\"var/nm2/postgresql.conf\",\"usr/local/pgsql/data/postgresql.conf\",\"usr/local/pgsql/data/pg_hba.conf\",\"usr/internet/pgsql/data/pg_hba.conf\",\"usr/local/pgsql/data/passwd\",\"usr/local/pgsql/bin/pg_passwd\",\"etc/postgresql/postgresql.conf\",\"etc/postgresql/pg_hba.conf\",\"home/postgres/data/postgresql.conf\",\"home/postgres/data/pg_version\",\"home/postgres/data/pg_ident.conf\",\"home/postgres/data/pg_hba.conf\",\"program files/postgresql/8.3/data/pg_hba.conf\",\"program files/postgresql/8.3/data/pg_ident.conf\",\"program files/postgresql/8.3/data/postgresql.conf\",\"program files/postgresql/8.4/data/pg_hba.conf\",\"program files/postgresql/8.4/data/pg_ident.conf\",\"program files/postgresql/8.4/data/postgresql.conf\",\"program files/postgresql/9.0/data/pg_hba.conf\",\"program files/postgresql/9.0/data/pg_ident.conf\",\"program files/postgresql/9.0/data/postgresql.conf\",\"program files/postgresql/9.1/data/pg_hba.conf\",\"program files/postgresql/9.1/data/pg_ident.conf\",\"program files/postgresql/9.1/data/postgresql.conf\",\"wamp/logs/access.log\",\"wamp/logs/apache_error.log\",\"wamp/logs/genquery.log\",\"wamp/logs/mysql.log\",\"wamp/logs/slowquery.log\",\"wamp/bin/apache/apache2.2.22/logs/access.log\",\"wamp/bin/apache/apache2.2.22/logs/error.log\",\"wamp/bin/apache/apache2.2.21/logs/access.log\",\"wamp/bin/apache/apache2.2.21/logs/error.log\",\"wamp/bin/mysql/mysql5.5.24/data/mysql-bin.index\",\"wamp/bin/mysql/mysql5.5.16/data/mysql-bin.index\",\"wamp/bin/apache/apache2.2.21/conf/httpd.conf\",\"wamp/bin/apache/apache2.2.22/conf/httpd.conf\",\"wamp/bin/apache/apache2.2.21/wampserver.conf\",\"wamp/bin/apache/apache2.2.22/wampserver.conf\",\"wamp/bin/apache/apache2.2.22/conf/wampserver.conf\",\"wamp/bin/mysql/mysql5.5.24/my.ini\",\"wamp/bin/mysql/mysql5.5.24/wampserver.conf\",\"wamp/bin/mysql/mysql5.5.16/my.ini\",\"wamp/bin/mysql/mysql5.5.16/wampserver.conf\",\"wamp/bin/php/php5.3.8/php.ini\",\"wamp/bin/php/php5.4.3/php.ini\",\"xampp/apache/logs/access.log\",\"xampp/apache/logs/error.log\",\"xampp/mysql/data/mysql-bin.index\",\"xampp/mysql/data/mysql.err\",\"xampp/mysql/data/{host}.err\",\"xampp/sendmail/sendmail.log\",\"xampp/apache/conf/httpd.conf\",\"xampp/filezillaftp/filezilla server.xml\",\"xampp/mercurymail/mercury.ini\",\"xampp/php/php.ini\",\"xampp/phpmyadmin/config.inc.php\",\"xampp/sendmail/sendmail.ini\",\"xampp/webalizer/webalizer.conf\",\"opt/lampp/etc/httpd.conf\",\"xampp/htdocs/aca.txt\",\"xampp/htdocs/admin.php\",\"xampp/htdocs/leer.txt\",\"usr/local/apache/logs/audit_log\",\"usr/local/apache2/logs/audit_log\",\"logs/security_debug_log\",\"logs/security_log\",\"usr/local/apache/conf/modsec.conf\",\"usr/local/apache2/conf/modsec.conf\",\"winnt/system32/logfiles/msftpsvc\",\"winnt/system32/logfiles/msftpsvc1\",\"winnt/system32/logfiles/msftpsvc2\",\"windows/system32/logfiles/msftpsvc\",\"windows/system32/logfiles/msftpsvc1\",\"windows/system32/logfiles/msftpsvc2\",\"etc/logrotate.d/proftpd\",\"www/logs/proftpd.system.log\",\"var/log/proftpd\",\"var/log/proftpd/xferlog.legacy\",\"var/log/proftpd.access_log\",\"var/log/proftpd.xferlog\",\"etc/pam.d/proftpd\",\"etc/proftp.conf\",\"etc/protpd/proftpd.conf\",\"etc/vhcs2/proftpd/proftpd.conf\",\"etc/proftpd/modules.conf\",\"var/log/vsftpd.log\",\"etc/vsftpd.chroot_list\",\"etc/logrotate.d/vsftpd.log\",\"etc/vsftpd/vsftpd.conf\",\"etc/vsftpd.conf\",\"etc/chrootusers\",\"var/log/xferlog\",\"var/adm/log/xferlog\",\"etc/wu-ftpd/ftpaccess\",\"etc/wu-ftpd/ftphosts\",\"etc/wu-ftpd/ftpusers\",\"var/log/pure-ftpd/pure-ftpd.log\",\"logs/pure-ftpd.log\",\"var/log/pureftpd.log\",\"usr/sbin/pure-config.pl\",\"usr/etc/pure-ftpd.conf\",\"etc/pure-ftpd/pure-ftpd.conf\",\"usr/local/etc/pure-ftpd.conf\",\"usr/local/etc/pureftpd.pdb\",\"usr/local/pureftpd/etc/pureftpd.pdb\",\"usr/local/pureftpd/sbin/pure-config.pl\",\"usr/local/pureftpd/etc/pure-ftpd.conf\",\"etc/pure-ftpd.conf\",\"etc/pure-ftpd/pure-ftpd.pdb\",\"etc/pureftpd.pdb\",\"etc/pureftpd.passwd\",\"etc/pure-ftpd/pureftpd.pdb\",\"usr/ports/ftp/pure-ftpd/pure-ftpd.conf\",\"usr/ports/ftp/pure-ftpd/pureftpd.pdb\",\"usr/ports/ftp/pure-ftpd/pureftpd.passwd\",\"usr/ports/net/pure-ftpd/pure-ftpd.conf\",\"usr/ports/net/pure-ftpd/pureftpd.pdb\",\"usr/ports/net/pure-ftpd/pureftpd.passwd\",\"usr/pkgsrc/net/pureftpd/pure-ftpd.conf\",\"usr/pkgsrc/net/pureftpd/pureftpd.pdb\",\"usr/pkgsrc/net/pureftpd/pureftpd.passwd\",\"usr/ports/contrib/pure-ftpd/pure-ftpd.conf\",\"usr/ports/contrib/pure-ftpd/pureftpd.pdb\",\"usr/ports/contrib/pure-ftpd/pureftpd.passwd\",\"var/log/muddleftpd\",\"usr/sbin/mudlogd\",\"etc/muddleftpd/mudlog\",\"etc/muddleftpd.com\",\"etc/muddleftpd/mudlogd.conf\",\"etc/muddleftpd/muddleftpd.conf\",\"var/log/muddleftpd.conf\",\"usr/sbin/mudpasswd\",\"etc/muddleftpd/muddleftpd.passwd\",\"etc/muddleftpd/passwd\",\"var/log/ftp-proxy/ftp-proxy.log\",\"var/log/ftp-proxy\",\"var/log/ftplog\",\"etc/logrotate.d/ftp\",\"etc/ftpchroot\",\"etc/ftphosts\",\"etc/ftpusers\",\"var/log/exim_mainlog\",\"var/log/exim/mainlog\",\"var/log/maillog\",\"var/log/exim_paniclog\",\"var/log/exim/paniclog\",\"var/log/exim/rejectlog\",\"var/log/exim_rejectlog\",\"winnt/system32/logfiles/smtpsvc\",\"winnt/system32/logfiles/smtpsvc1\",\"winnt/system32/logfiles/smtpsvc2\",\"winnt/system32/logfiles/smtpsvc3\",\"winnt/system32/logfiles/smtpsvc4\",\"winnt/system32/logfiles/smtpsvc5\",\"windows/system32/logfiles/smtpsvc\",\"windows/system32/logfiles/smtpsvc1\",\"windows/system32/logfiles/smtpsvc2\",\"windows/system32/logfiles/smtpsvc3\",\"windows/system32/logfiles/smtpsvc4\",\"windows/system32/logfiles/smtpsvc5\",\"etc/osxhttpd/osxhttpd.conf\",\"system/library/webobjects/adaptors/apache2.2/apache.conf\",\"etc/apache2/sites-available/default\",\"etc/apache2/sites-available/default-ssl\",\"etc/apache2/sites-enabled/000-default\",\"etc/apache2/sites-enabled/default\",\"etc/apache2/apache2.conf\",\"etc/apache2/ports.conf\",\"usr/local/etc/apache/httpd.conf\",\"usr/pkg/etc/httpd/httpd.conf\",\"usr/pkg/etc/httpd/httpd-default.conf\",\"usr/pkg/etc/httpd/httpd-vhosts.conf\",\"etc/httpd/mod_php.conf\",\"etc/httpd/extra/httpd-ssl.conf\",\"etc/rc.d/rc.httpd\",\"usr/local/apache/conf/httpd.conf.default\",\"usr/local/apache/conf/access.conf\",\"usr/local/apache22/conf/httpd.conf\",\"usr/local/apache22/httpd.conf\",\"usr/local/etc/apache22/conf/httpd.conf\",\"usr/local/apps/apache22/conf/httpd.conf\",\"etc/apache22/conf/httpd.conf\",\"etc/apache22/httpd.conf\",\"opt/apache22/conf/httpd.conf\",\"usr/local/etc/apache2/vhosts.conf\",\"usr/local/apache/conf/vhosts.conf\",\"usr/local/apache2/conf/vhosts.conf\",\"usr/local/apache/conf/vhosts-custom.conf\",\"usr/local/apache2/conf/vhosts-custom.conf\",\"etc/apache/default-server.conf\",\"etc/apache2/default-server.conf\",\"usr/local/apache2/conf/extra/httpd-ssl.conf\",\"usr/local/apache2/conf/ssl.conf\",\"etc/httpd/conf.d\",\"usr/local/etc/apache22/httpd.conf\",\"usr/local/etc/apache2/httpd.conf\",\"etc/apache2/httpd2.conf\",\"etc/apache2/ssl-global.conf\",\"etc/apache2/vhosts.d/00_default_vhost.conf\",\"apache/conf/httpd.conf\",\"etc/apache/httpd.conf\",\"etc/httpd/conf\",\"http/httpd.conf\",\"usr/local/apache1.3/conf/httpd.conf\",\"usr/local/etc/httpd/conf\",\"var/apache/conf/httpd.conf\",\"var/www/conf\",\"www/apache/conf/httpd.conf\",\"www/conf/httpd.conf\",\"etc/init.d\",\"etc/apache/access.conf\",\"etc/rc.conf\",\"www/logs/freebsddiary-error.log\",\"www/logs/freebsddiary-access_log\",\"library/webserver/documents/index.html\",\"library/webserver/documents/index.htm\",\"library/webserver/documents/default.html\",\"library/webserver/documents/default.htm\",\"library/webserver/documents/index.php\",\"library/webserver/documents/default.php\",\"var/log/webmin/miniserv.log\",\"usr/local/etc/webmin/miniserv.conf\",\"etc/webmin/miniserv.conf\",\"usr/local/etc/webmin/miniserv.users\",\"etc/webmin/miniserv.users\",\"winnt/system32/logfiles/w3svc/inetsvn1.log\",\"winnt/system32/logfiles/w3svc1/inetsvn1.log\",\"winnt/system32/logfiles/w3svc2/inetsvn1.log\",\"winnt/system32/logfiles/w3svc3/inetsvn1.log\",\"windows/system32/logfiles/w3svc/inetsvn1.log\",\"windows/system32/logfiles/w3svc1/inetsvn1.log\",\"windows/system32/logfiles/w3svc2/inetsvn1.log\",\"windows/system32/logfiles/w3svc3/inetsvn1.log\",\"var/log/httpd/access_log\",\"var/log/httpd/error_log\",\"apache/logs/error.log\",\"apache/logs/access.log\",\"apache2/logs/error.log\",\"apache2/logs/access.log\",\"logs/error.log\",\"logs/access.log\",\"etc/httpd/logs/access_log\",\"etc/httpd/logs/access.log\",\"etc/httpd/logs/error_log\",\"etc/httpd/logs/error.log\",\"usr/local/apache/logs/access_log\",\"usr/local/apache/logs/access.log\",\"usr/local/apache/logs/error_log\",\"usr/local/apache/logs/error.log\",\"usr/local/apache2/logs/access_log\",\"usr/local/apache2/logs/access.log\",\"usr/local/apache2/logs/error_log\",\"usr/local/apache2/logs/error.log\",\"var/www/logs/access_log\",\"var/www/logs/access.log\",\"var/www/logs/error_log\",\"var/www/logs/error.log\",\"var/log/httpd/access.log\",\"var/log/httpd/error.log\",\"var/log/apache/access_log\",\"var/log/apache/access.log\",\"var/log/apache/error_log\",\"var/log/apache/error.log\",\"var/log/apache2/access_log\",\"var/log/apache2/access.log\",\"var/log/apache2/error_log\",\"var/log/apache2/error.log\",\"var/log/access_log\",\"var/log/access.log\",\"var/log/error_log\",\"var/log/error.log\",\"opt/lampp/logs/access_log\",\"opt/lampp/logs/error_log\",\"opt/xampp/logs/access_log\",\"opt/xampp/logs/error_log\",\"opt/lampp/logs/access.log\",\"opt/lampp/logs/error.log\",\"opt/xampp/logs/access.log\",\"opt/xampp/logs/error.log\",\"program files/apache group/apache/logs/access.log\",\"program files/apache group/apache/logs/error.log\",\"program files/apache software foundation/apache2.2/logs/error.log\",\"program files/apache software foundation/apache2.2/logs/access.log\",\"opt/apache/apache.conf\",\"opt/apache/conf/apache.conf\",\"opt/apache2/apache.conf\",\"opt/apache2/conf/apache.conf\",\"opt/httpd/apache.conf\",\"opt/httpd/conf/apache.conf\",\"etc/httpd/apache.conf\",\"etc/apache2/apache.conf\",\"etc/httpd/conf/apache.conf\",\"usr/local/apache/apache.conf\",\"usr/local/apache/conf/apache.conf\",\"usr/local/apache2/apache.conf\",\"usr/local/apache2/conf/apache.conf\",\"usr/local/php/apache.conf.php\",\"usr/local/php4/apache.conf.php\",\"usr/local/php5/apache.conf.php\",\"usr/local/php/apache.conf\",\"usr/local/php4/apache.conf\",\"usr/local/php5/apache.conf\",\"private/etc/httpd/apache.conf\",\"opt/apache/apache2.conf\",\"opt/apache/conf/apache2.conf\",\"opt/apache2/apache2.conf\",\"opt/apache2/conf/apache2.conf\",\"opt/httpd/apache2.conf\",\"opt/httpd/conf/apache2.conf\",\"etc/httpd/apache2.conf\",\"etc/httpd/conf/apache2.conf\",\"usr/local/apache/apache2.conf\",\"usr/local/apache/conf/apache2.conf\",\"usr/local/apache2/apache2.conf\",\"usr/local/apache2/conf/apache2.conf\",\"usr/local/php/apache2.conf.php\",\"usr/local/php4/apache2.conf.php\",\"usr/local/php5/apache2.conf.php\",\"usr/local/php/apache2.conf\",\"usr/local/php4/apache2.conf\",\"usr/local/php5/apache2.conf\",\"private/etc/httpd/apache2.conf\",\"usr/local/apache/conf/httpd.conf\",\"usr/local/apache2/conf/httpd.conf\",\"etc/httpd/conf/httpd.conf\",\"etc/apache/apache.conf\",\"etc/apache/conf/httpd.conf\",\"etc/apache2/httpd.conf\",\"usr/apache2/conf/httpd.conf\",\"usr/apache/conf/httpd.conf\",\"usr/local/etc/apache/conf/httpd.conf\",\"usr/local/apache/httpd.conf\",\"usr/local/apache2/httpd.conf\",\"usr/local/httpd/conf/httpd.conf\",\"usr/local/etc/apache2/conf/httpd.conf\",\"usr/local/etc/httpd/conf/httpd.conf\",\"usr/local/apps/apache2/conf/httpd.conf\",\"usr/local/apps/apache/conf/httpd.conf\",\"usr/local/php/httpd.conf.php\",\"usr/local/php4/httpd.conf.php\",\"usr/local/php5/httpd.conf.php\",\"usr/local/php/httpd.conf\",\"usr/local/php4/httpd.conf\",\"usr/local/php5/httpd.conf\",\"etc/apache2/conf/httpd.conf\",\"etc/http/conf/httpd.conf\",\"etc/httpd/httpd.conf\",\"etc/http/httpd.conf\",\"etc/httpd.conf\",\"opt/apache/conf/httpd.conf\",\"opt/apache2/conf/httpd.conf\",\"var/www/conf/httpd.conf\",\"private/etc/httpd/httpd.conf\",\"private/etc/httpd/httpd.conf.default\",\"etc/apache2/vhosts.d/default_vhost.include\",\"etc/apache2/conf.d/charset\",\"etc/apache2/conf.d/security\",\"etc/apache2/envvars\",\"etc/apache2/mods-available/autoindex.conf\",\"etc/apache2/mods-available/deflate.conf\",\"etc/apache2/mods-available/dir.conf\",\"etc/apache2/mods-available/mem_cache.conf\",\"etc/apache2/mods-available/mime.conf\",\"etc/apache2/mods-available/proxy.conf\",\"etc/apache2/mods-available/setenvif.conf\",\"etc/apache2/mods-available/ssl.conf\",\"etc/apache2/mods-enabled/alias.conf\",\"etc/apache2/mods-enabled/deflate.conf\",\"etc/apache2/mods-enabled/dir.conf\",\"etc/apache2/mods-enabled/mime.conf\",\"etc/apache2/mods-enabled/negotiation.conf\",\"etc/apache2/mods-enabled/php5.conf\",\"etc/apache2/mods-enabled/status.conf\",\"program files/apache group/apache/conf/httpd.conf\",\"program files/apache group/apache2/conf/httpd.conf\",\"program files/xampp/apache/conf/apache.conf\",\"program files/xampp/apache/conf/apache2.conf\",\"program files/xampp/apache/conf/httpd.conf\",\"program files/apache group/apache/apache.conf\",\"program files/apache group/apache/conf/apache.conf\",\"program files/apache group/apache2/conf/apache.conf\",\"program files/apache group/apache/apache2.conf\",\"program files/apache group/apache/conf/apache2.conf\",\"program files/apache group/apache2/conf/apache2.conf\",\"program files/apache software foundation/apache2.2/conf/httpd.conf\",\"volumes/macintosh_hd1/opt/httpd/conf/httpd.conf\",\"volumes/macintosh_hd1/opt/apache/conf/httpd.conf\",\"volumes/macintosh_hd1/opt/apache2/conf/httpd.conf\",\"volumes/macintosh_hd1/usr/local/php/httpd.conf.php\",\"volumes/macintosh_hd1/usr/local/php4/httpd.conf.php\",\"volumes/macintosh_hd1/usr/local/php5/httpd.conf.php\",\"volumes/webbackup/opt/apache2/conf/httpd.conf\",\"volumes/webbackup/private/etc/httpd/httpd.conf\",\"volumes/webbackup/private/etc/httpd/httpd.conf.default\",\"usr/local/etc/apache/vhosts.conf\",\"usr/local/jakarta/tomcat/conf/jakarta.conf\",\"usr/local/jakarta/tomcat/conf/server.xml\",\"usr/local/jakarta/tomcat/conf/context.xml\",\"usr/local/jakarta/tomcat/conf/workers.properties\",\"usr/local/jakarta/tomcat/conf/logging.properties\",\"usr/local/jakarta/dist/tomcat/conf/jakarta.conf\",\"usr/local/jakarta/dist/tomcat/conf/server.xml\",\"usr/local/jakarta/dist/tomcat/conf/context.xml\",\"usr/local/jakarta/dist/tomcat/conf/workers.properties\",\"usr/local/jakarta/dist/tomcat/conf/logging.properties\",\"usr/share/tomcat6/conf/server.xml\",\"usr/share/tomcat6/conf/context.xml\",\"usr/share/tomcat6/conf/workers.properties\",\"usr/share/tomcat6/conf/logging.properties\",\"var/log/tomcat6/catalina.out\",\"var/cpanel/tomcat.options\",\"usr/local/jakarta/tomcat/logs/catalina.out\",\"usr/local/jakarta/tomcat/logs/catalina.err\",\"opt/tomcat/logs/catalina.out\",\"opt/tomcat/logs/catalina.err\",\"usr/share/logs/catalina.out\",\"usr/share/logs/catalina.err\",\"usr/share/tomcat/logs/catalina.out\",\"usr/share/tomcat/logs/catalina.err\",\"usr/share/tomcat6/logs/catalina.out\",\"usr/share/tomcat6/logs/catalina.err\",\"usr/local/apache/logs/mod_jk.log\",\"usr/local/jakarta/tomcat/logs/mod_jk.log\",\"usr/local/jakarta/dist/tomcat/logs/mod_jk.log\",\"opt/[jboss]/server/default/conf/jboss-minimal.xml\",\"opt/[jboss]/server/default/conf/jboss-service.xml\",\"opt/[jboss]/server/default/conf/jndi.properties\",\"opt/[jboss]/server/default/conf/log4j.xml\",\"opt/[jboss]/server/default/conf/login-config.xml\",\"opt/[jboss]/server/default/conf/standardjaws.xml\",\"opt/[jboss]/server/default/conf/standardjboss.xml\",\"opt/[jboss]/server/default/conf/server.log.properties\",\"opt/[jboss]/server/default/deploy/jboss-logging.xml\",\"usr/local/[jboss]/server/default/conf/jboss-minimal.xml\",\"usr/local/[jboss]/server/default/conf/jboss-service.xml\",\"usr/local/[jboss]/server/default/conf/jndi.properties\",\"usr/local/[jboss]/server/default/conf/log4j.xml\",\"usr/local/[jboss]/server/default/conf/login-config.xml\",\"usr/local/[jboss]/server/default/conf/standardjaws.xml\",\"usr/local/[jboss]/server/default/conf/standardjboss.xml\",\"usr/local/[jboss]/server/default/conf/server.log.properties\",\"usr/local/[jboss]/server/default/deploy/jboss-logging.xml\",\"private/tmp/[jboss]/server/default/conf/jboss-minimal.xml\",\"private/tmp/[jboss]/server/default/conf/jboss-service.xml\",\"private/tmp/[jboss]/server/default/conf/jndi.properties\",\"private/tmp/[jboss]/server/default/conf/log4j.xml\",\"private/tmp/[jboss]/server/default/conf/login-config.xml\",\"private/tmp/[jboss]/server/default/conf/standardjaws.xml\",\"private/tmp/[jboss]/server/default/conf/standardjboss.xml\",\"private/tmp/[jboss]/server/default/conf/server.log.properties\",\"private/tmp/[jboss]/server/default/deploy/jboss-logging.xml\",\"tmp/[jboss]/server/default/conf/jboss-minimal.xml\",\"tmp/[jboss]/server/default/conf/jboss-service.xml\",\"tmp/[jboss]/server/default/conf/jndi.properties\",\"tmp/[jboss]/server/default/conf/log4j.xml\",\"tmp/[jboss]/server/default/conf/login-config.xml\",\"tmp/[jboss]/server/default/conf/standardjaws.xml\",\"tmp/[jboss]/server/default/conf/standardjboss.xml\",\"tmp/[jboss]/server/default/conf/server.log.properties\",\"tmp/[jboss]/server/default/deploy/jboss-logging.xml\",\"program files/[jboss]/server/default/conf/jboss-minimal.xml\",\"program files/[jboss]/server/default/conf/jboss-service.xml\",\"program files/[jboss]/server/default/conf/jndi.properties\",\"program files/[jboss]/server/default/conf/log4j.xml\",\"program files/[jboss]/server/default/conf/login-config.xml\",\"program files/[jboss]/server/default/conf/standardjaws.xml\",\"program files/[jboss]/server/default/conf/standardjboss.xml\",\"program files/[jboss]/server/default/conf/server.log.properties\",\"program files/[jboss]/server/default/deploy/jboss-logging.xml\",\"[jboss]/server/default/conf/jboss-minimal.xml\",\"[jboss]/server/default/conf/jboss-service.xml\",\"[jboss]/server/default/conf/jndi.properties\",\"[jboss]/server/default/conf/log4j.xml\",\"[jboss]/server/default/conf/login-config.xml\",\"[jboss]/server/default/conf/standardjaws.xml\",\"[jboss]/server/default/conf/standardjboss.xml\",\"[jboss]/server/default/conf/server.log.properties\",\"[jboss]/server/default/deploy/jboss-logging.xml\",\"opt/[jboss]/server/default/log/server.log\",\"opt/[jboss]/server/default/log/boot.log\",\"usr/local/[jboss]/server/default/log/server.log\",\"usr/local/[jboss]/server/default/log/boot.log\",\"private/tmp/[jboss]/server/default/log/server.log\",\"private/tmp/[jboss]/server/default/log/boot.log\",\"tmp/[jboss]/server/default/log/server.log\",\"tmp/[jboss]/server/default/log/boot.log\",\"program files/[jboss]/server/default/log/server.log\",\"program files/[jboss]/server/default/log/boot.log\",\"[jboss]/server/default/log/server.log\",\"[jboss]/server/default/log/boot.log\",\"var/log/lighttpd.error.log\",\"var/log/lighttpd.access.log\",\"var/lighttpd.log\",\"var/logs/access.log\",\"var/log/lighttpd/\",\"var/log/lighttpd/error.log\",\"var/log/lighttpd/access.www.log\",\"var/log/lighttpd/error.www.log\",\"var/log/lighttpd/access.log\",\"usr/local/apache2/logs/lighttpd.error.log\",\"usr/local/apache2/logs/lighttpd.log\",\"usr/local/apache/logs/lighttpd.error.log\",\"usr/local/apache/logs/lighttpd.log\",\"usr/local/lighttpd/log/lighttpd.error.log\",\"usr/local/lighttpd/log/access.log\",\"var/log/lighttpd/{domain}/access.log\",\"var/log/lighttpd/{domain}/error.log\",\"usr/home/user/var/log/lighttpd.error.log\",\"usr/home/user/var/log/apache.log\",\"home/user/lighttpd/lighttpd.conf\",\"usr/home/user/lighttpd/lighttpd.conf\",\"etc/lighttpd/lighthttpd.conf\",\"usr/local/etc/lighttpd.conf\",\"usr/local/lighttpd/conf/lighttpd.conf\",\"usr/local/etc/lighttpd.conf.new\",\"var/www/.lighttpdpassword\",\"var/log/nginx/access_log\",\"var/log/nginx/error_log\",\"var/log/nginx/access.log\",\"var/log/nginx/error.log\",\"var/log/nginx.access_log\",\"var/log/nginx.error_log\",\"logs/access_log\",\"logs/error_log\",\"etc/nginx/nginx.conf\",\"usr/local/etc/nginx/nginx.conf\",\"usr/local/nginx/conf/nginx.conf\",\"usr/local/zeus/web/global.cfg\",\"usr/local/zeus/web/log/errors\",\"opt/lsws/conf/httpd_conf.xml\",\"usr/local/lsws/conf/httpd_conf.xml\",\"opt/lsws/logs/error.log\",\"opt/lsws/logs/access.log\",\"usr/local/lsws/logs/error.log\",\"usr/local/logs/access.log\",\"usr/local/samba/lib/log.user\",\"usr/local/logs/samba.log\",\"var/log/samba/log.smbd\",\"var/log/samba/log.nmbd\",\"var/log/samba.log\",\"var/log/samba.log1\",\"var/log/samba.log2\",\"var/log/log.smb\",\"etc/samba/netlogon\",\"etc/smbpasswd\",\"etc/smb.conf\",\"etc/samba/dhcp.conf\",\"etc/samba/smb.conf\",\"etc/samba/samba.conf\",\"etc/samba/smb.conf.user\",\"etc/samba/smbpasswd\",\"etc/samba/smbusers\",\"etc/samba/private/smbpasswd\",\"usr/local/etc/smb.conf\",\"usr/local/samba/lib/smb.conf.user\",\"etc/dhcp3/dhclient.conf\",\"etc/dhcp3/dhcpd.conf\",\"etc/dhcp/dhclient.conf\",\"program files/vidalia bundle/polipo/polipo.conf\",\"etc/tor/tor-tsocks.conf\",\"etc/stunnel/stunnel.conf\",\"etc/tsocks.conf\",\"etc/tinyproxy/tinyproxy.conf\",\"etc/miredo-server.conf\",\"etc/miredo.conf\",\"etc/miredo/miredo-server.conf\",\"etc/miredo/miredo.conf\",\"etc/wicd/dhclient.conf.template.default\",\"etc/wicd/manager-settings.conf\",\"etc/wicd/wired-settings.conf\",\"etc/wicd/wireless-settings.conf\",\"var/log/ipfw.log\",\"var/log/ipfw\",\"var/log/ipfw/ipfw.log\",\"var/log/ipfw.today\",\"etc/ipfw.rules\",\"etc/ipfw.conf\",\"etc/firewall.rules\",\"winnt/system32/logfiles/firewall/pfirewall.log\",\"winnt/system32/logfiles/firewall/pfirewall.log.old\",\"windows/system32/logfiles/firewall/pfirewall.log\",\"windows/system32/logfiles/firewall/pfirewall.log.old\",\"etc/clamav/clamd.conf\",\"etc/clamav/freshclam.conf\",\"etc/x11/xorg.conf\",\"etc/x11/xorg.conf-vesa\",\"etc/x11/xorg.conf-vmware\",\"etc/x11/xorg.conf.beforevmwaretoolsinstall\",\"etc/x11/xorg.conf.orig\",\"etc/bluetooth/input.conf\",\"etc/bluetooth/main.conf\",\"etc/bluetooth/network.conf\",\"etc/bluetooth/rfcomm.conf\",\"proc/self/environ\",\"proc/self/mounts\",\"proc/self/stat\",\"proc/self/status\",\"proc/self/cmdline\",\"proc/self/fd/0\",\"proc/self/fd/1\",\"proc/self/fd/2\",\"proc/self/fd/3\",\"proc/self/fd/4\",\"proc/self/fd/5\",\"proc/self/fd/6\",\"proc/self/fd/7\",\"proc/self/fd/8\",\"proc/self/fd/9\",\"proc/self/fd/10\",\"proc/self/fd/11\",\"proc/self/fd/12\",\"proc/self/fd/13\",\"proc/self/fd/14\",\"proc/self/fd/15\",\"proc/version\",\"proc/devices\",\"proc/cpuinfo\",\"proc/meminfo\",\"proc/net/tcp\",\"proc/net/udp\",\"etc/bash_completion.d/debconf\",\"root/.bash_logout\",\"root/.bash_history\",\"root/.bash_config\",\"root/.bashrc\",\"etc/bash.bashrc\",\"var/adm/syslog\",\"var/adm/sulog\",\"var/adm/utmp\",\"var/adm/utmpx\",\"var/adm/wtmp\",\"var/adm/wtmpx\",\"var/adm/lastlog/username\",\"usr/spool/lp/log\",\"var/adm/lp/lpd-errs\",\"usr/lib/cron/log\",\"var/adm/loginlog\",\"var/adm/pacct\",\"var/adm/dtmp\",\"var/adm/acct/sum/loginlog\",\"var/adm/x0msgs\",\"var/adm/crash/vmcore\",\"var/adm/crash/unix\",\"etc/newsyslog.conf\",\"var/adm/qacct\",\"var/adm/ras/errlog\",\"var/adm/ras/bootlog\",\"var/adm/cron/log\",\"etc/utmp\",\"etc/security/lastlog\",\"etc/security/failedlogin\",\"usr/spool/mqueue/syslog\",\"var/adm/messages\",\"var/adm/aculogs\",\"var/adm/aculog\",\"var/adm/vold.log\",\"var/adm/log/asppp.log\",\"var/log/poplog\",\"var/log/authlog\",\"var/lp/logs/lpsched\",\"var/lp/logs/lpnet\",\"var/lp/logs/requests\",\"var/cron/log\",\"var/saf/_log\",\"var/saf/port/log\",\"var/log/news.all\",\"var/log/news/news.all\",\"var/log/news/news.crit\",\"var/log/news/news.err\",\"var/log/news/news.notice\",\"var/log/news/suck.err\",\"var/log/news/suck.notice\",\"var/log/messages\",\"var/log/messages.1\",\"var/log/user.log\",\"var/log/user.log.1\",\"var/log/auth.log\",\"var/log/pm-powersave.log\",\"var/log/xorg.0.log\",\"var/log/daemon.log\",\"var/log/daemon.log.1\",\"var/log/kern.log\",\"var/log/kern.log.1\",\"var/log/mail.err\",\"var/log/mail.info\",\"var/log/mail.warn\",\"var/log/ufw.log\",\"var/log/boot.log\",\"var/log/syslog\",\"var/log/syslog.1\",\"tmp/access.log\",\"etc/sensors.conf\",\"etc/sensors3.conf\",\"etc/host.conf\",\"etc/pam.conf\",\"etc/resolv.conf\",\"etc/apt/apt.conf\",\"etc/inetd.conf\",\"etc/syslog.conf\",\"etc/sysctl.conf\",\"etc/sysctl.d/10-console-messages.conf\",\"etc/sysctl.d/10-network-security.conf\",\"etc/sysctl.d/10-process-security.conf\",\"etc/sysctl.d/wine.sysctl.conf\",\"etc/security/access.conf\",\"etc/security/group.conf\",\"etc/security/limits.conf\",\"etc/security/namespace.conf\",\"etc/security/pam_env.conf\",\"etc/security/sepermit.conf\",\"etc/security/time.conf\",\"etc/ssh/sshd_config\",\"etc/adduser.conf\",\"etc/deluser.conf\",\"etc/avahi/avahi-daemon.conf\",\"etc/ca-certificates.conf\",\"etc/ca-certificates.conf.dpkg-old\",\"etc/casper.conf\",\"etc/chkrootkit.conf\",\"etc/debconf.conf\",\"etc/dns2tcpd.conf\",\"etc/e2fsck.conf\",\"etc/esound/esd.conf\",\"etc/etter.conf\",\"etc/fuse.conf\",\"etc/foremost.conf\",\"etc/hdparm.conf\",\"etc/kernel-img.conf\",\"etc/kernel-pkg.conf\",\"etc/ld.so.conf\",\"etc/ltrace.conf\",\"etc/mail/sendmail.conf\",\"etc/manpath.config\",\"etc/kbd/config\",\"etc/ldap/ldap.conf\",\"etc/logrotate.conf\",\"etc/mtools.conf\",\"etc/smi.conf\",\"etc/updatedb.conf\",\"etc/pulse/client.conf\",\"usr/share/adduser/adduser.conf\",\"etc/hostname\",\"etc/networks\",\"etc/timezone\",\"etc/modules\",\"etc/passwd\",\"etc/passwd~\",\"etc/passwd-\",\"etc/shadow\",\"etc/shadow~\",\"etc/shadow-\",\"etc/fstab\",\"etc/motd\",\"etc/hosts\",\"etc/group\",\"etc/group-\",\"etc/alias\",\"etc/crontab\",\"etc/crypttab\",\"etc/exports\",\"etc/mtab\",\"etc/hosts.allow\",\"etc/hosts.deny\",\"etc/os-release\",\"etc/password.master\",\"etc/profile\",\"etc/default/grub\",\"etc/resolvconf/update-libc.d/sendmail\",\"etc/inittab\",\"etc/issue\",\"etc/issue.net\",\"etc/login.defs\",\"etc/sudoers\",\"etc/sysconfig/network-scripts/ifcfg-eth0\",\"etc/redhat-release\",\"etc/debian_version\",\"etc/fedora-release\",\"etc/mandrake-release\",\"etc/slackware-release\",\"etc/suse-release\",\"etc/security/group\",\"etc/security/passwd\",\"etc/security/user\",\"etc/security/environ\",\"etc/security/limits\",\"etc/security/opasswd\",\"boot/grub/grub.cfg\",\"boot/grub/menu.lst\",\"root/.ksh_history\",\"root/.xauthority\",\"usr/lib/security/mkuser.default\",\"var/log/squirrelmail.log\",\"var/log/apache2/squirrelmail.log\",\"var/log/apache2/squirrelmail.err.log\",\"var/lib/squirrelmail/prefs/squirrelmail.log\",\"var/log/mail.log\",\"etc/squirrelmail/apache.conf\",\"etc/squirrelmail/config_local.php\",\"etc/squirrelmail/default_pref\",\"etc/squirrelmail/index.php\",\"etc/squirrelmail/config_default.php\",\"etc/squirrelmail/config.php\",\"etc/squirrelmail/filters_setup.php\",\"etc/squirrelmail/sqspell_config.php\",\"etc/squirrelmail/config/config.php\",\"etc/httpd/conf.d/squirrelmail.conf\",\"usr/share/squirrelmail/config/config.php\",\"private/etc/squirrelmail/config/config.php\",\"srv/www/htdos/squirrelmail/config/config.php\",\"var/www/squirrelmail/config/config.php\",\"var/www/html/squirrelmail/config/config.php\",\"var/www/html/squirrelmail-1.2.9/config/config.php\",\"usr/share/squirrelmail/plugins/squirrel_logger/setup.php\",\"usr/local/squirrelmail/www/readme\",\"windows/system32/drivers/etc/hosts\",\"windows/system32/drivers/etc/lmhosts.sam\",\"windows/system32/drivers/etc/networks\",\"windows/system32/drivers/etc/protocol\",\"windows/system32/drivers/etc/services\",\"/boot.ini\",\"windows/debug/netsetup.log\",\"windows/comsetup.log\",\"windows/repair/setup.log\",\"windows/setupact.log\",\"windows/setupapi.log\",\"windows/setuperr.log\",\"windows/updspapi.log\",\"windows/wmsetup.log\",\"windows/windowsupdate.log\",\"windows/odbc.ini\",\"usr/local/psa/admin/htdocs/domains/databases/phpmyadmin/libraries/config.default.php\",\"etc/apache2/conf.d/phpmyadmin.conf\",\"etc/phpmyadmin/config.inc.php\",\"etc/openldap/ldap.conf\",\"etc/cups/acroread.conf\",\"etc/cups/cupsd.conf\",\"etc/cups/cupsd.conf.default\",\"etc/cups/pdftops.conf\",\"etc/cups/printers.conf\",\"windows/system32/macromed/flash/flashinstall.log\",\"windows/system32/macromed/flash/install.log\",\"etc/cvs-cron.conf\",\"etc/cvs-pserver.conf\",\"etc/subversion/config\",\"etc/modprobe.d/vmware-tools.conf\",\"etc/updatedb.conf.beforevmwaretoolsinstall\",\"etc/vmware-tools/config\",\"etc/vmware-tools/tpvmlp.conf\",\"etc/vmware-tools/vmware-tools-libraries.conf\",\"var/log/vmware/hostd.log\",\"var/log/vmware/hostd-1.log\",\"/wp-config.php\",\"/wp-config.bak\",\"/wp-config.old\",\"/wp-config.temp\",\"/wp-config.tmp\",\"/wp-config.txt\",\"/config.yml\",\"/config_dev.yml\",\"/config_prod.yml\",\"/config_test.yml\",\"/parameters.yml\",\"/routing.yml\",\"/security.yml\",\"/services.yml\",\"sites/default/default.settings.php\",\"sites/default/settings.php\",\"sites/default/settings.local.php\",\"app/etc/local.xml\",\"/sftp-config.json\",\"/web.config\",\"includes/config.php\",\"includes/configure.php\",\"config.inc.php\",\"localsettings.php\",\"inc/config.php\",\"typo3conf/localconf.php\",\"config/app.php\",\"config/custom.php\",\"config/database.php\",\"/configuration.php\",\"/config.php\",\"var/mail/www-data\",\"etc/network/\",\"etc/init/\",\"inetpub/wwwroot/global.asa\",\"system32/inetsrv/config/applicationhost.config\",\"system32/inetsrv/config/administration.config\",\"system32/inetsrv/config/redirection.config\",\"system32/config/default\",\"system32/config/sam\",\"system32/config/system\",\"system32/config/software\",\"winnt/repair/sam._\",\"/package.json\",\"/package-lock.json\",\"/gruntfile.js\",\"/npm-debug.log\",\"/ormconfig.json\",\"/tsconfig.json\",\"/webpack.config.js\",\"/yarn.lock\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\",\"normalizePath\"]},{\"id\":\"crs-931-110\",\"name\":\"RFI: Common RFI Vulnerable Parameter Name used w/ URL Payload\",\"tags\":{\"type\":\"rfi\",\"crs_id\":\"931110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"}],\"regex\":\"(?:\\\\binclude\\\\s*\\\\([^)]*|mosConfig_absolute_path|_CONF\\\\[path\\\\]|_SERVER\\\\[DOCUMENT_ROOT\\\\]|GALLERY_BASEDIR|path\\\\[docroot\\\\]|appserv_root|config\\\\[root_dir\\\\])=(?:file|ftps?|https?)://\",\"options\":{\"min_length\":15}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-931-120\",\"name\":\"RFI: URL Payload Used w/Trailing Question Mark Character (?)\",\"tags\":{\"type\":\"rfi\",\"crs_id\":\"931120\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"}],\"regex\":\"^(?i:file|ftps?|https?).*?\\\\?+$\",\"options\":{\"case_sensitive\":true,\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-932-160\",\"name\":\"Remote Command Execution: Unix Shell Code Found\",\"tags\":{\"type\":\"command_injection\",\"crs_id\":\"932160\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"${cdpath}\",\"${dirstack}\",\"${home}\",\"${hostname}\",\"${ifs}\",\"${oldpwd}\",\"${ostype}\",\"${path}\",\"${pwd}\",\"$cdpath\",\"$dirstack\",\"$home\",\"$hostname\",\"$ifs\",\"$oldpwd\",\"$ostype\",\"$path\",\"$pwd\",\"bin/bash\",\"bin/cat\",\"bin/csh\",\"bin/dash\",\"bin/du\",\"bin/echo\",\"bin/grep\",\"bin/less\",\"bin/ls\",\"bin/mknod\",\"bin/more\",\"bin/nc\",\"bin/ps\",\"bin/rbash\",\"bin/sh\",\"bin/sleep\",\"bin/su\",\"bin/tcsh\",\"bin/uname\",\"dev/fd/\",\"dev/null\",\"dev/stderr\",\"dev/stdin\",\"dev/stdout\",\"dev/tcp/\",\"dev/udp/\",\"dev/zero\",\"etc/group\",\"etc/master.passwd\",\"etc/passwd\",\"etc/pwd.db\",\"etc/shadow\",\"etc/shells\",\"etc/spwd.db\",\"proc/self/\",\"usr/bin/awk\",\"usr/bin/base64\",\"usr/bin/cat\",\"usr/bin/cc\",\"usr/bin/clang\",\"usr/bin/clang++\",\"usr/bin/curl\",\"usr/bin/diff\",\"usr/bin/env\",\"usr/bin/fetch\",\"usr/bin/file\",\"usr/bin/find\",\"usr/bin/ftp\",\"usr/bin/gawk\",\"usr/bin/gcc\",\"usr/bin/head\",\"usr/bin/hexdump\",\"usr/bin/id\",\"usr/bin/less\",\"usr/bin/ln\",\"usr/bin/mkfifo\",\"usr/bin/more\",\"usr/bin/nc\",\"usr/bin/ncat\",\"usr/bin/nice\",\"usr/bin/nmap\",\"usr/bin/perl\",\"usr/bin/php\",\"usr/bin/php5\",\"usr/bin/php7\",\"usr/bin/php-cgi\",\"usr/bin/printf\",\"usr/bin/psed\",\"usr/bin/python\",\"usr/bin/python2\",\"usr/bin/python3\",\"usr/bin/ruby\",\"usr/bin/sed\",\"usr/bin/socat\",\"usr/bin/tail\",\"usr/bin/tee\",\"usr/bin/telnet\",\"usr/bin/top\",\"usr/bin/uname\",\"usr/bin/wget\",\"usr/bin/who\",\"usr/bin/whoami\",\"usr/bin/xargs\",\"usr/bin/xxd\",\"usr/bin/yes\",\"usr/local/bin/bash\",\"usr/local/bin/curl\",\"usr/local/bin/ncat\",\"usr/local/bin/nmap\",\"usr/local/bin/perl\",\"usr/local/bin/php\",\"usr/local/bin/python\",\"usr/local/bin/python2\",\"usr/local/bin/python3\",\"usr/local/bin/rbash\",\"usr/local/bin/ruby\",\"usr/local/bin/wget\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-932-171\",\"name\":\"Remote Command Execution: Shellshock (CVE-2014-6271)\",\"tags\":{\"type\":\"command_injection\",\"crs_id\":\"932171\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"^\\\\(\\\\s*\\\\)\\\\s+{\",\"options\":{\"case_sensitive\":true,\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-932-180\",\"name\":\"Restricted File Upload Attempt\",\"tags\":{\"type\":\"command_injection\",\"crs_id\":\"932180\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x-filename\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x_filename\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x-file-name\"]}],\"list\":[\".htaccess\",\".htdigest\",\".htpasswd\",\"wp-config.php\",\"config.yml\",\"config_dev.yml\",\"config_prod.yml\",\"config_test.yml\",\"parameters.yml\",\"routing.yml\",\"security.yml\",\"services.yml\",\"default.settings.php\",\"settings.php\",\"settings.local.php\",\"local.xml\",\".env\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-933-111\",\"name\":\"PHP Injection Attack: PHP Script File Upload Found\",\"tags\":{\"type\":\"unrestricted_file_upload\",\"crs_id\":\"933111\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x-filename\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x_filename\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x.filename\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"x-file-name\"]}],\"regex\":\".*\\\\.(?:php\\\\d*|phtml)\\\\..*$\",\"options\":{\"case_sensitive\":true,\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-933-130\",\"name\":\"PHP Injection Attack: Global Variables Found\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933130\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"$globals\",\"$http_cookie_vars\",\"$http_env_vars\",\"$http_get_vars\",\"$http_post_files\",\"$http_post_vars\",\"$http_raw_post_data\",\"$http_request_vars\",\"$http_server_vars\",\"$_cookie\",\"$_env\",\"$_files\",\"$_get\",\"$_post\",\"$_request\",\"$_server\",\"$_session\",\"$argc\",\"$argv\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-933-131\",\"name\":\"PHP Injection Attack: HTTP Headers Values Found\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933131\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:HTTP_(?:ACCEPT(?:_(?:ENCODING|LANGUAGE|CHARSET))?|(?:X_FORWARDED_FO|REFERE)R|(?:USER_AGEN|HOS)T|CONNECTION|KEEP_ALIVE)|PATH_(?:TRANSLATED|INFO)|ORIG_PATH_INFO|QUERY_STRING|REQUEST_URI|AUTH_TYPE)\",\"options\":{\"case_sensitive\":true,\"min_length\":9}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-933-140\",\"name\":\"PHP Injection Attack: I/O Stream Found\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933140\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"php://(?:std(?:in|out|err)|(?:in|out)put|fd|memory|temp|filter)\",\"options\":{\"min_length\":8}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-933-150\",\"name\":\"PHP Injection Attack: High-Risk PHP Function Name Found\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933150\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"__halt_compiler\",\"apache_child_terminate\",\"base64_decode\",\"bzdecompress\",\"call_user_func\",\"call_user_func_array\",\"call_user_method\",\"call_user_method_array\",\"convert_uudecode\",\"file_get_contents\",\"file_put_contents\",\"fsockopen\",\"get_class_methods\",\"get_class_vars\",\"get_defined_constants\",\"get_defined_functions\",\"get_defined_vars\",\"gzdecode\",\"gzinflate\",\"gzuncompress\",\"include_once\",\"invokeargs\",\"pcntl_exec\",\"pcntl_fork\",\"pfsockopen\",\"posix_getcwd\",\"posix_getpwuid\",\"posix_getuid\",\"posix_uname\",\"reflectionfunction\",\"require_once\",\"shell_exec\",\"str_rot13\",\"sys_get_temp_dir\",\"wp_remote_fopen\",\"wp_remote_get\",\"wp_remote_head\",\"wp_remote_post\",\"wp_remote_request\",\"wp_safe_remote_get\",\"wp_safe_remote_head\",\"wp_safe_remote_post\",\"wp_safe_remote_request\",\"zlib_decode\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-933-160\",\"name\":\"PHP Injection Attack: High-Risk PHP Function Call Found\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933160\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"\\\\b(?:s(?:e(?:t(?:_(?:e(?:xception|rror)_handler|magic_quotes_runtime|include_path)|defaultstub)|ssion_s(?:et_save_handler|tart))|qlite_(?:(?:(?:unbuffered|single|array)_)?query|create_(?:aggregate|function)|p?open|exec)|tr(?:eam_(?:context_create|socket_client)|ipc?slashes|rev)|implexml_load_(?:string|file)|ocket_c(?:onnect|reate)|h(?:ow_sourc|a1_fil)e|pl_autoload_register|ystem)|p(?:r(?:eg_(?:replace(?:_callback(?:_array)?)?|match(?:_all)?|split)|oc_(?:(?:terminat|clos|nic)e|get_status|open)|int_r)|o(?:six_(?:get(?:(?:e[gu]|g)id|login|pwnam)|mk(?:fifo|nod)|ttyname|kill)|pen)|hp(?:_(?:strip_whitespac|unam)e|version|info)|g_(?:(?:execut|prepar)e|connect|query)|a(?:rse_(?:ini_file|str)|ssthru)|utenv)|r(?:unkit_(?:function_(?:re(?:defin|nam)e|copy|add)|method_(?:re(?:defin|nam)e|copy|add)|constant_(?:redefine|add))|e(?:(?:gister_(?:shutdown|tick)|name)_function|ad(?:(?:gz)?file|_exif_data|dir))|awurl(?:de|en)code)|i(?:mage(?:createfrom(?:(?:jpe|pn)g|x[bp]m|wbmp|gif)|(?:jpe|pn)g|g(?:d2?|if)|2?wbmp|xbm)|s_(?:(?:(?:execut|write?|read)ab|fi)le|dir)|ni_(?:get(?:_all)?|set)|terator_apply|ptcembed)|g(?:et(?:_(?:c(?:urrent_use|fg_va)r|meta_tags)|my(?:[gpu]id|inode)|(?:lastmo|cw)d|imagesize|env)|z(?:(?:(?:defla|wri)t|encod|fil)e|compress|open|read)|lob)|a(?:rray_(?:u(?:intersect(?:_u?assoc)?|diff(?:_u?assoc)?)|intersect_u(?:assoc|key)|diff_u(?:assoc|key)|filter|reduce|map)|ssert(?:_options)?)|h(?:tml(?:specialchars(?:_decode)?|_entity_decode|entities)|(?:ash(?:_(?:update|hmac))?|ighlight)_file|e(?:ader_register_callback|x2bin))|f(?:i(?:le(?:(?:[acm]tim|inod)e|(?:_exist|perm)s|group)?|nfo_open)|tp_(?:nb_(?:ge|pu)|connec|ge|pu)t|(?:unction_exis|pu)ts|write|open)|o(?:b_(?:get_(?:c(?:ontents|lean)|flush)|end_(?:clean|flush)|clean|flush|start)|dbc_(?:result(?:_all)?|exec(?:ute)?|connect)|pendir)|m(?:b_(?:ereg(?:_(?:replace(?:_callback)?|match)|i(?:_replace)?)?|parse_str)|(?:ove_uploaded|d5)_file|ethod_exists|ysql_query|kdir)|e(?:x(?:if_(?:t(?:humbnail|agname)|imagetype|read_data)|ec)|scapeshell(?:arg|cmd)|rror_reporting|val)|c(?:url_(?:file_create|exec|init)|onvert_uuencode|reate_function|hr)|u(?:n(?:serialize|pack)|rl(?:de|en)code|[ak]?sort)|(?:json_(?:de|en)cod|debug_backtrac|tmpfil)e|b(?:(?:son_(?:de|en)|ase64_en)code|zopen)|var_dump)(?:\\\\s|/\\\\*.*\\\\*/|//.*|#.*)*\\\\(.*\\\\)\",\"options\":{\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-933-170\",\"name\":\"PHP Injection Attack: Serialized Object Injection\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933170\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"[oOcC]:\\\\d+:\\\\\\\".+?\\\\\\\":\\\\d+:{[\\\\W\\\\w]*}\",\"options\":{\"case_sensitive\":true,\"min_length\":12}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-933-200\",\"name\":\"PHP Injection Attack: Wrapper scheme detected\",\"tags\":{\"type\":\"php_code_injection\",\"crs_id\":\"933200\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i:zlib|glob|phar|ssh2|rar|ogg|expect|zip)://\",\"options\":{\"case_sensitive\":true,\"min_length\":6}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-934-100\",\"name\":\"Node.js Injection Attack\",\"tags\":{\"type\":\"js_code_injection\",\"crs_id\":\"934100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:(?:_(?:\\\\$\\\\$ND_FUNC\\\\$\\\\$_|_js_function)|(?:new\\\\s+Function|\\\\beval)\\\\s*\\\\(|String\\\\s*\\\\.\\\\s*fromCharCode|function\\\\s*\\\\(\\\\s*\\\\)\\\\s*{|this\\\\.constructor)|module\\\\.exports\\\\s*=)\",\"options\":{\"case_sensitive\":true,\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-941-100\",\"name\":\"XSS Attack Detected via libinjection\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"referer\"]},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}]},\"operator\":\"is_xss\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-941-110\",\"name\":\"XSS Filter - Category 1: Script Tag Vector\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"referer\"]},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"]*>[\\\\s\\\\S]*?\",\"options\":{\"min_length\":8}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-941-120\",\"name\":\"XSS Filter - Category 2: Event Handler Vector\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941120\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"referer\"]},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"[\\\\s\\\\\\\"'`;\\\\/0-9=\\\\x0B\\\\x09\\\\x0C\\\\x3B\\\\x2C\\\\x28\\\\x3B]on[a-zA-Z]{3,25}[\\\\s\\\\x0B\\\\x09\\\\x0C\\\\x3B\\\\x2C\\\\x28\\\\x3B]*?=[^=]\",\"options\":{\"min_length\":8}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-941-140\",\"name\":\"XSS Filter - Category 4: Javascript URI Vector\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941140\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]},{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"referer\"]},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"[a-z]+=(?:[^:=]+:.+;)*?[^:=]+:url\\\\(javascript\",\"options\":{\"min_length\":18}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-941-180\",\"name\":\"Node-Validator Deny List Keywords\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941180\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"document.cookie\",\"document.write\",\".parentnode\",\".innerhtml\",\"window.location\",\"-moz-binding\",\"]\",\"options\":{\"min_length\":8}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-941-300\",\"name\":\"IE XSS Filters - Attack Detected via object tag\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941300\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\")|<.*\\\\+AD4-\",\"options\":{\"case_sensitive\":true,\"min_length\":6}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-941-360\",\"name\":\"JSFuck / Hieroglyphy obfuscation detected\",\"tags\":{\"type\":\"xss\",\"crs_id\":\"941360\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"![!+ ]\\\\[\\\\]\",\"options\":{\"case_sensitive\":true,\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-100\",\"name\":\"SQL Injection Attack Detected via libinjection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}]},\"operator\":\"is_sqli\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"crs-942-160\",\"name\":\"Detects blind sqli tests using sleep() or benchmark()\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942160\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i:sleep\\\\(\\\\s*?\\\\d*?\\\\s*?\\\\)|benchmark\\\\(.*?\\\\,.*?\\\\))\",\"options\":{\"case_sensitive\":true,\"min_length\":7}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-190\",\"name\":\"Detects MSSQL code execution and information gathering attempts\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942190\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:\\\\b(?:(?:c(?:onnection_id|urrent_user)|database)\\\\s*?\\\\([^\\\\)]*?|u(?:nion(?:[\\\\w(?:\\\\s]*?select| select @)|ser\\\\s*?\\\\([^\\\\)]*?)|s(?:chema\\\\s*?\\\\([^\\\\)]*?|elect.*?\\\\w?user\\\\()|into[\\\\s+]+(?:dump|out)file\\\\s*?[\\\\\\\"'`]|from\\\\W+information_schema\\\\W|exec(?:ute)?\\\\s+master\\\\.)|[\\\\\\\"'`](?:;?\\\\s*?(?:union\\\\b\\\\s*?(?:(?:distin|sele)ct|all)|having|select)\\\\b\\\\s*?[^\\\\s]|\\\\s*?!\\\\s*?[\\\\\\\"'`\\\\w])|\\\\s*?exec(?:ute)?.*?\\\\Wxp_cmdshell|\\\\Wiif\\\\s*?\\\\()\",\"options\":{\"min_length\":3}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-240\",\"name\":\"Detects MySQL charset switch and MSSQL DoS attempts\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942240\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:[\\\\\\\"'`](?:;*?\\\\s*?waitfor\\\\s+(?:delay|time)\\\\s+[\\\\\\\"'`]|;.*?:\\\\s*?goto)|alter\\\\s*?\\\\w+.*?cha(?:racte)?r\\\\s+set\\\\s+\\\\w+)\",\"options\":{\"min_length\":7}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-250\",\"name\":\"Detects MATCH AGAINST, MERGE and EXECUTE IMMEDIATE injections\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942250\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i:merge.*?using\\\\s*?\\\\(|execute\\\\s*?immediate\\\\s*?[\\\\\\\"'`]|match\\\\s*?[\\\\w(?:),+-]+\\\\s*?against\\\\s*?\\\\()\",\"options\":{\"case_sensitive\":true,\"min_length\":11}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-270\",\"name\":\"Basic SQL injection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942270\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"union.*?select.*?from\",\"options\":{\"min_length\":15}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-280\",\"name\":\"SQL Injection with delay functions\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942280\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:;\\\\s*?shutdown\\\\s*?(?:[#;{]|\\\\/\\\\*|--)|waitfor\\\\s*?delay\\\\s?[\\\\\\\"'`]+\\\\s?\\\\d|select\\\\s*?pg_sleep)\",\"options\":{\"min_length\":10}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-290\",\"name\":\"Finds basic MongoDB SQL injection attempts\",\"tags\":{\"type\":\"nosql_injection\",\"crs_id\":\"942290\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i:(?:\\\\[\\\\$(?:ne|eq|lte?|gte?|n?in|mod|all|size|exists|type|slice|x?or|div|like|between|and)\\\\]))\",\"options\":{\"case_sensitive\":true,\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[\"keys_only\"]},{\"id\":\"crs-942-360\",\"name\":\"Detects concatenated basic SQL injection and SQLLFI attempts\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942360\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:^[\\\\W\\\\d]+\\\\s*?(?:alter\\\\s*(?:a(?:(?:pplication\\\\s*rol|ggregat)e|s(?:ymmetric\\\\s*ke|sembl)y|u(?:thorization|dit)|vailability\\\\s*group)|c(?:r(?:yptographic\\\\s*provider|edential)|o(?:l(?:latio|um)|nversio)n|ertificate|luster)|s(?:e(?:rv(?:ice|er)|curity|quence|ssion|arch)|y(?:mmetric\\\\s*key|nonym)|togroup|chema)|m(?:a(?:s(?:ter\\\\s*key|k)|terialized)|e(?:ssage\\\\s*type|thod)|odule)|l(?:o(?:g(?:file\\\\s*group|in)|ckdown)|a(?:ngua|r)ge|ibrary)|t(?:(?:abl(?:espac)?|yp)e|r(?:igger|usted)|hreshold|ext)|p(?:a(?:rtition|ckage)|ro(?:cedur|fil)e|ermission)|d(?:i(?:mension|skgroup)|atabase|efault|omain)|r(?:o(?:l(?:lback|e)|ute)|e(?:sourc|mot)e)|f(?:u(?:lltext|nction)|lashback|oreign)|e(?:xte(?:nsion|rnal)|(?:ndpoi|ve)nt)|in(?:dex(?:type)?|memory|stance)|b(?:roker\\\\s*priority|ufferpool)|x(?:ml\\\\s*schema|srobject)|w(?:ork(?:load)?|rapper)|hi(?:erarchy|stogram)|o(?:perator|utline)|(?:nicknam|queu)e|us(?:age|er)|group|java|view)\\\\b|(?:(?:(?:trunc|cre)at|renam)e|d(?:e(?:lete|sc)|rop)|(?:inser|selec)t|load)\\\\s+\\\\w+|u(?:nion\\\\s*(?:(?:distin|sele)ct|all)\\\\b|pdate\\\\s+\\\\w+))|\\\\b(?:(?:(?:(?:trunc|cre|upd)at|renam)e|(?:inser|selec)t|de(?:lete|sc)|alter|load)\\\\s+(?:group_concat|load_file|char)\\\\b\\\\s*\\\\(?|end\\\\s*?\\\\);)|[\\\\\\\"'`\\\\w]\\\\s+as\\\\b\\\\s*[\\\\\\\"'`\\\\w]+\\\\s*\\\\bfrom|[\\\\s(?:]load_file\\\\s*?\\\\(|[\\\\\\\"'`]\\\\s+regexp\\\\W)\",\"options\":{\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-942-500\",\"name\":\"MySQL in-line comment detected\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942500\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i:/\\\\*[!+](?:[\\\\w\\\\s=_\\\\-(?:)]+)?\\\\*/)\",\"options\":{\"case_sensitive\":true,\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-943-100\",\"name\":\"Possible Session Fixation Attack: Setting Cookie Values in HTML\",\"tags\":{\"type\":\"http_protocol_violation\",\"crs_id\":\"943100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"}],\"regex\":\"(?i:\\\\.cookie\\\\b.*?;\\\\W*?(?:expires|domain)\\\\W*?=|\\\\bhttp-equiv\\\\W+set-cookie\\\\b)\",\"options\":{\"case_sensitive\":true,\"min_length\":15}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"crs-944-100\",\"name\":\"Remote Command Execution: Suspicious Java class detected\",\"tags\":{\"type\":\"java_code_injection\",\"crs_id\":\"944100\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"java\\\\.lang\\\\.(?:runtime|processbuilder)\",\"options\":{\"case_sensitive\":true,\"min_length\":17}},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-944-110\",\"name\":\"Remote Command Execution: Java process spawn (CVE-2017-9805)\",\"tags\":{\"type\":\"java_code_injection\",\"crs_id\":\"944110\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:runtime|processbuilder)\",\"options\":{\"case_sensitive\":true,\"min_length\":7}},\"operator\":\"match_regex\"},{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?:unmarshaller|base64data|java\\\\.)\",\"options\":{\"case_sensitive\":true,\"min_length\":5}},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"crs-944-130\",\"name\":\"Suspicious Java class detected\",\"tags\":{\"type\":\"java_code_injection\",\"crs_id\":\"944130\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"list\":[\"com.opensymphony.xwork2\",\"com.sun.org.apache\",\"java.io.bufferedinputstream\",\"java.io.bufferedreader\",\"java.io.bytearrayinputstream\",\"java.io.bytearrayoutputstream\",\"java.io.chararrayreader\",\"java.io.datainputstream\",\"java.io.file\",\"java.io.fileoutputstream\",\"java.io.filepermission\",\"java.io.filewriter\",\"java.io.filterinputstream\",\"java.io.filteroutputstream\",\"java.io.filterreader\",\"java.io.inputstream\",\"java.io.inputstreamreader\",\"java.io.linenumberreader\",\"java.io.objectoutputstream\",\"java.io.outputstream\",\"java.io.pipedoutputstream\",\"java.io.pipedreader\",\"java.io.printstream\",\"java.io.pushbackinputstream\",\"java.io.reader\",\"java.io.stringreader\",\"java.lang.class\",\"java.lang.integer\",\"java.lang.number\",\"java.lang.object\",\"java.lang.process\",\"java.lang.processbuilder\",\"java.lang.reflect\",\"java.lang.runtime\",\"java.lang.string\",\"java.lang.stringbuilder\",\"java.lang.system\",\"javax.script.scriptenginemanager\",\"org.apache.commons\",\"org.apache.struts\",\"org.apache.struts2\",\"org.omg.corba\",\"java.beans.xmldecode\"]},\"operator\":\"phrase_match\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"dog-000-001\",\"name\":\"Look for Cassandra injections\",\"tags\":{\"type\":\"nosql_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"}],\"regex\":\"\\\\ballow\\\\s+filtering\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[\"removeComments\"]},{\"id\":\"dog-000-002\",\"name\":\"OGNL - Look for formatting injection patterns\",\"tags\":{\"type\":\"java_code_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"[#%$]{[^}]+[^\\\\w\\\\s][^}]+}\",\"options\":{\"case_sensitive\":true}}}],\"transformers\":[]},{\"id\":\"dog-000-003\",\"name\":\"OGNL - Detect OGNL exploitation primitives\",\"tags\":{\"type\":\"java_code_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"[@#]ognl\",\"options\":{\"case_sensitive\":true}}}],\"transformers\":[]},{\"id\":\"dog-000-004\",\"name\":\"Spring4Shell - Attempts to exploit the Spring4shell vulnerability\",\"tags\":{\"type\":\"exploit_detection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.body\"}],\"regex\":\"^class\\\\.module\\\\.classLoader\\\\.\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[\"keys_only\"]},{\"id\":\"nfd-000-001\",\"name\":\"Detect common directory discovery scans\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"phrase_match\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"list\":[\"/wordpress/\",\"/etc/\",\"/login.php\",\"/install.php\",\"/administrator\",\"/admin.php\",\"/wp-config\",\"/phpmyadmin\",\"/fckeditor\",\"/mysql\",\"/manager/html\",\".htaccess\",\"/config.php\",\"/configuration\",\"/cgi-bin/php\",\"/search.php\",\"/tinymce\",\"/tiny_mce\",\"/settings.php\",\"../../..\",\"/install/\",\"/download.php\",\"/webdav\",\"/forum.php\",\"/user.php\",\"/style.php\",\"/jmx-console\",\"/modules.php\",\"/include.php\",\"/default.asp\",\"/help.php\",\"/database.yml\",\"/database.yml.pgsql\",\"/database.yml.sqlite3\",\"/database.yml.sqlite\",\"/database.yml.mysql\",\".%2e/\",\"/view.php\",\"/header.php\",\"/search.asp\",\"%5c%5c\",\"/server/php/\",\"/invoker/jmxinvokerservlet\",\"/phpmyadmin/index.php\",\"/data/admin/allowurl.txt\",\"/verify.php\",\"/misc/ajax.js\",\"/.idea\",\"/module.php\",\"/backup.rar\",\"/backup.tar\",\"/backup.zip\",\"/backup.7z\",\"/backup.gz\",\"/backup.tgz\",\"/backup.tar.gz\",\"waitfor%20delay\",\"/calendar.php\",\"/news.php\",\"/dompdf.php\",\"))))))))))))))))\",\"/web.config\",\"tree.php\",\"/cgi-bin-sdb/printenv\",\"/comments.php\",\"/detail.asp\",\"/license.txt\",\"/admin.asp\",\"/auth.php\",\"/list.php\",\"/content.php\",\"/mod.php\",\"/mini.php\",\"/install.pgsql\",\"/install.mysql\",\"/install.sqlite\",\"/install.sqlite3\",\"/install.txt\",\"/install.md\",\"/doku.php\",\"/main.asp\",\"/myadmin\",\"/force-download.php\",\"/iisprotect/admin\",\"/.gitignore\",\"/print.php\",\"/common.php\",\"/mainfile.php\",\"/functions.php\",\"/scripts/setup.php\",\"/faq.php\",\"/op/op.login.php\",\"/home.php\",\"/includes/hnmain.inc.php3\",\"/preview.php\",\"/dump.rar\",\"/dump.tar\",\"/dump.zip\",\"/dump.7z\",\"/dump.gz\",\"/dump.tgz\",\"/dump.tar.gz\",\"/thumbnail.php\",\"/sendcard.php\",\"/global.asax\",\"/directory.php\",\"/footer.php\",\"/error.asp\",\"/forum.asp\",\"/save.php\",\"/htmlsax3.php\",\"/adm/krgourl.php\",\"/includes/converter.inc.php\",\"/nucleus/libs/pluginadmin.php\",\"/base_qry_common.php\",\"/fileadmin\",\"/bitrix/admin/\",\"/adm.php\",\"/util/barcode.php\",\"/action.php\",\"/rss.asp\",\"/downloads.php\",\"/page.php\",\"/snarf_ajax.php\",\"/fck/editor\",\"/sendmail.php\",\"/detail.php\",\"/iframe.php\",\"/swfupload.swf\",\"/jenkins/login\",\"/phpmyadmin/main.php\",\"/phpmyadmin/scripts/setup.php\",\"/user/index.php\",\"/checkout.php\",\"/process.php\",\"/ks_inc/ajax.js\",\"/export.php\",\"/register.php\",\"/cart.php\",\"/console.php\",\"/friend.php\",\"/readmsg.php\",\"/install.asp\",\"/dagent/downloadreport.asp\",\"/system/index.php\",\"/core/changelog.txt\",\"/js/util.js\",\"/interna.php\",\"/gallery.php\",\"/links.php\",\"/data/admin/ver.txt\",\"/language/zh-cn.xml\",\"/productdetails.asp\",\"/admin/template/article_more/config.htm\",\"/components/com_moofaq/includes/file_includer.php\",\"/licence.txt\",\"/rss.xsl\",\"/vtigerservice.php\",\"/mysql/main.php\",\"/passwiki.php\",\"/scr/soustab.php\",\"/global.php\",\"/email.php\",\"/user.asp\",\"/msd\",\"/products.php\",\"/cultbooking.php\",\"/cron.php\",\"/static/js/admincp.js\",\"/comment.php\",\"/maintainers\",\"/modules/plain/adminpart/addplain.php\",\"/wp-content/plugins/ungallery/source_vuln.php\",\"/upgrade.txt\",\"/category.php\",\"/index_logged.php\",\"/members.asp\",\"/script/html.js\",\"/images/ad.js\",\"/awstats/awstats.pl\",\"/includes/esqueletos/skel_null.php\",\"/modules/profile/user.php\",\"/window_top.php\",\"/openbrowser.php\",\"/thread.php\",\"tinfoil_xss\",\"/includes/include.php\",\"/urheber.php\",\"/header.inc.php\",\"/mysqldumper\",\"/display.php\",\"/website.php\",\"/stats.php\",\"/assets/plugins/mp3_id/mp3_id.php\",\"/siteminderagent/forms/smpwservices.fcc\"]}}],\"transformers\":[\"lowercase\"]},{\"id\":\"nfd-000-002\",\"name\":\"Detect failed attempt to fetch readme files\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"readme\\\\.[\\\\.a-z0-9]+$\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-003\",\"name\":\"Detect failed attempt to fetch Java EE resource files\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"^(?:.*web\\\\-inf)(?:.*web\\\\.xml).*$\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-004\",\"name\":\"Detect failed attempt to fetch code files\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"\\\\.(java|pyc?|rb|class)\\\\b\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-005\",\"name\":\"Detect failed attempt to fetch source code archives\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"\\\\.(sql|log|ndb|gz|zip|tar\\\\.gz|tar|regVV|reg|conf|bz2|ini|db|war|bat|inc|btr|server|ds|conf|config|admin|master|sln|bak)\\\\b(?:[^.]|$)\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-006\",\"name\":\"Detect failed attempt to fetch sensitive files\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"\\\\.(cgi|bat|dll|exe|key|cert|crt|pem|der|pkcs|pkcs|pkcs[0-9]*|nsf|jsa|war|java|class|vb|vba|so|git|svn|hg|cvs)([^a-zA-Z0-9_]|$)\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-007\",\"name\":\"Detect failed attempt to fetch archives\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"/[\\\\d\\\\-_]*\\\\.(rar|tar|zip|7z|gz|tgz|tar.gz)\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-008\",\"name\":\"Detect failed attempt to trigger incorrect application behavior\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"(/(administrator/components/com.*\\\\.php|response\\\\.write\\\\(.+\\\\))|select\\\\(.+\\\\)from|\\\\(.*sleep\\\\(.+\\\\)|(%[a-zA-Z0-9]{2}[a-zA-Z]{0,1})+\\\\))\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"nfd-000-009\",\"name\":\"Detect failed attempt to leak the structure of the application\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.response.status\"}],\"regex\":\"^404$\",\"options\":{\"case_sensitive\":true}}},{\"operator\":\"match_regex\",\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"}],\"regex\":\"/(login\\\\.rol|LICENSE|[\\\\w-]+\\\\.(plx|pwd))$\",\"options\":{\"case_sensitive\":false}}}],\"transformers\":[]},{\"id\":\"sqr-000-001\",\"name\":\"SSRF: Try to access the credential manager of the main cloud services\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i)^\\\\W*((http|ftp)s?://)?\\\\W*((::f{4}:)?(169|(0x)?0*a9|0+251)\\\\.?(254|(0x)?0*fe|0+376)[0-9a-fx\\\\.:]+|metadata\\\\.google\\\\.internal|metadata\\\\.goog)\\\\W*/\",\"options\":{\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"sqr-000-002\",\"name\":\"Server-side Javascript injection: Try to detect obvious JS injection\",\"tags\":{\"type\":\"js_code_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"require\\\\(['\\\"][\\\\w\\\\.]+['\\\"]\\\\)|process\\\\.\\\\w+\\\\([\\\\w\\\\.]*\\\\)|\\\\.toString\\\\(\\\\)\",\"options\":{\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[\"removeNulls\"]},{\"id\":\"sqr-000-007\",\"name\":\"NoSQL: Detect common exploitation strategy\",\"tags\":{\"type\":\"nosql_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"}],\"regex\":\"^\\\\$(eq|ne|(l|g)te?|n?in|not|(n|x|)or|and|regex|where|expr|exists)$\"},\"operator\":\"match_regex\"}],\"transformers\":[\"keys_only\"]},{\"id\":\"sqr-000-008\",\"name\":\"Windows: Detect attempts to exfiltrate .ini files\",\"tags\":{\"type\":\"command_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i)[&|]\\\\s*type\\\\s+%\\\\w+%\\\\\\\\+\\\\w+\\\\.ini\\\\s*[&|]\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"sqr-000-009\",\"name\":\"Linux: Detect attempts to exfiltrate passwd files\",\"tags\":{\"type\":\"command_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i)[&|]\\\\s*cat\\\\s+\\\\/etc\\\\/[\\\\w\\\\.\\\\/]*passwd\\\\s*[&|]\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"sqr-000-010\",\"name\":\"Windows: Detect attempts to timeout a shell\",\"tags\":{\"type\":\"command_injection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"(?i)[&|]\\\\s*timeout\\\\s+/t\\\\s+\\\\d+\\\\s*[&|]\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"sqr-000-011\",\"name\":\"SSRF: Try to access internal OMI service (CVE-2021-38647)\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"http(s?):\\\\/\\\\/([A-Za-z0-9\\\\.\\\\-\\\\_]+|\\\\[[A-Fa-f0-9\\\\:]+\\\\]|):5986\\\\/wsman\",\"options\":{\"min_length\":4}},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"sqr-000-012\",\"name\":\"SSRF: Detect SSRF attempt on internal service\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"^(jar:)?(http|https):\\\\/\\\\/([0-9oq]{1,5}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}|[0-9]{1,10}|localhost)(:[0-9]{1,5})?(\\\\/.*|)$\"},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"sqr-000-013\",\"name\":\"SSRF: Detect SSRF attempts using IPv6 or octal/hexdecimal obfuscation\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"^(jar:)?(http|https):\\\\/\\\\/((\\\\[)?[:0-9a-f\\\\.x]{2,}(\\\\])?)(:[0-9]{1,5})?(\\\\/.*)?$\"},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"sqr-000-014\",\"name\":\"SSRF: Detect SSRF domain redirection bypass\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"^(http|https):\\\\/\\\\/(.*burpcollaborator\\\\.net|localtest\\\\.me|mail\\\\.ebc\\\\.apple\\\\.com|bugbounty\\\\.dod\\\\.network|.*\\\\.[nx]ip\\\\.io)\"},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"sqr-000-015\",\"name\":\"SSRF: Detect SSRF attempt using non HTTP protocol\",\"tags\":{\"type\":\"ssrf\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"^(jar:)?((file|netdoc):\\\\/\\\\/[\\\\\\\\\\\\/]+|(dict|gopher|ldap|sftp|tftp):\\\\/\\\\/.*:[0-9]{1,5})\"},\"operator\":\"match_regex\"}],\"transformers\":[\"lowercase\"]},{\"id\":\"sqr-000-017\",\"name\":\"Log4shell: Attempt to exploit log4j CVE-2021-44228\",\"tags\":{\"type\":\"exploit_detection\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.uri.raw\"},{\"address\":\"server.request.query\"},{\"address\":\"server.request.body\"},{\"address\":\"server.request.path_params\"},{\"address\":\"server.request.headers.no_cookies\"},{\"address\":\"grpc.server.request.message\"}],\"regex\":\"\\\\${[^j]*j[^n]*n[^d]*d[^i]*i[^:]*:[^}]*}\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-0xx\",\"name\":\"Joomla exploitation tool\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"JDatabaseDriverMysqli\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-10x\",\"name\":\"Nessus\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)^Nessus(/|([ :]+SOAP))\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-12x\",\"name\":\"Arachni\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"^Arachni\\\\/v\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-13x\",\"name\":\"Jorgee\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)\\\\bJorgee\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-14x\",\"name\":\"Probely\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)\\\\bProbely\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-15x\",\"name\":\"Metis\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)\\\\bmetis\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-16x\",\"name\":\"SQL power injector\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"sql power injector\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-18x\",\"name\":\"N-Stealth\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)\\\\bn-stealth\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-19x\",\"name\":\"Brutus\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)\\\\bbrutus\\\\b\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-1xx\",\"name\":\"Shellshock exploitation tool\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"\\\\(\\\\) \\\\{ :; *\\\\}\"},\"operator\":\"match_regex\"}],\"transformers\":[]},{\"id\":\"ua0-600-20x\",\"name\":\"Netsparker\",\"tags\":{\"type\":\"security_scanner\",\"category\":\"attack_attempt\"},\"conditions\":[{\"parameters\":{\"inputs\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"]}],\"regex\":\"(?i)(

`+v;L8FK0a; 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%0A>XZN1%JioQ=Xt^9tnOgk)H;&%m29+KyD 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-arm64.so b/vendor/github.com/DataDog/go-libddwaf/v2/internal/lib/libddwaf-linux-arm64.so new file mode 100644 index 0000000000000000000000000000000000000000..d906d9756047ce484fe43ac65e049c079125e499 GIT binary patch literal 2030704 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℘#eIVbJ z^CFgihd!CePbJS({x)?ol&`_MWb%di-dz3(^-#!{;Q5h!7WzghPspv3PaXQde0}EG z$Tz`T`DyImPQD&>9{r=A|6SC{Kt4D15X+|x^)FwR98&p0tnbjy_?di8&V^k5Dt)q$ z9}v!8`FxC1%4ecJEBOlarCNR;`84unLjB7h=l4P<--2^0db6MZ@${vEd}eZvpPM^x7WB&mhwl)vy#6@f2-xQ&^H?S9IRt2 zpEvX$`Bt1?(Odoe7vcGV{2A6Umj9l6NFv{s^-bjyZ86SaC|@?zzdYu9bNT#uA>W?w z8p&S``O8-e=dXM>#;N5Wvo4K%G3L|CyYTlP`ID@B^maf0U-I68{7vQ`%fDnkiTnk| zPv!fwUxxB2SeHz`3ircYKH;t7bu8q!@^|Tx{8aXJDgTUoD*2r3_gcO&eY=sbOuudA zXLAmA^7$A)`e#4?_sDr5pMmozmVeCiiTnW8F_pi@x(wxOk#i=WoSbv{AmlHfmUSG- zx8!^*{eO1@~wUw#hfX(L~jzSqiQ&izil66ZzqPCx&3*pCDGQlbCLHz2n}zD78I zR-M*>ypSf3Hi%k<{TNymkPgsn{A=pAl&{Ks zD)}L=jQ_q`%hzCkHS#yu*R6az_Gu?yh1{a|`}yz6^8@)t)LSh7GMvBioN-e59n|Mg zer~w`%kSm+T)uv||I0Vz{2IygQ2+9W8NZV6vcY&?*YYD-mqxx8dA9Nm@J{|;?p@Ib z{ru+${YReBXJYx+?8ijDKi`|mw+#J9ehANJ@@wd;x%@Hqdm(>1+&|?LZ!@mvQvT=A zf8?8zTP@##KG4W_CZAUR67%WgSFwMiuAl!V)YU+~KlL2T*Jl4F^1J!2R6Y#-N4`0A zo5{CiKDm6tocq80(D3_9zB{><@^|SQm3(RXVJ&}x{nE(yV!yQV?U;WjUz_tZ`mmq> zA>=%eZ$mz@eB#~4b(P4cz*G6m;rFln$WZ_CpHqjqeDaXL{AcvZk$mrvzkEveMI|4S zPc7ep`84vI!u?0S5ckGTJ{|9kKI-RxWXNB>Q^;Sw0(Fwe=cSLO^3`~LC|`nkX7bI* zA(vmqy{M26SeKFfBkH`Ae-Y|mzCyVF$akfl8~K5(Z!6Ej{a^k%eJT2PKmY6LO9S~Y z=x?!nKlWWBUzR*m`EPiBDBqcRX7UrLhg`lI`4sYk97gh^L;cI6#m0TClHX1}*YbPt zMt&=~wemfw+fF_${UQ3epZ{MOe<06U_gMZ8>zK$lqtB=ENzNMgtD*cu#>wO-ky|c5 z125$3ur4F{BJ9Uf{$1u%$=3<}U;aIwZ{#aeZ>@Zd(0}Atvwx#c`uRT??*H=BsKZ!3 z;g9+KC7+2tkjl3X=db)c^33Gtg!-4Czm3J&n)DdP|qXzh15?e{{`nxC4ZiKW-Z^B=Noy> zJX`s};rx}K$o!+v`}r@xx)0=woBaO{o7b`uXojzZ%F7raoi&k<34l@4+~! zJ@kM15}c2j{K|0t%6DS^h5U!q$w)p2=TRv?BGkY9IJ}nc&HNkrpF;lfBgn0j-^ugQ zfBN~~AMT&>2{rs0?{DFKQ>NA$l#5@!EK|G(zZy|@Fd~eS2Oui?5 zK9|46`B=ygWSo)wLe{sG&%yjF`PQNT$RDLYH}Zn_w(<+u7oGe~_I32%e*Ra7`j_8` z$MUC`Pa;1#^ndwbtjkdT5Pc()pGBSI@}oolm;Z`$e`X+$%cyqMV!2H~swIV_gRFd8w0FeiHLZMRQ@sJ59KS+pELO#QMjkC&T?GilhJiGttD~{*UNn z<};A5Mn19pefmHm{{&CvU*SXfz2uz9XQiL#@-JD(LVjt;Uw#|=x0E;RyGnit=X))` zBHVxEZ-o1gd=>UZCqIpQS2RKVZSW!gGpOf*dS`oEm*0J*{Ew_xC7+!6)beXX{_?*wpH}`)ypu0NKZz!cqyPOA z^1qEd2lC~~Etc=dIhDwdq<&KQ^7M_N{04H!qO8GMM=Ssc-^RMNbF`q_$AM4x7ce!uU|JPNcsFPn$Zqa1@{HG25 zUw#qa8_V}%{6szzeLIyu6piOIl&{SCX7Wk?Jju87&*c|#9u@Li`L2ZFtZoN=Nl`uX3* z{0H*qL;mt1=T0Jjjo(43{5P!cP<}K0Gn4;|{+Y{v%Q%JnKI&m4-;s4J<(us`-d~mc zuyFs8AJ2Om`Cr1{Kjm++j-7mC>L;4ApZ}}mJdj^RUybFDvA+`eW~@spf12lq@`spz zCjS$4mCK){pA_;td443{fqElP_vpL*{9ok0 zF_8ZqkL5FQUL^8Y$S0LI8kLk$eu$qted&EBU|KceQ+X z=F`X@3H?XD2m7v*KN$M|RQ>$7>3f;{4*F6q zKRfgv`I-Fva3p`6`Yh$YBA-hBDD$c1+mb^gev!VaUH{~9f%b#SNLjFSN|MIQrho$^wa;xO?vM*}+KiJoe z{5|$#D}O)qfBDb(u4tNm{{Ia9N4_`vE|!17_=$Y?aQ~5))a_9I0(Fwf_hKD$`C-&g zA^(c`jO3T|-ctTJeV~#rLS5DJV|Z^P|2X6?Ka+ayxzPA+e9jL33e8*7#^7F~3l0VGfJ!<*A^utEJ z2>Yd#A4uJH^4F-hXxe`M*K%JS$p6K@i{;l)SBZQIa!%#Dk>h zyf=|w!F@HAFUh_h$_wfwlXv0oKl1ZAXAAj(eAh^RQ|LeP4@3U)x2exsJ}q_C$lqn% zTlq8ew@!XDbsl}MpZ}BL_m}(HO|3QJ{f&uD1SKo{*wQPy3OU!vo8wy z0iplM-wF30`O&OI~h^Fu7{{%S<X~o%9~!F{HXA07I?d~v?Fl+VDqU&;T={;lPcOgWxU zBX8-4t$c%U{>smw&ZC+7`M06}$j{^XSe~=*68UU=S1NyleKeH6i)ZrHL;mtpS@%Nz z5#x;H4^vmA{B-KMk}ntT|MD5BpGH0x-`mP3eRo`ko%|~HT{Lq)|1X&TKt3n?I+kzB z`X=(N@Kin(^BKxdqHZ(!*YvksK07%S@*h&?Bl&u)V=4bD`?`|v&Hk$8AJAtS`GGv& z%4ZDyU%o$e8_m+s|9sBbfqXjpc`Uz}eUZq|ran{o3*4`U^4XbZCZ7k-h|CfJ{aawu9`gZb-sfXzQ`uYDK;|$~%@!nYe2RxB4 z%J-)7@9R&$b)8o1w z$=~99Eag-3d?i1R@2ces(&robTg<1GKO5>_J{|9kX6xraJ^gtgza#wn7x`4wTOvP+ zdPwD0(yxZ{=@}=JzaReoAitOS6!Hy1|B+wEdrSEt^x;Z=Bwou`i z^7mPI@5<#1u&)dGSIl!H|0LAE z{Ca)|Rq|!Xt(H&C{%z!Mac^wp7qgE#`9H`Znxmipt;}a2pMm$r^6R-rCGt(F!&H7J zeQzkAXuoma$m9=G=ehiSzPFH1&G|Bt&p)Xk14fp@JpMM?dU%ngjjODL!pG@R;l0z!LFZ6%; z0wI6-Z0x&S{sY#xkjK>3NdDvS`&Yhbxc|tP!fW|T;rx|fNuO!umve9Fg#Z5kr;I<4*X);A{=<;J{7(8qDnFPUhVqN~ z-b{WI^UUS*R|D5wNn!BI> zeVmU2`CRm|SpF63o5-&W_fPqXtk+QfALgIQf6Vi_{4w@LAzwPwzkKd+{>uNxeyrq= zkV7q>iS=sa3zAPOpWy!Sx!K8|;9e2U)6f4pavR7OV*aswf9fQWuTGy#yE}w$j3i(3hGm=ls^QHW2 z?qijFUglHFAEECx@~!E6t$fJ*JNdfo>uA1y{u3QO$^XhH8pwZ0-;3o-urCt%kUCH0 z8-@NOzmEGuCf|WRnaek39SiwZoL?jP)AXxSzAW>vz6ATOl`l!ooqQv5h!*JQziYVv$nPSDSiU3kPvpxopH%*{kiUG5 z2`2sDwu>@(&U|wD+x+e*5;{>!mm1NrXc9Ls0r?{$fMZ=O%(S?K@rBRNMh`CN>j%hzYU3i)fCYa{vFEPQD=?E!5Bde(HQ6U;Wkbxe&|epbsSSl|%i@HzBv7 z{Poa(duw~&9z^CS8D;r=6EgLzi+cbHEt-~nS-Ahm@27r-@}F@oXYz?39ItyWf0S_w`MlJ_NPYs$5H3eV*UI#q231a2g2WfM)a+crM?V918jV)a^)qz>m0p%HIrs|BiFQ^|K?U)1tLxpy`4H9j5BzmU<_KTV&Bmg?s}$uGz69muC(KC%2b>L-z3L*1tGiRK=UKa}4@{bcfO z$tRb8$$l^7yYT!-z7_eD@|Vb=k{?XH)$)1R7ma*jyp^9qKArqf>MB~gpa1Kj|I3#M z=db*H?lXz}EZ&>SkK(;U`E=p@l`l=5=kf`6ANS!x{xF1Sv7w$#1{7Uj^kxg0Ii&;O;+|K%4@ z&$0Zn(0}9$hVxf`HO~*_`%#CP{5rljm+wGb74j9>FC+QyLjB7R3ctVP>yTS5pKN#T zKl0x&|5mZ z-;jI?`BB`@NAiSvDCMtHZD4{vmah%BN$Tq5M$lB$FQ%>R-Mn>s81Xpsq&p<3jz*XXc(z$=3<}M?NRdH}YBd zu2x=z{x3hB^Ez6gpZ~e|Kz=mi#PSuWpG1BG^^?jkrhbO<4f)X&-0~xKJFov{33Fw>X3NPgAvW_G9#e7#OKc7BO$?p#5ul#=YOCx`f=Ue&uA%FRf z+?S%XpZ^~CKz<4HjO9B~KZ(2v_aFIHj5CyP7V?*$MjhtzbEvmMel~S7l7DzB_aFHt zFG_zaVXPudGWWUzPW^@_op;lfTY)MXScq z|NaU8{r@}R?{D&@xbMaC%jv6${O8njD!+p79m=o8Gx?SDja+^W?=9pjklRT9H*zcG zzYhIhz60~G*Fb(8dB*Z-b|2rv6ZvAiHQ+y{2_8H-_G-~e8!N!e0%at<>!R_<==2G z%H&t_do7o5cK9U!uWLq8A-_DFzw*1N^HRPcc~55xH@UzIv3>l*nh;rFln0nUp~{&_fm*X-wi3+p?OZ^Jsq^82Z)L|*Z|seE?! z%TRs+>ypXu3iU6)hy7T{A7&j#@_Be~DZhd`ujG4&^H=^?-rLAurJuL*oI3C1OL2dT z*6QcKKhF>3ejr7OqVZU_pJ*nqt-G2T*AfJJJpHTnu+e7~H1H%1BKIxw)jsEw~Q2ri$ zBa{Dy{gumi5BDGWLp(o{uT4Fa@|V~zmHeNqOD#W*I&9=0hyE|0lJD*0Pf}OWdj0&H z@b^!7$9!V>k65on{x{C4R6gnA75!=`KZ<>t$^XSY zGnZe({w?Hh)AvU5sW|sb`5&p1N`49bq?Z4UbF-1Z!+N#ypU?+7`E#F+`%JV^KmSq4 zUw$I_#PW4G?-KdvA%FRY%yTIJH#ukW=~=H_{yXMb$gicJjO3s5-csI${xAP2xz+ON zS;t0x6ZO!_pJBZ^`Ocg#(Z>D!XX3mb$ZzJmV)@(ju|)m_d8YCYsL!GNRq7#=zlrDa zhJ9Dazai(5{9*b=DZe=MANhjZ8*BNj)I%drc)pc?LmhVVXPJMrNk9KzgunmGC*!^p z%byK@e~?f5%y@sL^0`9)k#9x4W%3Q#U%7lL#xLXxF#bsXc*tMA47pYENzWLcN40z! za%kk!alW+jS@2H&d-iYi(|-P+u^$KW$vHP;`K;{6M1Dg!f8}$8-@o#hd1mrE*mt@7 z71q6w&qzKa`EB%vQhsHqfB6Udjq9hD@6CDI$p687TKT8!_fGx{^$=~^&wnb`eIQ?n zI*H{`HJ)c8-=2C-<(pH_L-}>&kjV$EdoG_o)W3XR_U}l3FY8#!w_!e&{A+Tn<+m`; zMt)$p|H!X;cbrcrpZKEj{)#s1=U*_+K>kR$|HyabzL&@!=6h55T;cBz^6#*YnS4Q> z&*d*uKZX3y?7NZtVAiFSzxU}lpGtl=$9^Gll*y|CstoF2A0AUC5W=yGHWGSg%t4E9#+=zrecJ@}(K4ksri3t^5u4X(wMK)c+R!{Qtqa z59H@j&$0Y#a!%y0G5=J4E4dBjE0J3!Kbn4+%O~D{yuO8e^^m{(Uwl_7Ux7R;`IL-b z%g<%MH1c(MZ!7;d@9pIO<(?C5+0Xw!A%FP(LiM_cvt zUyXVg$PZ(G#qvGrtBHJS&eK#r8S@#+mt;R?@-ssH%ipDs74n+>HIn~~dMM@3vyPSg zypX^AyR2^`pMmdf<#RnVu7^%O3+ovDyr2I%A%FRl+?QhcF|11>e}EiP`7|Ma`Ne#1 zCV!N=%Hek*^B96I?Q!|$)H`}yC1 z59Eu4^H;tw=X)ZbE!4k!7QSmJpPd{s`9AEgTs|xJr9%FA$Y1^pUdrcUoJt;t{x4sU zdT!)TlXEMdhwtj-Z*x9I+w}84HvIiTzA)p&@*jl!E-y;0}m9NP2 ziTq^xQY!x*{c|W^F7$u-hV;W+J{k9cLOwq^jO2eO&r&`&`?Qk(ntfNxH)lSL{BWLc zf?2Bl-IQri|;lKZXo$&|q$(d&? zzcT#$cll1_lgb|;pP_t2-^=93hW;-PIRG0#N4P&j|(>$2~L@)0>_@&!2`bNROP!$N)? z^)QlupZ!?Me@YIO{9S&x)^_T)k)Ij*k9=R&rIX*qx<@0a zg?v)^Ow{2}{v~}flRwVyyIlSb`?Qd6#QKip({Nst^7F~9l3zj{*760y{a^kGeYlk$ zy89&mYcEkJ|1teG+OePiWAulC{AYYuEPtBk6ZstEoXQsu=db)DeotlcXF~q+OUSK| zPk7Dv_vT2RG5=D&9Q9Mlm!{9u@*}9vM*br6Y~^X_|MGQ1{yX*azmI(}knc%9kL9<7 z{x4sJ?@HxA4Ef7nVjVO2U5ua0lW_l+f5^TV$@gMEmhzohmr6b_^;63~qAxY_A2Od- z{u=Aj$=4#EXy<ae1`HBnSUl&`v1B>6qWKT@Jjv#{kE1b!n!o_CBokyGuow}|4(^-AYUisFJF@R zB=W2152^f{kiYyy)+>`g67HY!FT%fnkuS%5M)DQI`76IZ^ndv$;rEyPTGp$P-$$OU z{1@!6P9AMI>9_R|?b^@($>P!%+Y7t+=-o z^83U6M;=W${@zl)Z20{p|A2i|%U>X$Mn3=k<8!u^Z$O{vA$b zv3$mM{N6;qa=3rWUt|75`P+CV|CAhZ`F`R4Dc_Xu9m&56e}9l)#XYBzpU!@)<&Uzy zjr=U~Y2_dAU7h@T-W%=S&;NqZ|K(5d-dH}-L*y?%m~~9$KMv=w{0N@UU$efI{Ar%A(pAzzy z-^@KBmA}Nk9?ECndo%f3oY%Sh4#qF!)3S~u`R1JCrTjzobtT_`_tx^+m}evZCF|bG z?`58yykgy>z54mTPCf(qk@`7haDseC8aaVVdh->sQ^famhh$*qvT!~93` zP1x6^{6)^SO8zjp)$#|}ca8ik>adkhMSt$(2e4jI-p~KTkiUGD&&K;JmVeCop2#0# zo~e8bau~`FV7)T=Od)^yfO;t8GvOoo&$$GK2m)8rY;cc#B3@~Jt$Qu$h}<50c>`DF6XsfS!X1LGI+b-7=S9*9rd_U@~mT$_r+{m}#d~fCZkWVN74dX}q^z;82^B>568|q*FC(iLi{!jX9D*u3W zAIg73&YApp-kZyR&36^@+1OtrdB%5@@(f%RU;(FJqii zJ{7rD@>4_o%O_l8{N6@BCG%(6LBSQZ2+{1JEhIk?0m+u|PXQkds`9jo5CBK}0Tgw;WzSPM7z`C^ZJ2@Xa`R?Qp9oWzR zGy2IuUNL?wUy}JG@>4?pk>AIBhVs2b{mbX2PICF0?Ds-`aL8Z&U%so9Z_YYa^0Pz# zm(RsIHu5v6+g83D`E>F{Lj51q&wo$$??Ao{^Ni(7hVxhcBlb%wpN-#5L-`TpmdUT7 z4s-cT?5{$80lAIjbMxL(zB|ABDtR0FzkDC^Y2u`h=5sknz{@*P?CTt08eU!KrUM)Iqfe<}Yx=TRkJfI6?` zmxl9Kz8rPf$`@mtPQGNQ|AYJa|2ovad>OtgmQQ^0IOjyZ3i~dVr|gTN{9MkvOuiED z&E-pTE*J9U_^y%sK>AxLUx}}{>p#KIGKDo=9$Zv zpl%EK`8$p4WF+5!dMM>LalTaY+r#}wz6$ec2J#!& zU$Oj7>MfD~ndejaU$|!uBVRw9zw(XQ7oB`R)+IWupa0|JHjuB* zdt>?9cp~41`KR)X`3&Wk&^I#q=%R5wY)@3ALiF2)#PfeXy@@L7Rme0$+ zYvdb;{N)$1FFN^~+;5}9Pg|0BAMK0lDZ#(ZM=HH@FgH=u8%@+U+8mv2Dd z%jDOxF1b7j{YSoWxc|u4rOr$Ff#Loy-;(*)@~s%BksnSyxAH99f8>{OzDGyI-v%G@ zzY!nEcc!jl`T5jmB7cTHkjmE#{YU-=&u8+Psn1+~3jL&z|AgPgBl)8Au~NPZ`?Qk( zmUXG+d-HrFzlGdd`3KZ(C%=z#Gx}{m|AqM8fqa5w+y`R$`l0{GA7?(P{2}(&P<~VB zKk|!1|B*k>_=WrzoKqwD?TlZ_&t)Ae`A*brEq|XnY2?eZA6xk=%(Ih!6#CDR{rnGP zeFyTZ$RU=0%zP60XN;f9FXnrP^6lC0nfzkOEDddlFE{x<)h2OvOJL#X5{CnK< zYx#YwV>b#LXzu#Y&lKUsv+w$)}e8f`9{l)$M59NQxDP6{rq2~ zFAd}q&NCh-mOsOJn#lKLKB@dRekTs)UxfZIf0y~>@``mSZN~59D{)>#$M*Bz zo%{JfUNL?wUn%4--#661{Kvd^C_jbz$>a~y_j37x^qE5bT*zNOBXw2E_v3pj`2*x! z%TEvYANhT(Z)*>Kf0j=<&!pehd30Pq|BJ)d{_FzNdD;0#`7%Y_pCk6p^_iSIJNwtyT{`+^39oND}RFe>Eu&# zpNx+0=f7IGf6D*EK8oc({F40T-|&1YpN(}N$}{Gl$&Y3q898}0+8 z{0!Eyl0V5ks+J$cIF0;?@b^#oQ|yaQ{*%yuPUz=~D&;$}?<)DltV=CFE95WVmwQnwpON`= z@~OxnIJR@|!rn zO8F1@u1bC_eYlqYgSu+uliW70t5$wE^X%l8QxDNe{roSdKM&+@QHQbo!*KtUe}<>> z-!Y$|`~$|%fe3Kk^kr{mcKu_@(?M*1eM774nzgMsAJ#V&>V(cbIUzE}i@$ z_EB_lKmTv|u7Ui1-W$sg3iU7lCgd;Qdt>^K{M0qZp2`0h@|PdVdkgtxeAh^REp=7O z&tbn;^4~IkEuWuzR3qPs`$j82A^iPWKJgyoJ{gVr`5(ak9mwb7+>GUGQ*Vj9V4PHb z0pB~6-$*{0e7|u2kzdd6h(bQ;nd5aF$?xF#Qhs9S|MKbRC$)TL`gSAFsqDgFG{4fh}UUE~wXGtTiu{zKL)l`j|WKk_xg?=Si7q5sRLCg(zaM(F?Y?fBkO zKIv5B^{V7ov5#u`cX_^%FTlCh%HN^Cb@GduXLM>m|Lb_~Kz;*t8_RbI^)KI$JX85% z?BAjMSoV7+pN%@q<)86gg?yrmsek#|^tVzz4bNBdn(wXU*OOZ#UzNIQ<;$@yoqQ|u ziB9Y1e}DM>CBK0A$MQ*b9)E8l-;sTh%4g(zhw_7YK9e7g=kk5o?}dCT_U}l(GhWK? z;C!j%2Qz*xznA^k$af3Gx`j31MzAKSG$2z9+S=hfr z`Q7yAOg<<5HkY5n{0sS%oNFWb%Hi)1@=dARN`5uBn`?rnMsAN8Eb7spfi%i-^z@>}V9nfw*jJ(s_U7xHb$b0nXY^P-eL%6C=r z-%?k#{7iCgsZFpd#Q3%RPS&@Pug3GO{3&wlXq;3cDg5ROB{2uBdkw1y2@|WowL-{44 z|H!Yyb9u@<3;BigjgkBx)Kw{epZQesr`V^pd_Y|_@|)>1t^5Pt+sR)epXl6v{$Dbm zfxM#+#PYvWhl%_ua!BRT=i_}blu!28v1jtt!~IkKG4m))KIg)q#kH4#wPj}L|e^&Bm?jAp1%YQ_FYviZWms@{{R%seFsj|K;zoE}8ru>N%G$PR@n=@AQq4d}Z!YrF=cU zw~`-6zpdq$h5Nt!be?bJ?@^zfe4<&#>mL2SpZ{s(Hjsaxz8A~ar9UL{|4?tKe4<~C z-#e6_$9psR_jx{-AHsd6knc@BjO0HE_fPqj^oL6REB>xk%RB11k#9y0t^9ZFi%xzK z-y2=f&;RJ~`%C^g=SVD{;(+l!P2|-BzGCP<@R-Mr`>2wCpZ-?MGxl#I-;CdDt$dQ-j`Qi{vy)p?_VZsDAISgBK8ocZh4WW_1i7X1 z8`!@?`R?KVDL<5)bNM3VQ^?N>^)HW>AJ;=E|1N!?k}q73pReVAq;4DeL$i&aZ{>SU zGWJe>H#tNX_Va&>{yC7Z8qQz&_M!jCC%Ja~-c-IU>pPTB{_MC;GWp-Rx8(Alg#Iu8 zl>RW1FG&9^Gn zV);_kVIsdT+&|?%1J;r=h*je1MuE$f@gUuK-4{NH#cpN4ZYm#@G% zQpjhfZb$M<{x!+BbzaKn<$hbqk0R$;8rfO$q&^z&bnemIbS zpFR-FZ(u%&e14u!|$#-U-mhx!cNxxmMO1=>} z)bflvY~+6==T<%!b>7LhUq~QvHWcI zZz4Z|{hP{9VSR`43&}Z?Pq5K=|K{@d$)S*+Po5+BiQFqn`PIC)lJ7@9tmUUNP9r}X zZ{-(~Pba^O=cB9p`A_oN`1~5k{~hXIz6^aMk)KD-sr+&3WGH`+{+Y@5rEYWiu|B`Et=QEHGSeICS zGxd|mPa}s^eqZQ6@;_21nS2)Bo6DD=Zx`~F@R58~zN?gP!1I-S6TFsR#CJ9FdFUst zd~?R>l;6NUt>nv+XDz=b^dI@n)K4q_f_>e|7p1PEs-OR})aO8+GM`w!dgwp$ zm)Kva{HTz>{7mCzBsfz2)+i!|yNo6XZFP zKf`-V`Q7Z7N`89iKk`?}t&xBJ0Qt*zqkcO1gY2*9#(w^{(KiP2M|eJ#zsq_h@=fS( zsr&=>(NKO7`zVvY$a$2@H=sTX`LFoik^Cn1ODTVab*bdPW}I4nVd($z|AzeKMaWX-~j ze?tzHe0%PVwfrmUr;)Ene`w`}CGsKpr1E|E-l2TL>&M@f$p`O^J(u6c_ZIS> zhQGhb-{-qZdBwh{Ufkyrj`>vHwF!i_&JNdog?+>^1^Piga9ms3?XDt62 zIVbX&$t{(CM1LE~_u{)U`J~&Czx)<*E97_LBl$ecrd^-9-bbCMlr$YYn!@}R+?94NfzsY-3`TOKKlrO-( z&g7T!T?fc({`W%u(i*cy|NG|{c{=CVNAex;v*ah>rTme3$KzZf-+aEYSMp;PpX1y4 z+#>(9p6Xi<-x2=%!{>PZK|TK_&o}b$zV`pe-vE&M_GjlZA%+wmLu zgFGMR71k@%RjcP`yn9@SZ_BIu<8R0Bgj3 zZv6Z}e(#av=Vz7=Rv14Y%V*qY>m2+nxPZ>XvFB|TS^6-2npOfF4xqQg@ zBl+q49xvtL`C8s_pKs(_F@7h1mVBbRpF?;)maomaC-NN_e<C2O{(bImojhm!!QK5F)~23gd3ZjRpF#Z$qN1iZ8>?dpA#o}aqL7+ocQ@~UJ@sMAx`&8Vg|Fv zz#EPm3^<@OXh95ORt#ENJ7{+V=zy5jfEvW2-4S~Ph(XH;Ml5PjcdJ3eYC+IqP}8Cj zw7R|D(kG7&G8RyjZfss zZw83`oA{qUQ}$qKJgo9x*7#ACe@NpK`3;TVR`az@jYm}eag7I5e@|ba(*Lr?lPdo;jsII^4;vbv$cHulBg)^#HJ(=a=QaMM z8W+kMpUAg0eoOVozQ!{uf2%UBhkvQ&O#zKhhEEVPvp}Y z|GaA7tj0f~>Xp~H=SOFxd6S~X)2d!&jbBy$y{hq|lCNvLs@B<>8vh|xp0>vKsq%C+ zetNR*r14{F-Z9kpOUjRt-=1!-?^NX%8vhAZ9!ukM|H(7fPe9}A>T^)zlQ?Zz<9C0{ z9_O>7@uK1(jc08~epTa>IBiYiO_k52ToZpaiPJVT`LeRNn;M_QX?HYUR-eNfzw<{< zIiniy{)NNi8Xx_!!;>0c>^eNH@od}SS&jGp#^HI5-=808{EMo-lX~d-scQ0{Rq}O> zPoCG-xL?J$U5!ul)7N;r>6Cw{ar-k4Cn~>5IoA{y8lULL(s*9!ZBnjDJ`;NgX!7Q7 zI&phYNtXJIBc(mg1O^uVEaPq&S z@$0|s$cHt)rsSg+~6qF3Fn(wh7m%C52+UwP_b$DTF*O={j(*Ld|09Y537 zcwfa2q&jUsZ6$AMe5YDBSl0MZ$%izaQ}ePljgy8`o(+w!E551m$JKMg8sGf1lTTXX ze>K_n*LYaT*ERmVYCX2C@wk#F|JSsB?yC8nrSYVa4`@84__D_Th0145;~6Etq49rE zaptDR3raq&@n2AJY+B>R+*LXw8FKfJ|cu3bW6}N0j`A#xJV*#iqs+N`+Do>RQ9@gG!viTvKQeo9K-(s=MQ3uF5XX#7*k4=iiE z{5eN{P2)u+zoGG(k`HVA)06c;jn|cYUgNjbdPQC1eI?)4xL2(Mli#1#L!jl9-_rOm zDmxEoJgDSD8vm9pPQTvJ_^OiM)c93pKVglpDfzUeKp(EBS!NKc@C8mo;8g@*$0v6kpT$zflM8-L>Hp|0_+;%$xp2i33p8Yh3M`q3Xu>+QrO-qZMsk`HP8 z*Hph-)A&s#zp3%-YMnN$@vM@MYrLU&TH}AJ{7+uvc3bI3<6D&e`x?Khbzs^5h)zNzHbG#*j<-_&?m$%i%mapj-m8jmaa zyvBb<#Zh&Qr<8nKl+AK2kiS@mJJ1vZnE+KXdfF zsqvRo|BY+>gpyBd{HK(j%NoC}9@raTqpPtr3c``0*JfY;5HGWQwYik~7_2Q>a`>bc7r?l(kLcw6I>a|nHnUsdwtkEiuB{EAZ^OXHLC2?33-eASU(*0}vOhleyiInS`B@w-ZX zL*txnA@Xliu!QP9crgl>C~; z?E{XUH#Gho${xZRA02Y!;~HO6@jzPR=3z%ZuknE5WsN_i<|A#52bFwZjqsFsJ zzOC`exwXE=%SxVnep)|QA9tQ>X?&m3e?a5*k|Q6|_&H^t8ya6y@|zm}S+(vL*7zwU zpVs)~Twh+}XO(sAQKA`d6 zRdIMo<2fb2rtyFN*D9{octOdBHC|QvOl!QP176YQDUz@s^SgY5WtaeK$1TQSzG_|7}&Cu*Q2zKCSUzo6NH`zIe>B&$`CHb21)j z{DhJx|7BVaE!Do3#?LDGfX3I4tA3&JOUiGoY25#mBfp{XyUI?&8V@P?xW-r1x?f)7 zt4hAC@yYq#y2jU(d|%_guJla)EI{PnWWV*MlD9OTS3IEc-=D0XYrLrB*EIe^N)HiiYo#dUsLkS z8ef0bDQ8IIWi>xt)427VBfqKfBWhfWYy8qlM?S6b>q>8Vjo(o6b&a1@_SV+;EhSGn z(|Twqy;&N+t>gn5ub* zmlZE-{7;nMXluNwwvrEMyr%qcNaI~4zozjUDjwU^_)y7*HU3-5 z-r^d!Ldt$L9#TB7@#iM%?HXTI@^y`u6>n?&CsaH3HC|Qni2X#6A9k2W>FqU7Tm_ck4UrZpZ?@@0+x zh#D8_8o#9E`x;NGc{J%x>*1=B4`}=cReLRK{HBs$(|BB!XH(;wNI5OzWYcZ)CQQd0gYxaYsI> z^Cumi*7%x|&uaXR;(3k76)$SMsCZf9b;YY1A1Yqgcu2}Yk2=rwYrK2G(SJbWBXxc__zn27#wYf(qVYQy zo%}-@PbT_J<8TC1@@#5=F{zZ+auRFY~@$P#LuWJ0hKhgNa4_B2vc==fE_y1{XT&Qol z8h5OL5KH+DihSt(TnO>IxZ&af7iTUWba8i)mR;P5gq@qxsyy) z-o^L2c+tge7caZ`J{PaLxZ@d|FY7M;h)cfd;sFU+T>O}eue*6O}Jn!PqyLi#XPq}#6#ZS9<)x}?M@w$t@=;BQm54m{T#m~5S*Tr9Q z@xF_nb@8E#zvANLOAqY-RTmd7e$K@$7hiR8zl*!awSbFXaLET<{Gy95yZ9v+Uvcrv zE*^66D=xn3;;*^*nv1Ww__~X~?&2FR{)UU+bn!P`eAC6>a`8JZ{;`dVT(JSOGA$NJ0(l|9!oHQvh_nPUhBO3a63kNF^x<~+w~h9d@{uI$OZ)J`gVVwKwPp0021r5>VlSNV|A^$f7< z8-jbbQEqa-j0GouQr;x+kSD}C4e{&K-yNj8NCaVQ#1MUh#tbnqXu`Phb@|FXP>yAB ziJszX(|J5Z=Sc%kY*1b_++U$QC~JVn47%9+`m*k$+eiw}P8mGMXxfNRiQG%=qZWZ@ zJSA?>ReqCR3QU!1Tvx%D-49gEo@TOqedOJzCQ5?D{Xbd0dC~#uP;*4J@1RQ_Tyev( zE`{Z%egwC+8Jzy^X?mEnkXMUZ6$4ffAS7n^iU!FV64VUQGHAol8v5Qnx+BHQtUOOr zbIYZ9Qk#p)GJ?NMI*7PCOmudTEH}G5NO;CXy6+Xu9i%PV6d&TIXzd_LvyGdE3vmV% z-7ebrV!PD2%x4=JAfEvf#d$JfEhowb7iak`vbr>u5leiPEThPhFYyprhRKfajU(!e z?-2F{5S%5YLJmb096jGC;SBiE)P=OtZQ z$Am-Pm#3m_kRr{ie1Zid=OHbl>EtsXj2b9?-jHz{nSrIeH9W!B&1eM7gHRA>?QgpK(^&uHHU~434m=%L&WRvF&ACgXyA5Ff8 zrK4l@?tk7)!iA9N{rooLbSnSbaT+z?YbN*KiBHSliqWa(--^;9C4))+yH5F1jNCPI zj6kPJ2Bo5Y_(R$89l#wk%ShTV6O80s->vxKjNCTU48F#UF?7hpy6oi{x&l*%BPkeA(d|jOlzR~wsYt)H z7z1rRF7r0rL-3!~XQV)p`Ix7U(}U~0i6&lbQ<|n51T6#~3cbOO;s#Gi7meftE4e4{ zA#2!l5*>^SD?K?N;;B|vm^?Tr7uLogD5%^iyF>GhrK3}IVU*$c6-V#wp zZ(BnHSwed-^8iMPA+?as7`$uHtP#P(a*P+zCJaR@F=DyPY@wzMO!?5S%v8Gi>GEko(feOpgi7%DEA$a`J7Oew!WLWG1&6z&oX++i=m6%+jD$N!~W zn}(5Y8~&Du3=NoGm08%q#$Z+UV5qUv9+L2I2qrxoA}Np567oF)`$v6^>(N=5%3^F5 zLxmZiB~1^H%#xTl6rLqDZ%dX!$vZmph3C-emt%7z!8W6F(Cj6oyPhzQ;4-^0kw5J8 z=k6?e+wx!*0oZ1LmK69J(hbju^=3(ZERC|9xg?NNjIJl<&`XzO=kg)9G+VCBkvQKh z&!JeCkgm}=`nOPXdJ0SaGlOmUvi+b*b6<}aC0##08^7|a%3Xc}tLp$IL+y zofD5XqqMtr=fMprtsZQ(j+LkfzsH5+p8sML{xR+=Ir?eVt%P!hff# zvt+<|Ly)k?%)rV$fsPN_v6mQ^>%LwwO{vNemcUb2pc~FX;`jBs zAA=|w$M8e16{7~ZIOj;a)$2S5&I_@iIh5@=_E6*J9Drv)P-@(bLPrP)Wkp3ykim>E zEHx-QjlBOdLNn+UQ;>Ny|MGAi(XJTGlREb`=1JNk5KvY<{^mT1ctvlXB!mnuv$GZ? z`?Dg9^qjn_IVW}D1 zD;`;n=8Wjhlb%;TFE1=vzFAqa*jy0l!rX&CYm>Ot74;|4XDIB3#OFBDiMbQwXYLtK z8~q4U5@YSh7=}c2JACW@9x+RTBHn^!V?c!EKKLGE#~pvez%!(_YX(QUem}0T(Wen# zQ-8z6xRJ%Gcg^HT*G=d7_sf@-a{S|qC68D!(j&yl^KjPM1$|+f} zj~W$OOlv4C7!Q5$cQ5rmeQVuJ3;0EU4oy#&vw~C&D+wc?;Ydeja8G}Rrvxo}3+<;RGQKsrj%gT8%{#SrDT|{A|#z;Uej6k?EL~GVpcVceArd`a5}RkyLic8zTNR zEi97wp0?3lB-MvAw23C@(_(5nkpgMGxZL=c-6#6 zn4tb1W+50jyBH`96T@YenGFx=G7i#XP)8FT*+XG=05uPXeBE;u4sC|#XJ~dt&s+BK z9xC56L>9xYaX*H9kG?k6eQK=x&RF*qrTY+6U7kU%39r=sf3wzQouK!BP1WpuC4Yz{ zd8l&;dJpvvk?u@jaEPSmIMM~v29GbCtsWx9EoSo&8E#1!K$RVL+J~TV-hj|f4$|M1 zqdDZ^PqE%1(zDaFg|8mInT2ow;qf6-J7{JO(ZnI%#RCsP@u@?ihC2?M(I5>6rN)6?P8Eg47NCd%{a&-l_(2xiIw);A*G^*Pb-4!->O{!F{JZGSnG*>gXNSv2(9^eR!bP zhmH^xCT>ay4iBFcNtv6R7w%FW*n&^VP#M$bCJz z@Z&g4Tj1+qFKSnSOnHVwrZV$j47h)9)r(QvY|OyVFE^nqE>%$C>rpQPIC*b%hC`<2 zx_9>t0i#2NiCGV(-cUjp^Atx92U2tqg9z5ZupU-0%m_!2Xw?{|1SZzTstm46>g$mR zt$BNMWFUOCIg*?8WLL}hF%?@dJw`VWYaLwA2VG&eOD4$`rg-XP#EZ>896e&!a<0o)V%qPm4mP zdqPy_X>@kXXRQkOI9;2y%8;ME3YpHFFEdY5^Ril*c^RQ~OYR+MF@gWVPZp_g(^&^dGUww{74aqTGt3lFOk=zi~IT~Xa zPv&TWTa`Il;Zmr{t@0f0j?*JH%G%W)sD$Pvk9iTG z)2vtWmRCgZm~lELtT4EcydVpTs!Ww)^ib8d={JU1M8b~74 zhkz$Z1)M=zuD0PjIX@5Z6H|I}*r_RYk3NcCA$tagJ4!Gy!g3Hpfy(1#m95F_99_xo zSD|OS42xN4hL#~$HrfWtl4An2;0U8!k0PXjk2E-9Urc$>yQ(tJ-ngR;9^`qPNYiZ6 zt8k7vV;#oEIQn0dNn>Mj!=NR5WgDR&7B?kz%CbOi5OTwZfbP!Hgjp)Sw4jkRb=qiw zY{+#i3<$Sfa<~mci3Oddl$^b4cBCHVdU2fks^bABPqF%iM@l`f%O*%rA1oQB4hNi{ z()0Bxou(e8K4Qy7#Z|H-&eAopLD$VGCnd+Thn%%3lx9OjDMiy}C{58R!F%Jppt?n| zj=hSjbVF>?n^PiaYnhj%K50x5g%d`_9voSVfa7X%?(8 zX~6hs+CaVG5-oIMSZSZrK9dR?aS`>YR5vJ7h@iZy02q%e8d4(^=% zInV2XU^N(aAH}{3r}6QsBZeP2ZDXRzG5n+;)4_y;)368qghu~~=RH9mCLt`w$VHlU z(WP_)UIPQZh!|Kd{rd79r;AG*4t)hHL}-x)rMKkAJG?H}`b3S=7==+yl~?K&M!7B* zl{V!q7}xuhcBiwKb>9)hqB1?fBUrQ+VMK`y4BcT_vP|J5ur-5jG@?F?^zdT{gp$U_*XJ9;)b#8)UzB6)KoJ{}TXNau zb_9jc%W;CAB`Z=MWjc#RxYhB!N9ew>ZyBO+4SJn#J{3#;xsU3E2xnSPqMTrrH`&h|b@_y{T`d??FEp_^GdaR2Hsul$KbaaTEcXSwD*Dsf~2T6F|Dc^rK{O&AT zWfcRd;lnBjB8k*2M)|uKD3~uh3pXc{v$Vz_k>Eb8XXLp^%+eyiiK&pshlQ;Yc6?@O z#RH2<&G@jcRh$tKWI7XJQ;j71v){38j3C!d7~dTZa_2XYu+2^ zhZem0UAY&Nq#Ro>DT;DLFc=~bHfr3Rk?VPIeAltxgy$9TJS^BwKkt6t5t%nMhP=@q zQc^7)?C;K?XX|-muk#eg#7KH!cS&V?zE9sEu%WZER9AUyMmoY7x>=+6JjD3pAz>bb zf_X4CgAtNPW-tjf!-(jy@Z^ySMPyR(pqN!Ghsassov9MbXJje;H&_iOWL4AzS?8E~ z-w;tjZ(qg7Iu%tx+Sr`Jv>L8M(4=vqF0gke*JAoud%@^3NW_W$rS4?|F#Y?hY6OSs$X?)RZ;?Bx=rDv)= zp{rG%r*zW?XX5%s)&t|Y#ceko_bGM3Qgy+k8Zsw z%4ok!h<7PR%3TPwjY(DzGxFQy6eJwoihx@PG9qN%AZugY@1(O=c?o-0=(`Vcg3JQJ z5Pkw%pCoi^><-55?zk%%QDA>EU> zi5*)fk7evBopQztv5NfGB$xJnm2XbzMz-%wxvsWZ(z1oTZ2Q{@xy2`@ABpcUZ?M0;&z23)8;Wt?bT!SpZ(JYr_#HLN42(JQdd6n+iMATr$$ zi@i%^DA1F#vnQHYNNZLuQw-+K%w>`=Meed(Pm7%=u?4trGT0I5q}NG1!A;+ zg@pYga+yT^Lwp_H6D(aI**zurhR(wrqN9g7NcfR)uYJ@kTqgBLMd>mr?-z;lq`E%} z$@(FYxJ;Ud(#TSf0<;Cnb9jGe{2N#+;LIC zy~oYkWzsutb}p0Q@iOF#C(Onb(m7$)p^OtU5KKQ~rY_LRGopWq)Sd~Ue>`grE|JEw zR&8L7jpRaRc#RZ8m_m^DS(#(!Y+ts(IdgP@ z^w0SR7f5u~#Gs21{sPIb`db%Bb(N>jliDgzohS8G5kF6wt0H=yv{yxcm2_8SHoetV z^n&xebdeO#!}_8Zcun1%zSrK+x-Y2a1moe{{6}>M}R-6q~aYJoB5Qk*kv}l=v<(EB!oxbidPjr{4 z;osdN`n0F&w{y#$$U}Bw*)x1d)Q)>#pHRmhfBv|qxyK(n?#b@88W6Gltz({)ZIzFC z!u$O3Wl#5EUOnzfJR<7Lp420^G5ZdP+Oj7ZkY>_)ROC;168p{42~XyLojvAhAFyM` zJcY%cQCjxYgTdZ0&oGGjFNqxGNSBYAqvNynqoQ}*(>ls?Cp<9c=5bGdoE{#PMT;Dh zlF?&1giwViMfE99=}CX-DNo}`8}aj#X67l+=t+O_DNo|503@E`!!OW1SkP=bp7Q_|SXSj^Yws*?_sO_##M;R`{@sb~qX6q5!ylhw2+3>PmUT5_yeVTli#je_&bymA-SJzqQEe?`>tA#2?u37ze zS@K%2{4T3qdqBX-GD|#g5?8`?f$GlKF49Yq)g3-Iw|zmO#Ek#Bsx5Q*^#US>>Oby`Q2i z`3bv}q^$7?JA+I0yUa3R=ez9)E}1)a@86(reUFHz@ZI;ox8%Opm%%sT?=z#g#J+D6 zPX7Bu9a7!9Rsom9M^+Y>`bTC2m*n@G?f(hieZM*UpYiDj%*g+O{C>bp;WGFEGmA?h zjJ}R*J&eUciGPstDjt{qVJtD=-u4euk;HZM!#J#fOrls~%i)^%5uEXWT>MAD@qG)| z6t0yRwUeky_{XRn$2Iq3G}J{=QvZtbJmj0W=5Ve24_Jo$B<_z>I{~>Gt}$FQKTa(v zU-)r~HE7cR3F_-XERv8jfF90N#AIO-vKT88cm`5Q)?Uua$91T>_!o#1Vc11S$&*NYUn!5V)RD{vi>0iLa>uXT% zFHo!X$58ISqjv6(aNqw5<=`5}ifk6VfkQY2ToeBuGu9@ZPyc&r_Hm8tA89bzq%4nJ zLixG+do&ov=XxC{WpR!D0p;>@`VUble4qP6>?9&g_%!7sJSB?j5ZCypsVSeD#%KA^ z%%>?<$w=`ts50)Ve1k+rnQ;V@&-O4Ya>NN&Bzxd|XF=C70t0U!bCk zPo*zV-odqpYa?tddEIc9q4oeNWz#3}YQ0pVBxnSHZQ0t9%Zkn2(H)_(z_P z_;bAO0;+tC<$2?#K3vH-0#gZWA&3;5*IMazkUUEuSG|U{fX$?DxJzPVE z#bGRb$Q^_`<9L4?HofFoxP%AuwkVtE+N$bymWSV2kQJZ^qIIM_zYTR6yQ z3r9M2Bja?DK9zwa58BmSLj0RnE3MTS;zC)VGrQyr|$`)5Hng?iRDP z6_L1{B#q^FZD5TQ%jzZq{VWb?L&G$nzFS0nnp@<0b$tttporcUd90xgGAC+V$Y3rF zi$j@l<^NOvkN;h(6M2n=_@$hV_@R}-DCXSF|-+c8yv$d zb{I^Y?<(;K>*JW0*Tk8w%B;Wvx7w`yx;_h!BlA0iRUgcrutIKN=K=MI8XTI48H=(? zk`|3iD*wG`RdjhcRJofA*`z#Z3iXcboO+8szacV$RWR#J3x;Y`1Ph}AdBGxH)D#=J zW_p%{XZ<+LmXz(Do8`q>Sb-^@fbJ(R>TlGLxuT4z)AYb8zRp6Y@E(Oc%ZSZv%<|G$ zX2rZ6%L-U7{U`1(P42%(U&merIwkctp#*Fk;S3$tk7uDp-woHa<0y_CS{{kE9)sJXg~7j2*$) zzRJj>Of48ed%WTkGoXTT{yPX#YZeS|JdDN$jVHW;ajt%2`g0OFl zeAI;hiy0O?U&=^BgC%bDaL|P>6(0D)ou}j~>MhZfOQ~|WgL?2WZBdH<|G>D%=|26b)Z0d9bMN_}d_g4c~*k1RVaO7937>9Gvu+ zl|l*mQU{znvb&I*bmkVKnu4*^hDMB4`CvTry-OnDC09{MR7=1&z`ww0V8PWAZm(tHE=ozYcdew7AA^WzJ$4W1Ix?fGNwL3udid*2>nqMIm3zf z7H2tmz>(FY!x~|b;uJ@*Gn|!JAkJBf$+oI_0%3H58N200Q3F`G?a$a1xqN#I4$o`F z1;vpR*$Bo*c+jmsaQdlyru;*>*P&C?3b88s3ql?&_Zop7)~pR1r$;a_jbM|^>d#;+ zQ^ZhnW`(`j{tYx{Fgn^;-A>6(^|r^t$((^FmxFd_ z0pAS^hiR}|h?uiPqj+q^2(&mhW9^DOI~st^ba8kYgu`c)c(BK5hfg$ez5aFj_-4#; zYA7z=VOk)4W>y#E@_lwe?#EOX;8J*Ifs|*>@&YO1kR2o!IO-8u;HYGDL7wo4EpXH( zKIVxrPmXzN%+m`SP95a9Q$yu(Fy7Pfl7>7*gg^Nn+N+CZTd-wWAJ~0EumX$no?s>K z2saex0?7q3oU_9VBsCuxn4~ptw@fme595ZR8O$!w^unZ$cmA_hN4Y+^&TqRNo3jsl z8;5=^yzv#0JsCTN1%nRc{s=NvfAYPA17schigMR)1C4=q4XOqh8Y8yot=_&L3H2pv&NgKif#aI`a!M|#fAZQ!s}Ad9;e?DPh!EF|DZn%m9#2P}!B6NC5J zaF^(P;7RQE4L)F{heY#3R(S~eDD07U4ffcn4Hn;9#$EM*S^9u=_a|ukeKtBUM9~Ka ztpOf?=oSp{keU3z6FDrpA9zxSeSJtCwt643M9>W1V425cy8f6s`jE9AYe2{OC;ZtD zSpNyT^*+lUwaf3b{!!mxgLRJiGas7TPZ(Bi69 zxIr@Kul8}@1z+g~$zSZyA}*Kh63~^h{E%F;iyxBuTGGIW$eVWILy~{fM7s0lIcY`!ii>hRO1B5`x(Ho{%q^=5N2ZC$aoZekI-7SV=MXK#i*O;-Zewj@ z+`r}dh%#82+6a4Ea+JV*7KQ^kB-j-h@`n_Qfni|~ej}@xtz4dsc~NmZYL^Wg5nD@6 zWRTBZ48JfY!!FDD;D+=)HfG*Qc2??Eevg9SKf55`gTVJK!^Q-3XkcWL-%rlF+jaSt zpDa_(urbtDRsP#lT;eZyu>me!s&n5NWHin!Vj(j*Crr(h=wK^e2&u>q7-hGT;=J_N z4b$##C4G6yg%r2i&8?)d6}I8zvmLJ-L0=emFyqaPx8P8&u}whRYZeYmZ9rqrbI13j z&d?{vV}_NRMS(N;Dr;Ccw^1~bFt7TIm7T?6mF#0RVP$4%OH5nhxZe9n2Lc_u)F;oR z1<;pzhTUg4l$La!buR^T{8=p5IMqyGVr=A)f5vMM_h9N&fmmeL9_%6M*_?dVoE_gw zx^rz*r@F;V?qy+1jP|goh0awTC#y0U-e&gqcv3q>Zm*|;&6&NdwTnZpy$gLVx7+OP z@f3GQkV$Eeo!dv6dkV-rW(Sh{NZq!>`^d=VseL59k7xIJGW(XKPTXBS@gG%xzekU$ zA<7q?k)u9V^kaq{l_#ZTc;9ALTb_Bg5wGUuOBhn`HBNt>tLcH6d55NrlB_>B)9=u( zoM_cNqWunzh)Nt6GM&Urwb&~4Az7F+2k(&bJV&}Y?@PQx!lp0s4)#i$cwleA?7V~b zw2@0|i&6@0;0c83I{~? z8i_87!8%DETq(RmQb$DRZPI^SRIicn6J~RrfHBvok zm9LTfDUrEGil?rltf%F(J1>g%+awbbt+z@246nRRsxR>((l1#xeEX7Bew)N!6`8k5 z@>SU-3g<-ZZBjbdMLKidOhfX#NW4v&=Mx~k3uYaX7e(%ElD;Snt#ipvT_fqscH|l< zT`t26N3Yvm$h?t4I~1-;4VJD3pxCRv+&iQ%lkvBF*>?zbg797TE!>iOOZ1_Xw?rG6 zV1o#CdW(nGN#!k3zeZ|fQh!Snu94=Lx8CB-w@Ldg-ar-J64kd!Z%q1ciPGC-IOe0b zMD}eGd0WKa#(0NQrLLYT?dduC7`X-aX%@IVOohqJ?%xw~rS-%5wG1H}21PuO1JNF*(c1vbeP7G2b#ireea-aPaOvyL*4h z#bJnRcVTpZTIGpISh|Tbk^_#;HC-n8zE%~eO6~j(_*;K%2WHT1*k)qh%}wm@r5;ZuR}FYy_)*1ib}x zrIZK^caxT|+q+48)(ZRKxx{EU&Nfs`7~Rf5#ZPj(?6RL!cb$TB+bz=)H-fz)EZ|Yuf8=*5 zoc{S5`vhJE zYWW0S^vUcJ!$Tze5EhH^UeI1Ub(rLAJ9?N@9yW7_NFpFohe#?QCp^&uF`CCaKnF$j zFc}@>mBS=_h?fqN$RU}Ij?=M2Jad@D5AifgG~+oM?U&W`QDFJH^UyI-uxu_7JE+v9k%cXm$;G=UC*RXuZN>2g9gb?2u@_!V-tjajS}fr2c3<|SpAfC*S@sEx4xJ~= z)M?D_MffzU99ztsV%=jEVppgA5Lvy~^^>2HUT&*0a9CIW~A!v|eT56`q6O3QxVt;^TB; z1>%_%tNRiwtoRBqvD!*-bcPMbA|v@_>^ZCc5=%YjYn)+)=Ypv-to=s;mg5u z*6;-uJsGUOzzQdM^+i@X$&1kPNuGU?A;N?IJ88vFvEIpG_j#6lURFB&JPv!4%2|